diff --git a/.github/actions/prepare-build/action.yml b/.github/actions/prepare-build/action.yml new file mode 100644 index 0000000000..ce75b7a57c --- /dev/null +++ b/.github/actions/prepare-build/action.yml @@ -0,0 +1,47 @@ +name: "Prebuilt steps for build" +description: "Reusable steps for multiple jobs" +inputs: + java_ver: + required: true + description: "Java version to install" + ghc_ver: + required: true + description: "GHC version to install" + github_ref: + required: true + description: "Git reference" + os: + required: true + description: "Target OS" + cache_path: + required: false + default: "~/.cabal/store" + description: "Cache path" + cabal_ver: + required: false + default: 3.10.1.0 + description: "GHC version to install" +runs: + using: "composite" + steps: + - name: Setup Haskell + uses: simplex-chat/setup-haskell-action@v2 + with: + ghc-version: ${{ inputs.ghc_ver }} + cabal-version: ${{ inputs.cabal_ver }} + + - name: Setup Java + if: startsWith(inputs.github_ref, 'refs/tags/v') + uses: actions/setup-java@v3 + with: + distribution: 'corretto' + java-version: ${{ inputs.java_ver }} + cache: 'gradle' + + - name: Restore cached build + uses: actions/cache@v4 + with: + path: | + ${{ inputs.cache_path }} + dist-newstyle + key: ${{ inputs.os }}-ghc${{ inputs.ghc_ver }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }} diff --git a/.github/actions/prepare-release/action.yml b/.github/actions/prepare-release/action.yml new file mode 100644 index 0000000000..e0d32bd596 --- /dev/null +++ b/.github/actions/prepare-release/action.yml @@ -0,0 +1,39 @@ +name: "Upload binary and update hash" +description: "Reusable steps for multiple jobs" +inputs: + bin_path: + required: true + description: "Path to binary to upload" + bin_name: + required: true + description: "Name of uploaded binary" + bin_hash: + required: true + description: "Message with SHA to include in release" + github_ref: + required: true + description: "Github reference" + github_token: + required: true + description: "Github token" +runs: + using: "composite" + steps: + - name: Upload file with specific name + if: startsWith(inputs.github_ref, 'refs/tags/v') + uses: simplex-chat/upload-release-action@v2 + with: + repo_token: ${{ inputs.github_token }} + file: ${{ inputs.bin_path }} + asset_name: ${{ inputs.bin_name }} + tag: ${{ inputs.github_ref }} + + - name: Add hash to release notes + if: startsWith(inputs.github_ref, 'refs/tags/v') + uses: simplex-chat/action-gh-release@v2 + env: + GITHUB_TOKEN: ${{ inputs.github_token }} + with: + append_body: true + body: | + ${{ inputs.bin_hash }} diff --git a/.github/actions/swap/action.yml b/.github/actions/swap/action.yml new file mode 100644 index 0000000000..87d670b147 --- /dev/null +++ b/.github/actions/swap/action.yml @@ -0,0 +1,44 @@ +name: 'Set Swap Space' +description: 'Add moar swap' +branding: + icon: 'crop' + color: 'orange' +inputs: + swap-size-gb: + description: 'Swap space to create, in Gigabytes.' + required: false + default: '10' +runs: + using: "composite" + steps: + - name: Swap space report before modification + shell: bash + run: | + echo "Memory and swap:" + free -h + echo + swapon --show + echo + - name: Set Swap + shell: bash + run: | + export SWAP_FILE=$(swapon --show=NAME | tail -n 1) + echo "Swap file: $SWAP_FILE" + if [ -z "$SWAP_FILE" ]; then + SWAP_FILE=/opt/swapfile + else + sudo swapoff $SWAP_FILE + sudo rm $SWAP_FILE + fi + sudo fallocate -l ${{ inputs.swap-size-gb }}G $SWAP_FILE + sudo chmod 600 $SWAP_FILE + sudo mkswap $SWAP_FILE + sudo swapon $SWAP_FILE + - name: Swap space report after modification + shell: bash + run: | + echo "Memory and swap:" + free -h + echo + swapon --show + echo diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5a07a8722..ca1bc79510 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,24 +5,75 @@ on: branches: - master - stable - - users tags: - "v*" - "!*-fdroid" - "!*-armv7a" pull_request: + paths-ignore: + - "apps/ios" + - "apps/multiplatform" + - "blog" + - "docs" + - "fastlane" + - "images" + - "packages" + - "website" + - "README.md" + - "PRIVACY.md" + +# This workflow uses custom actions (prepare-build and prepare-release) defined in: +# +# .github/actions/ +# ├── prepare-build +# │ └── action.yml +# └── prepare-release +# └── action.yml + +# Important! +# Do not use always(), it makes build unskippable. +# See: https://github.com/actions/runner/issues/1846#issuecomment-1246102753 jobs: - prepare-release: - if: startsWith(github.ref, 'refs/tags/v') + +# ============================= +# Global variables +# ============================= + +# That is the only and less hacky way to setup global variables +# to use in strategy matrix (env:/YAML anchors doesn't work). +# See: https://github.com/orgs/community/discussions/56787#discussioncomment-6041789 +# https://github.com/actions/runner/issues/1182 +# https://stackoverflow.com/a/77549656 + + variables: + runs-on: ubuntu-latest + outputs: + GHC_VER: 9.6.3 + JAVA_VER: 17 + steps: + - name: Dummy job when we have just simple variables + if: false + run: echo + +# ============================= +# Create release +# ============================= + +# Create release, but only if it's triggered by tag push. +# On pull requests/commits push, this job will always complete. + + maybe-release: runs-on: ubuntu-latest steps: - name: Clone project + if: startsWith(github.ref, 'refs/tags/v') uses: actions/checkout@v3 - name: Build changelog id: build_changelog - uses: mikepenz/release-changelog-builder-action@v4 + if: startsWith(github.ref, 'refs/tags/v') + uses: simplex-chat/release-changelog-builder-action@v5 with: configuration: .github/changelog_conf.json failOnError: true @@ -32,7 +83,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create release - uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/v') + uses: simplex-chat/action-gh-release@v2 with: body: ${{ steps.build_changelog.outputs.changelog }} prerelease: true @@ -42,155 +94,295 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - build: - name: build-${{ matrix.os }}-${{ matrix.ghc }} - if: always() - needs: prepare-release - runs-on: ${{ matrix.os }} +# ========================= +# Linux Build +# ========================= + + build-linux: + name: "ubuntu-${{ matrix.os }} (CLI,Desktop), GHC: ${{ matrix.ghc }}" + needs: [maybe-release, variables] + runs-on: ubuntu-${{ matrix.os }} strategy: fail-fast: false matrix: include: - - os: ubuntu-20.04 + - os: 22.04 ghc: "8.10.7" - cache_path: ~/.cabal/store - - os: ubuntu-20.04 - ghc: "9.6.3" - cache_path: ~/.cabal/store - asset_name: simplex-chat-ubuntu-20_04-x86-64 - desktop_asset_name: simplex-desktop-ubuntu-20_04-x86_64.deb - - os: ubuntu-22.04 - ghc: "9.6.3" - cache_path: ~/.cabal/store - asset_name: simplex-chat-ubuntu-22_04-x86-64 + should_run: ${{ !(github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} + - os: 22.04 + ghc: ${{ needs.variables.outputs.GHC_VER }} + cli_asset_name: simplex-chat-ubuntu-22_04-x86-64 desktop_asset_name: simplex-desktop-ubuntu-22_04-x86_64.deb - - os: macos-latest - ghc: "9.6.3" - cache_path: ~/.cabal/store - asset_name: simplex-chat-macos-x86-64 - desktop_asset_name: simplex-desktop-macos-x86_64.dmg - - os: windows-latest - ghc: "9.6.3" - cache_path: C:/cabal - asset_name: simplex-chat-windows-x86-64 - desktop_asset_name: simplex-desktop-windows-x86_64.msi + should_run: true + - os: 24.04 + ghc: ${{ needs.variables.outputs.GHC_VER }} + cli_asset_name: simplex-chat-ubuntu-24_04-x86-64 + desktop_asset_name: simplex-desktop-ubuntu-24_04-x86_64.deb + should_run: true steps: - - name: Configure pagefile (Windows) - if: matrix.os == 'windows-latest' - uses: al-cheb/configure-pagefile-action@v1.3 - with: - minimum-size: 16GB - maximum-size: 16GB - disk-root: "C:" - - - name: Clone project + - name: Checkout Code + if: matrix.should_run == true uses: actions/checkout@v3 - - name: Setup Haskell - uses: haskell-actions/setup@v2 + - name: Setup swap + if: matrix.ghc == '8.10.7' && matrix.should_run == true + uses: ./.github/actions/swap with: - ghc-version: ${{ matrix.ghc }} - cabal-version: "3.10.1.0" + swap-size-gb: 30 + + # Otherwise we run out of disk space with Docker build + - name: Free disk space + if: matrix.should_run == true + shell: bash + run: ./scripts/ci/linux_util_free_space.sh - name: Restore cached build - id: restore_cache - uses: actions/cache/restore@v3 + if: matrix.should_run == true + uses: actions/cache@v4 with: path: | - ${{ matrix.cache_path }} + ~/.cabal/store dist-newstyle - key: ${{ matrix.os }}-ghc${{ matrix.ghc }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }} + key: ubuntu-${{ matrix.os }}-ghc${{ matrix.ghc }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }} - # / Unix + - name: Set up Docker Buildx + if: matrix.should_run == true + uses: simplex-chat/docker-setup-buildx-action@v3 - - name: Unix prepare cabal.project.local for Mac - if: matrix.os == 'macos-latest' + - name: Build and cache Docker image + if: matrix.should_run == true + uses: simplex-chat/docker-build-push-action@v6 + with: + context: . + load: true + file: Dockerfile.build + tags: build/${{ matrix.os }}:latest + build-args: | + TAG=${{ matrix.os }} + GHC=${{ matrix.ghc }} + + # Docker needs these flags for AppImage build: + # --device /dev/fuse + # --cap-add SYS_ADMIN + # --security-opt apparmor:unconfined + - name: Start container + if: matrix.should_run == true shell: bash run: | - echo "ignore-project: False" >> cabal.project.local - echo "package direct-sqlcipher" >> cabal.project.local - echo " extra-include-dirs: /usr/local/opt/openssl@1.1/include" >> cabal.project.local - echo " extra-lib-dirs: /usr/local/opt/openssl@1.1/lib" >> cabal.project.local - echo " flags: +openssl" >> cabal.project.local + docker run -t -d \ + --device /dev/fuse \ + --cap-add SYS_ADMIN \ + --security-opt apparmor:unconfined \ + --name builder \ + -v ~/.cabal:/root/.cabal \ + -v /home/runner/work/_temp:/home/runner/work/_temp \ + -v ${{ github.workspace }}:/project \ + build/${{ matrix.os }}:latest - - name: Install AppImage dependencies - if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04' - run: sudo apt install -y desktop-file-utils - - - name: Install pkg-config for Mac - if: matrix.os == 'macos-latest' - run: brew install pkg-config - - - name: Unix prepare cabal.project.local for Ubuntu - if: matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04' + - name: Prepare cabal.project.local + if: matrix.should_run == true shell: bash run: | echo "ignore-project: False" >> cabal.project.local echo "package direct-sqlcipher" >> cabal.project.local echo " flags: +openssl" >> cabal.project.local - - name: Unix build CLI - id: unix_cli_build - if: matrix.os != 'windows-latest' + # chmod/git commands are used to workaround permission issues when cache is restored + - name: Build CLI + if: matrix.should_run == true + shell: docker exec -t builder sh -eu {0} + run: | + chmod -R 777 dist-newstyle ~/.cabal && git config --global --add safe.directory '*' + cabal clean + cabal update + cabal build -j --enable-tests + mkdir -p /out + for i in simplex-chat simplex-chat-test; do + bin=$(find /project/dist-newstyle -name "$i" -type f -executable) + chmod +x "$bin" + mv "$bin" /out/ + done + strip /out/simplex-chat + + - name: Copy tests from container + if: matrix.should_run == true shell: bash run: | - cabal build --enable-tests - path=$(cabal list-bin simplex-chat) - echo "bin_path=$path" >> $GITHUB_OUTPUT - echo "bin_hash=$(echo SHA2-512\(${{ matrix.asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT + docker cp builder:/out/simplex-chat-test . - - name: Unix upload CLI binary to release - if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os != 'windows-latest' - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ steps.unix_cli_build.outputs.bin_path }} - asset_name: ${{ matrix.asset_name }} - tag: ${{ github.ref }} - - - name: Unix update CLI binary hash - if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os != 'windows-latest' - uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - append_body: true - body: | - ${{ steps.unix_cli_build.outputs.bin_hash }} - - - name: Setup Java - if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name - uses: actions/setup-java@v3 - with: - distribution: 'corretto' - java-version: '17' - cache: 'gradle' - - - name: Linux build desktop - id: linux_desktop_build - if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04') + - name: Copy CLI from container and prepare it + id: linux_cli_prepare + if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true shell: bash + run: | + docker cp builder:/out/simplex-chat ./${{ matrix.cli_asset_name }} + path="${{ github.workspace }}/${{ matrix.cli_asset_name }}" + echo "bin_path=$path" >> $GITHUB_OUTPUT + echo "bin_hash=$(echo SHA2-256\(${{ matrix.cli_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT + + - name: Upload CLI + if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true + uses: ./.github/actions/prepare-release + with: + bin_path: ${{ steps.linux_cli_prepare.outputs.bin_path }} + bin_name: ${{ matrix.cli_asset_name }} + bin_hash: ${{ steps.linux_cli_prepare.outputs.bin_hash }} + github_ref: ${{ github.ref }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Desktop + if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true + shell: docker exec -t builder sh -eu {0} run: | scripts/desktop/build-lib-linux.sh cd apps/multiplatform ./gradlew packageDeb - path=$(echo $PWD/release/main/deb/simplex_*_amd64.deb) - echo "package_path=$path" >> $GITHUB_OUTPUT - echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT - - name: Linux make AppImage - id: linux_appimage_build - if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04' + - name: Prepare Desktop + id: linux_desktop_build + if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true shell: bash run: | - scripts/desktop/make-appimage-linux.sh - path=$(echo $PWD/apps/multiplatform/release/main/*imple*.AppImage) - echo "appimage_path=$path" >> $GITHUB_OUTPUT - echo "appimage_hash=$(echo SHA2-512\(simplex-desktop-x86_64.AppImage\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT + path=$(echo ${{ github.workspace }}/apps/multiplatform/release/main/deb/simplex_*_amd64.deb ) + echo "package_path=$path" >> $GITHUB_OUTPUT + echo "package_hash=$(echo SHA2-256\(${{ matrix.desktop_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT - - name: Mac build desktop + - name: Upload Desktop + uses: ./.github/actions/prepare-release + if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true + with: + bin_path: ${{ steps.linux_desktop_build.outputs.package_path }} + bin_name: ${{ matrix.desktop_asset_name }} + bin_hash: ${{ steps.linux_desktop_build.outputs.package_hash }} + github_ref: ${{ github.ref }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Build AppImage + if: startsWith(github.ref, 'refs/tags/v') && matrix.os == '22.04' && matrix.should_run == true + shell: docker exec -t builder sh -eu {0} + run: | + scripts/desktop/make-appimage-linux.sh + + - name: Prepare AppImage + id: linux_appimage_build + if: startsWith(github.ref, 'refs/tags/v') && matrix.os == '22.04' && matrix.should_run == true + shell: bash + run: | + path=$(echo ${{ github.workspace }}/apps/multiplatform/release/main/*imple*.AppImage) + echo "appimage_path=$path" >> $GITHUB_OUTPUT + echo "appimage_hash=$(echo SHA2-256\(simplex-desktop-x86_64.AppImage\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT + + - name: Upload AppImage + if: startsWith(github.ref, 'refs/tags/v') && matrix.os == '22.04' && matrix.should_run == true + uses: ./.github/actions/prepare-release + with: + bin_path: ${{ steps.linux_appimage_build.outputs.appimage_path }} + bin_name: "simplex-desktop-x86_64.AppImage" + bin_hash: ${{ steps.linux_appimage_build.outputs.appimage_hash }} + github_ref: ${{ github.ref }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Fix permissions for cache + if: matrix.should_run == true + shell: bash + run: | + sudo chmod -R 777 dist-newstyle ~/.cabal + sudo chown -R $(id -u):$(id -g) dist-newstyle ~/.cabal + + - name: Run tests + if: matrix.should_run == true + timeout-minutes: 120 + shell: bash + run: | + i=1 + attempts=1 + ${{ (github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} && attempts=3 + while [ "$i" -le "$attempts" ]; do + if ./simplex-chat-test; then + break + else + echo "Attempt $i failed, retrying..." + i=$((i + 1)) + sleep 1 + fi + done + if [ "$i" -gt "$attempts" ]; then + echo "All "$attempts" attempts failed." + exit 1 + fi + +# ========================= +# MacOS Build +# ========================= + + build-macos: + name: "${{ matrix.os }} (CLI,Desktop), GHC: ${{ matrix.ghc }}" + needs: [maybe-release, variables] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + ghc: ${{ needs.variables.outputs.GHC_VER }} + cli_asset_name: simplex-chat-macos-aarch64 + desktop_asset_name: simplex-desktop-macos-aarch64.dmg + openssl_dir: "/opt/homebrew/opt" + - os: macos-13 + ghc: ${{ needs.variables.outputs.GHC_VER }} + cli_asset_name: simplex-chat-macos-x86-64 + desktop_asset_name: simplex-desktop-macos-x86_64.dmg + openssl_dir: "/usr/local/opt" + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Prepare build + uses: ./.github/actions/prepare-build + with: + java_ver: ${{ needs.variables.outputs.JAVA_VER }} + ghc_ver: ${{ matrix.ghc }} + os: ${{ matrix.os }} + github_ref: ${{ github.ref }} + + - name: Install OpenSSL + run: brew install openssl@3.0 + + - name: Prepare cabal.project.local + shell: bash + run: | + echo "ignore-project: False" >> cabal.project.local + echo "package simplexmq" >> cabal.project.local + echo " extra-include-dirs: ${{ matrix.opnessl_dir }}/openssl@3.0/include" >> cabal.project.local + echo " extra-lib-dirs: ${{ matrix.openssl_dir}}/openssl@3.0/lib" >> cabal.project.local + echo "" >> cabal.project.local + echo "package direct-sqlcipher" >> cabal.project.local + echo " extra-include-dirs: ${{ matrix.openssl_dir }}/openssl@3.0/include" >> cabal.project.local + echo " extra-lib-dirs: ${{ matrix.openssl_dir }}/openssl@3.0/lib" >> cabal.project.local + echo " flags: +openssl" >> cabal.project.local + + - name: Build CLI + id: mac_cli_build + shell: bash + run: | + cabal build -j --enable-tests + path=$(cabal list-bin simplex-chat) + echo "bin_path=$path" >> $GITHUB_OUTPUT + echo "bin_hash=$(echo SHA2-256\(${{ matrix.cli_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT + + - name: Upload CLI + if: startsWith(github.ref, 'refs/tags/v') + uses: ./.github/actions/prepare-release + with: + bin_path: ${{ steps.mac_cli_build.outputs.bin_path }} + bin_name: ${{ matrix.cli_asset_name }} + bin_hash: ${{ steps.mac_cli_build.outputs.bin_hash }} + github_ref: ${{ github.ref }} + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Desktop id: mac_desktop_build - if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'macos-latest' + if: startsWith(github.ref, 'refs/tags/v') shell: bash env: APPLE_SIMPLEX_SIGNING_KEYCHAIN: ${{ secrets.APPLE_SIMPLEX_SIGNING_KEYCHAIN }} @@ -200,88 +392,77 @@ jobs: scripts/ci/build-desktop-mac.sh path=$(echo $PWD/apps/multiplatform/release/main/dmg/SimpleX-*.dmg) echo "package_path=$path" >> $GITHUB_OUTPUT - echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT + echo "package_hash=$(echo SHA2-256\(${{ matrix.desktop_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT - - name: Linux upload desktop package to release - if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04') - uses: svenstaro/upload-release-action@v2 + - name: Upload Desktop + if: startsWith(github.ref, 'refs/tags/v') + uses: ./.github/actions/prepare-release with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ steps.linux_desktop_build.outputs.package_path }} - asset_name: ${{ matrix.desktop_asset_name }} - tag: ${{ github.ref }} + bin_path: ${{ steps.mac_desktop_build.outputs.package_path }} + bin_name: ${{ matrix.desktop_asset_name }} + bin_hash: ${{ steps.mac_desktop_build.outputs.package_hash }} + github_ref: ${{ github.ref }} + github_token: ${{ secrets.GITHUB_TOKEN }} - - name: Linux update desktop package hash - if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04') - uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - append_body: true - body: | - ${{ steps.linux_desktop_build.outputs.package_hash }} - - - name: Linux upload AppImage to release - if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04' - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ steps.linux_appimage_build.outputs.appimage_path }} - asset_name: simplex-desktop-x86_64.AppImage - tag: ${{ github.ref }} - - - name: Linux update AppImage hash - if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04' - uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - append_body: true - body: | - ${{ steps.linux_appimage_build.outputs.appimage_hash }} - - - name: Mac upload desktop package to release - if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'macos-latest' - uses: svenstaro/upload-release-action@v2 - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ steps.mac_desktop_build.outputs.package_path }} - asset_name: ${{ matrix.desktop_asset_name }} - tag: ${{ github.ref }} - - - name: Mac update desktop package hash - if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'macos-latest' - uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - append_body: true - body: | - ${{ steps.mac_desktop_build.outputs.package_hash }} - - - name: Cache unix build - uses: actions/cache/save@v3 - if: matrix.os != 'windows-latest' - with: - path: | - ${{ matrix.cache_path }} - dist-newstyle - key: ${{ steps.restore_cache.outputs.cache-primary-key }} - - - name: Unix test - if: matrix.os != 'windows-latest' - timeout-minutes: 40 + - name: Run tests + timeout-minutes: 120 shell: bash - run: cabal test --test-show-details=direct + run: | + i=1 + attempts=1 + ${{ (github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} && attempts=3 + while [ "$i" -le "$attempts" ]; do + if cabal test --test-show-details=direct; then + break + else + echo "Attempt $i failed, retrying..." + i=$((i + 1)) + sleep 1 + fi + done + if [ "$i" -gt "$attempts" ]; then + echo "All "$attempts" attempts failed." + exit 1 + fi - # Unix / +# ========================= +# Windows Build +# ========================= - # / Windows - # rm -rf dist-newstyle/src/direct-sq* is here because of the bug in cabal's dependency which prevents second build from finishing + build-windows: + name: "${{ matrix.os }} (CLI,Desktop), GHC: ${{ matrix.ghc }}" + needs: [maybe-release, variables] + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: windows-latest + ghc: ${{ needs.variables.outputs.GHC_VER }} + cli_asset_name: simplex-chat-windows-x86-64 + desktop_asset_name: simplex-desktop-windows-x86_64.msi + steps: + - name: Checkout Code + uses: actions/checkout@v3 + - name: Prepare build + uses: ./.github/actions/prepare-build + with: + java_ver: ${{ needs.variables.outputs.JAVA_VER }} + ghc_ver: ${{ matrix.ghc }} + os: ${{ matrix.os }} + cache_path: "C:/cabal" + github_ref: ${{ github.ref }} + + - name: Configure pagefile (Windows) + uses: simplex-chat/configure-pagefile-action@v1.4 + with: + minimum-size: 16GB + maximum-size: 16GB + disk-root: "C:" + - name: 'Setup MSYS2' - if: matrix.os == 'windows-latest' - uses: msys2/setup-msys2@v2 + uses: simplex-chat/setup-msys2@v2 with: msystem: ucrt64 update: true @@ -293,15 +474,14 @@ jobs: toolchain:p cmake:p - - - name: Windows build - id: windows_build - if: matrix.os == 'windows-latest' + # rm -rf dist-newstyle/src/direct-sq* is here because of the bug in cabal's dependency which prevents second build from finishing + - name: Build CLI + id: windows_cli_build shell: msys2 {0} run: | export PATH=$PATH:/c/ghcup/bin:$(echo /c/tools/ghc-*/bin || echo) scripts/desktop/prepare-openssl-windows.sh - openssl_windows_style_path=$(echo `pwd`/dist-newstyle/openssl-1.1.1w | sed 's#/\([a-zA-Z]\)#\1:#' | sed 's#/#\\#g') + openssl_windows_style_path=$(echo `pwd`/dist-newstyle/openssl-3.0.15 | sed 's#/\([a-zA-Z]\)#\1:#' | sed 's#/#\\#g') rm cabal.project.local 2>/dev/null || true echo "ignore-project: False" >> cabal.project.local echo "package direct-sqlcipher" >> cabal.project.local @@ -311,70 +491,42 @@ jobs: rm -rf dist-newstyle/src/direct-sq* sed -i "s/, unix /--, unix /" simplex-chat.cabal - cabal build --enable-tests + cabal build -j --enable-tests rm -rf dist-newstyle/src/direct-sq* path=$(cabal list-bin simplex-chat | tail -n 1) echo "bin_path=$path" >> $GITHUB_OUTPUT - echo "bin_hash=$(echo SHA2-512\(${{ matrix.asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT + echo "bin_hash=$(echo SHA2-256\(${{ matrix.cli_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT - - name: Windows upload CLI binary to release - if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest' - uses: svenstaro/upload-release-action@v2 + - name: Upload CLI + if: startsWith(github.ref, 'refs/tags/v') + uses: ./.github/actions/prepare-release with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ steps.windows_build.outputs.bin_path }} - asset_name: ${{ matrix.asset_name }} - tag: ${{ github.ref }} + bin_path: ${{ steps.windows_cli_build.outputs.bin_path }} + bin_name: ${{ matrix.cli_asset_name }} + bin_hash: ${{ steps.windows_cli_build.outputs.bin_hash }} + github_ref: ${{ github.ref }} + github_token: ${{ secrets.GITHUB_TOKEN }} - - name: Windows update CLI binary hash - if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest' - uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - append_body: true - body: | - ${{ steps.windows_build.outputs.bin_hash }} - - - name: Windows build desktop + - name: Build Desktop id: windows_desktop_build - if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest' + if: startsWith(github.ref, 'refs/tags/v') shell: msys2 {0} run: | export PATH=$PATH:/c/ghcup/bin:$(echo /c/tools/ghc-*/bin || echo) scripts/desktop/build-lib-windows.sh cd apps/multiplatform ./gradlew packageMsi + rm -rf dist-newstyle/src/direct-sq* path=$(echo $PWD/release/main/msi/*imple*.msi | sed 's#/\([a-z]\)#\1:#' | sed 's#/#\\#g') echo "package_path=$path" >> $GITHUB_OUTPUT - echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT + echo "package_hash=$(echo SHA2-256\(${{ matrix.desktop_asset_name }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT - - name: Windows upload desktop package to release - if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest' - uses: svenstaro/upload-release-action@v2 + - name: Upload Desktop + if: startsWith(github.ref, 'refs/tags/v') + uses: ./.github/actions/prepare-release with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ steps.windows_desktop_build.outputs.package_path }} - asset_name: ${{ matrix.desktop_asset_name }} - tag: ${{ github.ref }} - - - name: Windows update desktop package hash - if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest' - uses: softprops/action-gh-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - append_body: true - body: | - ${{ steps.windows_desktop_build.outputs.package_hash }} - - - name: Cache windows build - uses: actions/cache/save@v3 - if: matrix.os == 'windows-latest' - with: - path: | - ${{ matrix.cache_path }} - dist-newstyle - key: ${{ steps.restore_cache.outputs.cache-primary-key }} - - # Windows / + bin_path: ${{ steps.windows_desktop_build.outputs.package_path }} + bin_name: ${{ matrix.desktop_asset_name }} + bin_hash: ${{ steps.windows_desktop_build.outputs.package_hash }} + github_ref: ${{ github.ref }} + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 72a7cf2b94..b396c9a289 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -5,14 +5,20 @@ on: pull_request_target: types: [opened, closed, synchronize] +permissions: + actions: write + contents: write + pull-requests: write + statuses: write + jobs: CLAssistant: runs-on: ubuntu-latest steps: - name: "CLA Assistant" - if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request' + if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' # Beta Release - uses: cla-assistant/github-action@v2.1.3-beta + uses: cla-assistant/github-action@v2.3.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # the below token should have repo scope and must be manually added by you in the repository's secret @@ -33,4 +39,4 @@ jobs: #custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA' #custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.' #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) - #use-dco-flag: true - If you are using DCO instead of CLA \ No newline at end of file + #use-dco-flag: true - If you are using DCO instead of CLA diff --git a/.github/workflows/reproduce-schedule.yml b/.github/workflows/reproduce-schedule.yml new file mode 100644 index 0000000000..7de44addc7 --- /dev/null +++ b/.github/workflows/reproduce-schedule.yml @@ -0,0 +1,45 @@ +name: Reproduce latest release + +on: + workflow_dispatch: + schedule: + - cron: '0 2 * * *' # every day at 02:00 night + +jobs: + reproduce: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Get latest release + shell: bash + run: | + curl --proto '=https' \ + --tlsv1.2 \ + -sSf -L \ + 'https://api.github.com/repos/simplex-chat/simplex-chat/releases/latest' \ + 2>/dev/null | \ + grep -i "tag_name" | \ + awk -F \" '{print "TAG="$4}' >> $GITHUB_ENV + + - name: Execute reproduce script + run: | + ${GITHUB_WORKSPACE}/scripts/reproduce-builds.sh "$TAG" + + - name: Check if build has been reproduced + env: + url: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_URL }} + user: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_USER }} + pass: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_PASS }} + run: | + if [ -f "${GITHUB_WORKSPACE}/$TAG/_sha256sums" ]; then + exit 0 + else + curl --proto '=https' --tlsv1.2 -sSf \ + -u "${user}:${pass}" \ + -H 'Content-Type: application/json' \ + -d '{"title": "👾 GitHub: Runner", "description": "⛔️ '"$TAG"' did not reproduce."}' \ + "$url" + exit 1 + fi diff --git a/.github/workflows/web.yml b/.github/workflows/web.yml index 039c136464..5fbe8293bc 100644 --- a/.github/workflows/web.yml +++ b/.github/workflows/web.yml @@ -4,13 +4,13 @@ on: push: branches: - master - - stable paths: - website/** - images/** - blog/** - docs/** - .github/workflows/web.yml + - PRIVACY.md jobs: build: @@ -33,7 +33,7 @@ jobs: ./website/web.sh - name: Deploy - uses: peaceiris/actions-gh-pages@v3 + uses: simplex-chat/actions-gh-pages@v3 with: publish_dir: ./website/_site github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index d7106ec8fa..645b55ec9d 100644 --- a/.gitignore +++ b/.gitignore @@ -54,12 +54,14 @@ website/translations.json website/src/img/images/ website/src/images/ website/src/js/lottie.min.js +website/src/privacy.md # Generated files website/package/generated* # Ignore build tool output, e.g. code coverage website/.nyc_output/ website/coverage/ +result # Ignore API documentation website/api-docs/ diff --git a/Dockerfile b/Dockerfile index 6c60195f97..cdcbc40d7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,7 +29,7 @@ RUN cp ./scripts/cabal.project.local.linux ./cabal.project.local # Compile simplex-chat RUN cabal update -RUN cabal build exe:simplex-chat +RUN cabal build exe:simplex-chat --constraint 'simplexmq +client_library' --constraint 'simplex-chat +client_library' # Strip the binary from debug symbols to reduce size RUN bin=$(find /project/dist-newstyle -name "simplex-chat" -type f -executable) && \ diff --git a/Dockerfile.build b/Dockerfile.build new file mode 100644 index 0000000000..76bb1127f2 --- /dev/null +++ b/Dockerfile.build @@ -0,0 +1,92 @@ +# syntax=docker/dockerfile:1.7.0-labs +ARG TAG=24.04 +FROM ubuntu:${TAG} AS build + +### Build stage + +ARG GHC=9.6.3 +ARG CABAL=3.10.1.0 +ARG JAVA=17 + +ENV TZ=Etc/UTC \ + DEBIAN_FRONTEND=noninteractive + +# Install curl, git and and simplex-chat dependencies +RUN apt-get update && \ + apt-get install -y curl \ + libpq-dev \ + git \ + sqlite3 \ + libsqlite3-dev \ + build-essential \ + libgmp3-dev \ + zlib1g-dev \ + llvm \ + cmake \ + llvm-dev \ + libnuma-dev \ + libssl-dev \ + desktop-file-utils \ + patchelf \ + ca-certificates \ + zip \ + wget \ + fuse3 \ + file \ + appstream \ + gpg \ + unzip &&\ + ln -s /bin/fusermount /bin/fusermount3 || : + +# Install Java Coretto +# Required, because official Java in Ubuntu +# depends on libjpeg.so.8 and liblcms2.so.2 which are NOT copied into final +# /usr/lib/runtime/lib directory and I do not have time to figure out gradle.kotlin +# to fix this :( +RUN curl --proto '=https' --tlsv1.2 -sSf 'https://apt.corretto.aws/corretto.key' | gpg --dearmor -o /usr/share/keyrings/corretto-keyring.gpg &&\ + echo "deb [signed-by=/usr/share/keyrings/corretto-keyring.gpg] https://apt.corretto.aws stable main" > /etc/apt/sources.list.d/corretto.list &&\ + apt update &&\ + apt install -y java-${JAVA}-amazon-corretto-jdk + +# Specify bootstrap Haskell versions +ENV BOOTSTRAP_HASKELL_GHC_VERSION=${GHC} +ENV BOOTSTRAP_HASKELL_CABAL_VERSION=${CABAL} + +# Do not install Stack +ENV BOOTSTRAP_HASKELL_INSTALL_NO_STACK=true +ENV BOOTSTRAP_HASKELL_INSTALL_NO_STACK_HOOK=true + +# Install ghcup +RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 sh + +# Adjust PATH +ENV PATH="/root/.cabal/bin:/root/.ghcup/bin:$PATH" + +# Set both as default +RUN ghcup set ghc "${GHC}" && \ + ghcup set cabal "${CABAL}" + +#===================== +# Install Android SDK +#===================== +ARG SDK_VERSION=13114758 + +ENV SDK_VERSION=$SDK_VERSION \ + ANDROID_HOME=/root + +RUN curl -L -o tools.zip "https://dl.google.com/android/repository/commandlinetools-linux-${SDK_VERSION}_latest.zip" && \ + unzip tools.zip && rm tools.zip && \ + mv cmdline-tools tools && mkdir "$ANDROID_HOME/cmdline-tools" && mv tools "$ANDROID_HOME/cmdline-tools/" && \ + ln -s "$ANDROID_HOME/cmdline-tools/tools" "$ANDROID_HOME/cmdline-tools/latest" + +ENV PATH="$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/cmdline-tools/tools/bin" + +# https://askubuntu.com/questions/885658/android-sdk-repositories-cfg-could-not-be-loaded +RUN mkdir -p ~/.android ~/.gradle && \ + touch ~/.android/repositories.cfg && \ + echo 'org.gradle.console=plain' > ~/.gradle/gradle.properties &&\ + yes | sdkmanager --licenses >/dev/null + +ENV PATH=$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/build-tools + +WORKDIR /project diff --git a/PRIVACY.md b/PRIVACY.md index 3204fa1e53..18e5539726 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -1,60 +1,95 @@ -# SimpleX Chat Privacy Policy and Conditions of Use +--- +layout: layouts/privacy.html +permalink: /privacy/index.html +--- -SimpleX Chat is the first communication network based on a new protocol stack that builds on the same ideas of complete openness and decentralization as email and web, with the focus on providing security and privacy of communications, and without compromising on usability. +# SimpleX Chat Operators Privacy Policy and Conditions of Use -SimpleX Chat communication protocol is the first protocol that has no user profile IDs of any kind, not even random numbers, cryptographic keys or hashes that identify the users. SimpleX Chat apps allow their users to send messages and files via relay server infrastructure. Relay server owners and providers do not have any access to your messages, thanks to double-ratchet end-to-end encryption algorithm (also known as Signal algorithm - do not confuse with Signal protocols or platform) and additional encryption layers, and they also have no access to your profile and contacts - as they do not provide any user accounts. +## Summary -Double ratchet algorithm has such important properties as [forward secrecy](./docs/GLOSSARY.md#forward-secrecy), sender [repudiation](./docs/GLOSSARY.md#) and break-in recovery (also known as [post-compromise security](./docs/GLOSSARY.md#post-compromise-security)). +[Introduction](#introduction) and [General principles](#general-principles) cover SimpleX Chat network design, the network operators, and the principles of privacy and security provided by SimpleX network. -If you believe that any part of this document is not aligned with our mission or values, please raise it with us via [email](chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion). +[Privacy policy](#privacy-policy) covers: +- data stored only on your device - [your profiles](#user-profiles), delivered [messages and files](#messages-and-files). You can transfer this information to another device, and you are responsible for its preservation - if you delete the app it will be lost. +- [private message delivery](#private-message-delivery) that protects your IP address and connection graph from the destination servers. +- [undelivered messages and files](#storage-of-messages-and-files-on-the-servers) stored on the servers. +- [how users connect](#connections-with-other-users) without any user profile identifiers. +- [iOS push notifications](#ios-push-notifications) privacy limitations. +- [user support](#user-support), [SimpleX directory](#simplex-directory) and [any other data](#another-information-stored-on-the-servers) that may be stored on the servers. +- [preset server operators](#preset-server-operators) and the [information they may share](#information-preset-server-operators-may-share). +- [source code license](#source-code-license) and [updates to this document](#updates). + +[Conditions of Use](#conditions-of-use-of-software-and-infrastructure) are the conditions you need to accept to use SimpleX Chat applications and the relay servers of preset operators. Their purpose is to protect the users and preset server operators. + +*Please note*: this summary and any links in this document are provided for information only - they are not a part of the Privacy Policy and Conditions of Use. + +## Introduction + +SimpleX Chat (also referred to as SimpleX) is the first communication network based on a new protocol stack that builds on the same ideas of complete openness and decentralization as email and web, with the focus on providing security and privacy of communications, and without compromising on usability. + +SimpleX messaging protocol is the first protocol that has no user profile IDs of any kind, not even random numbers, cryptographic keys or hashes that identify the users. SimpleX apps allow their users to send messages and files via relay server infrastructure. Relay server owners and operators do not have any access to your messages, thanks to double-ratchet end-to-end encryption algorithm (also known as Signal algorithm - do not confuse with Signal protocols or platform) and additional encryption layers, and they also have no access to your profile and contacts - as they do not host user accounts. + +Double ratchet algorithm has such important properties as [forward secrecy](/docs/GLOSSARY.md#forward-secrecy), sender [repudiation](/docs/GLOSSARY.md#) and break-in recovery (also known as [post-compromise security](/docs/GLOSSARY.md#post-compromise-security)). + +If you believe that any part of this document is not aligned with SimpleX network mission or values, please raise it via [email](mailto:chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion). ## Privacy Policy -SimpleX Chat Ltd uses the best industry practices for security and encryption to provide client and server software for secure [end-to-end encrypted](./docs/GLOSSARY.md#end-to-end-encryption) messaging via private connections. This encryption cannot be compromised by the relays servers, even if they are modified or compromised, via [man-in-the-middle attack](./docs/GLOSSARY.md#man-in-the-middle-attack), unlike most other communication platforms, services and networks. +### General principles -SimpleX Chat software is built on top of SimpleX messaging and application protocols, based on a new message routing protocol allowing to establish private connections without having any kind of addresses or other identifiers assigned to its users - it does not use emails, phone numbers, usernames, identity keys or any other user profile identifiers to pass messages between the user applications. +SimpleX network software uses the best industry practices for security and encryption to provide client and server software for secure [end-to-end encrypted](/docs/GLOSSARY.md#end-to-end-encryption) messaging via private connections. This encryption is protected from being compromised by the relays servers, even if they are modified or compromised, via [man-in-the-middle attack](/docs/GLOSSARY.md#man-in-the-middle-attack). -SimpleX Chat software is similar in its design approach to email clients and browsers - it allows you to have full control of your data and freely choose the relay server providers, in the same way you choose which website or email provider to use, or use your own relay servers, simply by changing the configuration of the client software. The only current restriction to that is Apple push notifications - at the moment they can only be delivered via the preset servers that we operate, as explained below. We are exploring the solutions to deliver push notifications to iOS devices via other providers or users' own servers. +SimpleX software is built on top of SimpleX messaging and application protocols, based on a new message routing protocol allowing to establish private connections without having identifiers assigned to its users - it does not use emails, phone numbers, usernames, identity keys or any other user profile identifiers to pass messages between the user applications. -While SimpleX Chat Ltd is not a communication service provider, and provide public preset relays "as is", as experimental, without any guarantees of availability or data retention, we are committed to maintain a high level of availability, reliability and security of these preset relays. We will be adding alternative preset infrastructure providers to the software in the future, and you will continue to be able to use any other providers or your own servers. +SimpleX software is similar in its design approach to email clients and browsers - it allows you to have full control of your data and freely choose the relay server operators, in the same way you choose which website or email provider to use, or use your own relay servers, simply by changing the configuration of the client software. The only current restriction to that is Apple push notifications - at the moment they can only be delivered via the servers operated by SimpleX Chat Ltd, as explained below. We are exploring the solutions to deliver push notifications to iOS devices via other providers or users' own servers. -We see users and data sovereignty, and device and provider portability as critically important properties for any communication system. +SimpleX network operators are not communication service provider, and provide public relays "as is", as experimental, without any guarantees of availability or data retention. The operators of the relay servers preset in the app ("Preset Server Operators"), including SimpleX Chat Ltd, are committed to maintain a high level of availability, reliability and security. SimpleX client apps can have multiple preset relay server operators that you can opt-in or opt-out of using. You are and will continue to be able to use any other operators or your own servers. -SimpleX Chat security assessment was done in October 2022 by [Trail of Bits](https://www.trailofbits.com/about), and most fixes were released in v4.2 – see [the announcement](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). +SimpleX network design is based on the principles of users and data sovereignty, and device and operator portability. + +The implementation security assessment of SimpleX cryptography and networking was done in October 2022 by [Trail of Bits](https://www.trailofbits.com/about), and most fixes were released in v4.2 – see [the announcement](/blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). + +The cryptographic review of SimpleX protocols design was done in July 2024 by Trail of Bits – see [the announcement](/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md). ### Your information #### User profiles -Servers used by SimpleX Chat apps do not create, store or identify user profiles. The profiles you can create in the app are local to your device, and can be removed at any time via the app. +Servers used by SimpleX Chat apps do not create, store or identify user chat profiles. The profiles you can create in the app are local to your device, and can be removed at any time via the app. -When you create the local profile, no records are created on any of the relay servers, and infrastructure providers, whether SimpleX Chat Ltd or any other, have no access to any part of your information, and even to the fact that you created a profile - it is a local record stored only on your device. That means that if you delete the app, and have no backup, you will permanently lose all your data and the private connections you created with other software users. +When you create the local profile, no records are created on any of the relay servers, and infrastructure operators, whether preset in the app or any other, have no access to any part of your information, and even to the fact that you created a profile - it is a local record stored only on your device. That means that if you delete the app, and have no backup, you will permanently lose all your data and the private connections you created with other software users. You can transfer the profile to another device by creating a backup of the app data and restoring it on the new device, but you cannot use more than one device with the copy of the same profile at the same time - it will disrupt any active conversations on either or both devices, as a security property of end-to-end encryption. #### Messages and Files -SimpleX relay servers cannot decrypt or otherwise access the content or even the size of your messages and files you send or receive. Each message is padded to a fixed size of 16kb. Each file is sent in chunks of 64kb, 256kb, 1mb or 8mb via all or some of the configured file relay servers. Both messages and files are sent end-to-end encrypted, and the servers do not have technical means to compromise this encryption, because part of the [key exchange](./docs/GLOSSARY.md#key-exchange) happens out-of-band. +SimpleX relay servers cannot decrypt or otherwise access the content or even the size of your messages and files you send or receive. Each message is padded to a fixed size of 16kb. Each file is sent in chunks of 64kb, 256kb, 1mb or 4mb via all or some of the configured file relay servers. Both messages and files are sent end-to-end encrypted, and the servers do not have technical means to compromise this encryption, because part of the [key exchange](/docs/GLOSSARY.md#key-exchange) happens out-of-band. Your message history is stored only on your own device and the devices of your contacts. While the recipients' devices are offline, messaging relay servers temporarily store end-to-end encrypted messages – you can configure which relay servers are used to receive the messages from the new contacts, and you can manually change them for the existing contacts too. -You do not have control over which servers are used to send messages to your contacts - they are chosen by them. To send messages your client needs to connect to these servers, therefore the servers chosen by your contacts can observe your IP address. You can use VPN or some overlay network (e.g., Tor) to hide your IP address from the servers chosen by your contacts. In the near future we will add the layer in the messaging protocol that will route sent message via the relays chosen by you as well. +#### Private message delivery -The messages are permanently removed from the used relay servers as soon as they are delivered, as long as these servers used unmodified published code. Undelivered messages are deleted after the time that is configured in the messaging servers you use (21 days for preset messaging servers). +You do not have control over which servers are used to send messages to your contacts - these servers are chosen by your contacts. To send messages your client by default uses configured servers to forward messages to the destination servers, thus protecting your IP address from the servers chosen by your contacts. + +In case you use preset servers of more than one operator, the app will prefer to use a server of an operator different from the operator of the destination server to forward messages, preventing destination server to correlate messages as belonging to one client. + +You can additionally use VPN or some overlay network (e.g., Tor) to hide your IP address from the servers chosen by you. + +*Please note*: the clients allow changing configuration to connect to the destination servers directly. It is not recommended - if you make such change, your IP address will be visible to the destination servers. + +#### Storage of messages and files on the servers + +The messages are removed from the relay servers as soon as all messages of the file they were stored in are delivered and saving new messages switches to another file, as long as these servers use unmodified published code. Undelivered messages are also marked as delivered after the time that is configured in the messaging servers you use (21 days for preset messaging servers). The files are stored on file relay servers for the time configured in the relay servers you use (48 hours for preset file servers). -If a messaging servers are restarted, the encrypted message can be stored in a backup file until it is overwritten by the next restart (usually within 1 week for preset relay servers). - -As this software is fully open-source and provided under AGPLv3 license, all infrastructure providers and owners, and the developers of the client and server applications who use the SimpleX Chat source code, are required to publish any changes to this software under the same AGPLv3 license - including any modifications to the provided servers. - -In addition to the AGPLv3 license terms, SimpleX Chat Ltd is committed to the software users that the preset relays that we provide via the apps will always be compiled from the [published open-source code](https://github.com/simplex-chat/simplexmq), without any modifications. +The encrypted messages can be stored for some time after they are delivered or expired (because servers use append-only logs for message storage). This time varies, and may be longer in connections with fewer messages, but it is usually limited to 1 month, including any backup storage. #### Connections with other users -When you create a connection with another user, two messaging queues (you can think about them as mailboxes) are created on messaging relay servers (chosen by you and your contact each), that can be the preset servers or the servers that you and your contact configured in the app. SimpleX messaging protocol uses separate queues for direct and response messages, and the apps prefer to create these queues on two different relay servers for increased privacy, in case you have more than one relay server configured in the app, which is the default. +When you create a connection with another user, two messaging queues (you can think about them as mailboxes) are created on messaging relay servers (chosen by you and your contact each), that can be the preset servers or the servers that you and your contact configured in the app. SimpleX messaging protocol uses separate queues for direct and response messages, and the apps prefer to create these queues on two different relay servers, or, if available, the relays of two different operators, for increased privacy, in case you have more than one relay server configured in the app, which is the default. -SimpleX relay servers do not store information about which queues are linked to your profile on the device, and they do not collect any information that would allow infrastructure owners and providers to establish that these queues are related to your device or your profile - the access to each queue is authorized by two anonymous unique cryptographic keys, different for each queue, and separate for sender and recipient of the messages. +Preset and unmodified SimpleX relay servers do not store information about which queues are linked to your profile on the device, and they do not collect any information that would allow infrastructure owners and operators to establish that these queues are related to your device or your profile - the access to each queue is authorized by two anonymous unique cryptographic keys, different for each queue, and separate for sender and recipient of the messages. #### Connection links privacy @@ -70,99 +105,142 @@ You can always safely replace the initial part of the link `https://simplex.chat #### iOS Push Notifications +This section applies only to the notification servers operated by SimpleX Chat Ltd. + When you choose to use instant push notifications in SimpleX iOS app, because the design of push notifications requires storing the device token on notification server, the notifications server can observe how many messaging queues your device has notifications enabled for, and approximately how many messages are sent to each queue. Preset notification server cannot observe the actual addresses of these queues, as a separate address is used to subscribe to the notifications. It also cannot observe who sends messages to you. Apple push notifications servers can only observe how many notifications are sent to you, but not from how many contacts, or from which messaging relays, as notifications are delivered to your device end-to-end encrypted by one of the preset notification servers - these notifications only contain end-to-end encrypted metadata, not even encrypted message content, and they look completely random to Apple push notification servers. -You can read more about the design of iOS push notifications [here](https://simplex.chat/blog/20220404-simplex-chat-instant-notifications.html#our-ios-approach-has-one-trade-off). +You can read more about the design of iOS push notifications [here](./blog/20220404-simplex-chat-instant-notifications.md#our-ios-approach-has-one-trade-off). #### Another information stored on the servers -Additional technical information can be stored on our servers, including randomly generated authentication tokens, keys, push tokens, and other material that is necessary to transmit messages. SimpleX Chat design limits this additional technical information to the minimum required to operate the software and servers. To prevent server overloading or attacks, the servers can temporarily store data that can link to particular users or devices, including IP addresses, geographic location, or information related to the transport sessions. This information is not stored for the absolute majority of the app users, even for those who use the servers very actively. +Additional technical information can be stored on the network servers, including randomly generated authentication tokens, keys, push tokens, and other material that is necessary to transmit messages. SimpleX network design limits this additional technical information to the minimum required to operate the software and servers. To prevent server overloading or attacks, the servers can temporarily store data that can link to particular users or devices, including IP addresses, geographic location, or information related to the transport sessions. This information is not stored for the absolute majority of the app users, even for those who use the servers very actively. #### SimpleX Directory -[SimpleX Directory](./docs/DIRECTORY.md) stores: your search requests, the messages and the members profiles in the registered groups. You can connect to SimpleX Directory via [this address](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). +This section applies only to the experimental group directory operated by SimpleX Chat Ltd. + +[SimpleX Directory](/docs/DIRECTORY.md) stores: your search requests, the messages and the members profiles in the registered groups. You can connect to SimpleX Directory via [this address](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). + +#### Public groups and content channels + +You may participate in a public group and receive content from a public channel (Group). In case you send messages or comments to the Group, you grant a license: +- to all recipients: + - to share your messages with the new Group members and outside of the group, e.g. via quoting (replying), forwarding and copy-pasting your message. When your message is deleted or marked as deleted, the copies of your message will not be deleted. + - to retain a copy of your messages according to the Group settings (e.g., the Group may allow irreversible message deletion from the recipient devices for a limited period of time, or it may only allow to edit and mark messages as deleted on recipient devices). Deleting message from the recipient devices or marking message as deleted revokes the license to share the message. +- to Group owners: to share your messages with the new Group members as history of the Group. Currently, the Group history shared with the new members is limited to 100 messages. + +Group owners may use chat relays or automated bots (Chat Relays) to re-broadcast member messages to all members, for efficiency. The Chat Relays may be operated by the group owners, by preset operators or by 3rd parties. The Chat Relays have access to and will retain messages in line with Group settings, for technical functioning of the Group. Neither you nor group owners grant any content license to Chat Relay operators. #### User Support -If you contact SimpleX Chat Ltd, any personal data you share with us is kept only for the purposes of researching the issue and contacting you about your case. We recommend contacting support [via chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion) when it is possible, and avoid sharing any personal information. +The app includes support contact operated by SimpleX Chat Ltd. If you contact support, any personal data you share is kept only for the purposes of researching the issue and contacting you about your case. We recommend contacting support [via chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion) when it is possible, and avoid sharing any personal information. -### Information we may share +### Preset Server Operators -SimpleX Chat Ltd operates preset relay servers using third parties. While we do not have access and cannot share any user data, these third parties may access the encrypted user messages (but NOT the actual unencrypted message content or size) as it is stored or transmitted via our servers. Hosting providers can also store IP addresses and other transport information as part of their logs. +Preset server operators will not share the information on their servers with each other, other than aggregate usage statistics. -We use a third party for email services - if you ask for support via email, your and SimpleX Chat Ltd email providers may access these emails according to their privacy policies and terms. When the request is sensitive, we recommend contacting us via SimpleX Chat or using encrypted email using PGP key published at [openpgp.org](https://keys.openpgp.org/search?q=chat%40simplex.chat). +Preset server operators must not provide general access to their servers or the data on their servers to each other. -The cases when SimpleX Chat Ltd may share the data temporarily stored on the servers: +Preset server operators will provide non-administrative access to control port of preset servers to SimpleX Chat Ltd, for the purposes of removing illegal content identified in publicly accessible resources (contact and group addresses, and downloadable files). This control port access only allows deleting known links and files, and accessing aggregate server-wide statistics, but does NOT allow enumerating any information on the servers or accessing statistics related to specific users. + +### Information Preset Server Operators May Share + +The preset server operators use third parties. While they do not have access and cannot share any user data, these third parties may access the encrypted user messages (but NOT the actual unencrypted message content or size) as it is stored or transmitted via the servers. Hosting and network providers can also store IP addresses and other transport information as part of their logs. + +SimpleX Chat Ltd uses a third party for email services - if you ask for support via email, your and SimpleX Chat Ltd email providers may access these emails according to their privacy policies and terms. When the request is sensitive, please contact us via SimpleX Chat apps or using encrypted email using PGP key published at [openpgp.org](https://keys.openpgp.org/search?q=chat%40simplex.chat). + +The cases when the preset server operators may share the data temporarily stored on the servers: - To meet any applicable law, or enforceable governmental request or court order. - To enforce applicable terms, including investigation of potential violations. - To detect, prevent, or otherwise address fraud, security, or technical issues. -- To protect against harm to the rights, property, or safety of software users, SimpleX Chat Ltd, or the public as required or permitted by law. +- To protect against harm to the rights, property, or safety of software users, operators of preset servers, or the public as required or permitted by law. -At the time of updating this document, we have never provided or have been requested the access to the preset relay servers or any information from the servers by any third parties. If we are ever requested to provide such access or information, we will follow the due legal process to limit any information shared with the third parties to the minimally required by law. +By the time of updating this document, the preset server operators were not served with any enforceable requests and did not provide any information from the servers to any third parties. If the preset server operators are ever requested to provide such access or information, they will follow the due legal process to limit any information shared with the third parties to the minimally required by law. + +Preset server operators will publish information they are legally allowed to share about such requests in the [Transparency reports](./docs/TRANSPARENCY.md). + +### Source code license + +As this software is fully open-source and provided under AGPLv3 license, all infrastructure owners and operators, and the developers of the client and server applications who use the SimpleX Chat source code, are required to publish any changes to this software under the same AGPLv3 license - including any modifications to the servers. + +In addition to the AGPLv3 license terms, the preset relay server operators are committed to the software users that these servers will always be compiled from the [published open-source code](https://github.com/simplex-chat/simplexmq), without any modifications. ### Updates -We will update this Privacy Policy as needed so that it is current, accurate, and as clear as possible. Your continued use of our software applications and preset relays infrastructure confirms your acceptance of our updated Privacy Policy. +This Privacy Policy applies to SimpleX Chat Ltd and all other preset server operators you use in the app. -Please also read our Conditions of Use of Software and Infrastructure below. +This Privacy Policy may be updated as needed so that it is current, accurate, and as clear as possible. When it is updated, you will have to review and accept the changed policy within 30 days of such changes to continue using preset relay servers. Even if you fail to accept the changed policy, your continued use of SimpleX Chat software applications and preset relay servers confirms your acceptance of the updated Privacy Policy. -If you have questions about our Privacy Policy please contact us via [email](chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion). +Please also read The Conditions of Use of Software and Infrastructure below. + +If you have questions about this Privacy Policy please contact SimpleX Chat Ltd via [email](mailto:chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion). ## Conditions of Use of Software and Infrastructure -You accept the Conditions of Use of Software and Infrastructure ("Conditions") by installing or using any of our software or using any of our server infrastructure (collectively referred to as "Applications"), whether preset in the software or not. +You accept the Conditions of Use of Software and Infrastructure ("Conditions") by installing or using any of SimpleX Chat software or using any of server infrastructure (collectively referred to as "Applications") operated by the Preset Server Operators, including SimpleX Chat Ltd, whether these servers are preset in the software or not. -**Minimal age**. You must be at least 13 years old to use our Applications. The minimum age to use our Applications without parental approval may be higher in your country. +**Minimal age**. You must be at least 13 years old to use SimpleX Chat Applications. The minimum age to use SimpleX Applications without parental approval may be higher in your country. -**Infrastructure**. Our Infrastructure includes preset messaging and file relay servers, and iOS push notification servers provided by SimpleX Chat Ltd for public use. Our infrastructure does not have any modifications from the [published open-source code](https://github.com/simplex-chat/simplexmq) available under AGPLv3 license. Any infrastructure provider, whether commercial or not, is required by the Affero clause (named after Affero Inc. company that pioneered the community-based Q&A sites in early 2000s) to publish any modifications under the same license. The statements in relation to Infrastructure and relay servers anywhere in this document assume no modifications to the published code, even in the cases when it is not explicitly stated. +**Infrastructure**. Infrastructure of the preset server operators includes messaging and file relay servers. SimpleX Chat Ltd also provides iOS push notification servers for public use. This infrastructure does not have any modifications from the [published open-source code](https://github.com/simplex-chat/simplexmq) available under AGPLv3 license. Any infrastructure provider, whether commercial or not, is required by the Affero clause (named after Affero Inc. company that pioneered the community-based Q&A sites in early 2000s) to publish any modifications under the same license. The statements in relation to Infrastructure and relay servers anywhere in this document assume no modifications to the published code, even in the cases when it is not explicitly stated. -**Client applications**. Our client application Software (referred to as "app" or "apps") also has no modifications compared with published open-source code, and any developers of the alternative client apps based on our code are required to publish any modifications under the same AGPLv3 license. Client applications should not include any tracking or analytics code, and do not share any information with SimpleX Chat Ltd or any other third parties. If you ever discover any tracking or analytics code, please report it to us, so we can remove it. +**Client applications**. SimpleX Chat client application Software (referred to as "app" or "apps") also has no modifications compared with published open-source code, and any developers of the alternative client apps based on SimpleX Chat code are required to publish any modifications under the same AGPLv3 license. Client applications should not include any tracking or analytics code, and do not share any tracking information with SimpleX Chat Ltd, preset server operators or any other third parties. If you ever discover any tracking or analytics code, please report it to SimpleX Chat Ltd, so it can be removed. -**Accessing the infrastructure**. For the efficiency of the network access, the client Software by default accesses all queues your app creates on any relay server within one user profile via the same network (TCP/IP) connection. At the cost of additional traffic this configuration can be changed to use different transport session for each connection. Relay servers do not collect information about which queues were created or accessed via the same connection, so the relay servers cannot establish which queues belong to the same user profile. Whoever might observe your network traffic would know which relay servers you use, and how much data you send, but not to whom it is sent - the data that leaves the servers is always different from the data they receive - there are no identifiers or ciphertext in common, even inside TLS encryption layer. Please refer to our [technical design document](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md) for more information about our privacy model and known security and privacy risks. +**Accessing the infrastructure**. For the efficiency of the network access, the client Software by default accesses all queues your app creates on any relay server within one user profile via the same network (TCP/IP) connection. At the cost of additional traffic this configuration can be changed to use different transport session for each connection. Relay servers do not collect information about which queues were created or accessed via the same connection, so the relay servers cannot establish which queues belong to the same user profile. Whoever might observe your network traffic would know which relay servers you use, and how much data you send, but not to whom it is sent - the data that leaves the servers is always different from the data they receive - there are no identifiers or ciphertext in common, even inside TLS encryption layer. Please refer to the [technical design document](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md) for more information about the privacy model and known security and privacy risks. -**Privacy of user data**. Servers do not retain any data we transmit for any longer than necessary to deliver the messages between apps. SimpleX Chat Ltd collects aggregate statistics across all its servers, as supported by published code and can be enabled by any infrastructure provider, but not any statistics per-user, or per geographic location, or per IP address, or per transport session. We do not have information about how many people use SimpleX Chat applications, we only know an approximate number of app installations and the aggregate traffic through the preset servers. In any case, we do not and will not sell or in any way monetize user data. Our future business model assumes charging for some optional Software features instead, in a transparent and fair way. +**Privacy of user data**. Servers do not retain any data you transmit for any longer than necessary to deliver the messages between apps. Preset server operators collect aggregate statistics across all their servers, as supported by published code and can be enabled by any infrastructure operator, but not any statistics per-user, or per geographic location, or per IP address, or per transport session. SimpleX Chat Ltd does not have information about how many people use SimpleX Chat applications, it only knows an approximate number of app installations and the aggregate traffic through the preset servers. In any case, preset server operators do not and will not sell or in any way monetize user data. The future business model assumes charging for some optional Software features instead, in a transparent and fair way. -**Operating our Infrastructure**. For the purpose of using our Software, if you continue using preset servers, you agree that your end-to-end encrypted messages are transferred via the preset servers in any countries where we have or use facilities and service providers or partners. The information about geographic location of the servers will be made available in the apps in the near future. +**Operating Infrastructure**. For the purpose of using SimpleX Chat Software, if you continue using preset servers, you agree that your end-to-end encrypted messages are transferred via the preset servers in any countries where preset server operators have or use facilities and service providers or partners. The information about geographic location and hosting providers of the preset messaging servers is available on server pages. -**Software**. You agree to downloading and installing updates to our Applications when they are available; they would only be automatic if you configure your devices in this way. +**Software**. You agree to downloading and installing updates to SimpleX Chat Applications when they are available; they would only be automatic if you configure your devices in this way. -**Traffic and device costs**. You are solely responsible for the traffic and device costs that you incur while using our Applications, and any associated taxes. +**Traffic and device costs**. You are solely responsible for the traffic and device costs that you incur while using SimpleX Chat Applications, and any associated taxes. -**Legal and acceptable usage**. You agree to use our Applications only for legal and acceptable purposes. You will not use (or assist others in using) our Applications in ways that: 1) violate or infringe the rights of Software users, SimpleX Chat Ltd, or others, including privacy, publicity, intellectual property, or other proprietary rights; 2) involve sending illegal or impermissible communications, e.g. spam. While we cannot access content or identify messages or groups, in some cases the links to the illegal or impermissible communications available via our Applications can be shared publicly on social media or websites. We reserve the right to remove such links from the preset servers and disrupt the conversations that send illegal content via our servers, whether they were reported by the users or discovered by our team. +**Legal usage**. You agree to use SimpleX Chat Applications only for legal purposes. You will not use (or assist others in using) the Applications in ways that: 1) violate or infringe the rights of Software users, SimpleX Chat Ltd, other preset server operators, or others, including privacy, publicity, intellectual property, or other proprietary rights; 2) involve sending illegal communications, e.g. spam. While server operators cannot access content or identify messages or groups, in some cases the links to the illegal communications can be shared publicly on social media or websites. Preset server operators reserve the right to remove such links from the preset servers and disrupt the conversations that send illegal content via their servers, whether they were reported by the users or discovered by the operators themselves. -**Damage to SimpleX Chat Ltd**. You must not (or assist others to) access, use, modify, distribute, transfer, or exploit our Applications in unauthorized manners, or in ways that harm Software users, SimpleX Chat Ltd, our Infrastructure, or any other systems. For example, you must not 1) access our Infrastructure or systems without authorization, in any way other than by using the Software; 2) disrupt the integrity or performance of our Infrastructure; 3) collect information about our users in any manner; or 4) sell, rent, or charge for our Infrastructure. This does not prohibit you from providing your own Infrastructure to others, whether free or for a fee, as long as you do not violate these Conditions and AGPLv3 license, including the requirement to publish any modifications of the relay server software. +**Damage to SimpleX Chat Ltd and Preset Server Operators**. You must not (or assist others to) access, use, modify, distribute, transfer, or exploit SimpleX Chat Applications in unauthorized manners, or in ways that harm Software users, SimpleX Chat Ltd, other preset server operators, their Infrastructure, or any other systems. For example, you must not 1) access preset operators' Infrastructure or systems without authorization, in any way other than by using the Software or by using a 3rd party client applications that satisfies the requirements of the Conditions of use (see the next section); 2) disrupt the integrity or performance of preset operators' Infrastructure; 3) collect information about the users in any manner; or 4) sell, rent, or charge for preset operators' Infrastructure. This does not prohibit you from providing your own Infrastructure to others, whether free or for a fee, as long as you do not violate these Conditions and AGPLv3 license, including the requirement to publish any modifications of the relay server software. -**Keeping your data secure**. SimpleX Chat is the first communication software that aims to be 100% private by design - server software neither has the ability to access your messages, nor it has information about who you communicate with. That means that you are solely responsible for keeping your device, your user profile and any data safe and secure. If you lose your phone or remove the Software from the device, you will not be able to recover the lost data, unless you made a back up. To protect the data you need to make regular backups, as using old backups may disrupt your communication with some of the contacts. +**3rd party client applications**. You may use a 3rd party application (App) to access preset operators' Infrastructure or systems, provided that this App: +- is compatible with the protocol specifications not older than 1 year, +- provides user-to-user messaging only or enables automated chat bots sending messages requested by users (in case of bots, it must be made clear to the users that these are automated bots), +- implements the same limits, rules and restrictions as Software, +- requires that the users accept the same Conditions of use of preset operators' Infrastructure as in Software prior to providing access to this Infrastructure, +- displays the notice that it is the App for using SimpleX network, +- provides its source code under open-source license accessible to the users via the App interface. In case the App uses the source code of Software, the App's source code must be provided under AGPLv3 license, and in case it is developed without using Software code its source code must be provided under any widely recognized free open-source license, +- does NOT use the branding of SimpleX Chat Ltd without the permission, +- does NOT pretend to be Software, +- complies with these Conditions of use. + +**Keeping your data secure**. SimpleX Chat is the first communication software that aims to be 100% private by design - server software neither has the ability to access your messages, nor it has information about who you communicate with. That means that you are solely responsible for keeping your device, your user profile and any data safe and secure. If you lose your phone or remove the Software from the device, you will not be able to recover the lost data, unless you made a back up. To protect the data you need to make regular backups, as using old backups may disrupt your communication with some of the contacts. SimpleX Chat Ltd and other preset server operators are not responsible for any data loss. **Storing the messages on the device**. The messages are stored in the encrypted database on your device. Whether and how database passphrase is stored is determined by the configuration of the Software you use. The databases created prior to 2023 or in CLI (terminal) app may remain unencrypted, and it will be indicated in the app interface. In this case, if you make a backup of the data and store it unencrypted, the backup provider may be able to access the messages. Please note, that the desktop apps can be configured to store the database passphrase in the configuration file in plaintext, and unless you set the passphrase when first running the app, a random passphrase will be used and stored on the device. You can remove it from the device via the app settings. **Storing the files on the device**. The files currently sent and received in the apps by default (except CLI app) are stored on your device encrypted using unique keys, different for each file, that are stored in the database. Once the message that the file was attached to is removed, even if the copy of the encrypted file is retained, it should be impossible to recover the key allowing to decrypt the file. This local file encryption may affect app performance, and it can be disabled via the app settings. This change will only affect the new files. If you later re-enable the encryption, it will also affect only the new files. If you make a backup of the app data and store it unencrypted, the backup provider will be able to access any unencrypted files. In any case, irrespective of the storage setting, the files are always sent by all apps end-to-end encrypted. -**No Access to Emergency Services**. Our Applications do not provide access to emergency service providers like the police, fire department, hospitals, or other public safety organizations. Make sure you can contact emergency service providers through a mobile, fixed-line telephone, or other service. +**No Access to Emergency Services**. SimpleX Chat Applications do not provide access to emergency service providers like the police, fire department, hospitals, or other public safety organizations. Make sure you can contact emergency service providers through a mobile, fixed-line telephone, or other service. -**Third-party services**. Our Applications may allow you to access, use, or interact with our or third-party websites, apps, content, and other products and services. When you use third-party services, their terms and privacy policies govern your use of those services. +**Third-party services**. SimpleX Chat Applications may allow you to access, use, or interact with the websites of SimpleX Chat Ltd, preset server operators or other third-party websites, apps, content, and other products and services. When you use third-party services, their terms and privacy policies govern your use of those services. -**Your Rights**. You own the messages and the information you transmit through our Applications. Your recipients are able to retain the messages they receive from you; there is no technical ability to delete data from their devices. While there are various app features that allow deleting messages from the recipients' devices, such as _disappearing messages_ and _full message deletion_, their functioning on your recipients' devices cannot be guaranteed or enforced, as the device may be offline or have a modified version of the Software. At the same time, repudiation property of the end-to-end encryption algorithm allows you to plausibly deny having sent the message, like you can deny what you said in a private face-to-face conversation, as the recipient cannot provide any proof to the third parties, by design. +**Your Rights**. You own the messages and the information you transmit through SimpleX Applications. Your recipients are able to retain the messages they receive from you; there is no technical ability to delete data from their devices. While there are various app features that allow deleting messages from the recipients' devices, such as _disappearing messages_ and _full message deletion_, their functioning on your recipients' devices cannot be guaranteed or enforced, as the device may be offline or have a modified version of the Software. At the same time, repudiation property of the end-to-end encryption algorithm allows you to plausibly deny having sent the message, like you can deny what you said in a private face-to-face conversation, as the recipient cannot provide any proof to the third parties, by design. -**License**. SimpleX Chat Ltd grants you a limited, revocable, non-exclusive, and non-transferable license to use our Applications in accordance with these Conditions. The source-code of Applications is available and can be used under [AGPL v3 license](https://github.com/simplex-chat/simplex-chat/blob/stable/LICENSE). +**License**. SimpleX Chat Ltd grants you a limited, revocable, non-exclusive, and non-transferable license to use SimpleX Chat Applications in accordance with these Conditions. The source-code of Applications is available and can be used under [AGPL v3 license](https://github.com/simplex-chat/simplex-chat/blob/stable/LICENSE). -**SimpleX Chat Ltd Rights**. We own all copyrights, trademarks, domains, logos, trade secrets, and other intellectual property rights associated with our Applications. You may not use our copyrights, trademarks, domains, logos, and other intellectual property rights unless you have our written permission, and unless under an open-source license distributed together with the source code. To report copyright, trademark, or other intellectual property infringement, please contact chat@simplex.chat. +**SimpleX Chat Ltd Rights**. SimpleX Chat Ltd (and, where applicable, preset server operators) owns all copyrights, trademarks, domains, logos, trade secrets, and other intellectual property rights associated with the Applications. You may not use SimpleX Chat Ltd copyrights, trademarks, domains, logos, and other intellectual property rights unless you have SimpleX Chat Ltd written permission, and unless under an open-source license distributed together with the source code. To report copyright, trademark, or other intellectual property infringement, please contact chat@simplex.chat. -**Disclaimers**. YOU USE OUR APPLICATIONS AT YOUR OWN RISK AND SUBJECT TO THE FOLLOWING DISCLAIMERS. WE PROVIDE OUR APPLICATIONS ON AN “AS IS” BASIS WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, AND FREEDOM FROM COMPUTER VIRUS OR OTHER HARMFUL CODE. SIMPLEX CHAT LTD DOES NOT WARRANT THAT ANY INFORMATION PROVIDED BY US IS ACCURATE, COMPLETE, OR USEFUL, THAT OUR APPLICATIONS WILL BE OPERATIONAL, ERROR-FREE, SECURE, OR SAFE, OR THAT OUR APPLICATIONS WILL FUNCTION WITHOUT DISRUPTIONS, DELAYS, OR IMPERFECTIONS. WE DO NOT CONTROL, AND ARE NOT RESPONSIBLE FOR, CONTROLLING HOW OR WHEN OUR USERS USE OUR APPLICATIONS. WE ARE NOT RESPONSIBLE FOR THE ACTIONS OR INFORMATION (INCLUDING CONTENT) OF OUR USERS OR OTHER THIRD PARTIES. YOU RELEASE US, AFFILIATES, DIRECTORS, OFFICERS, EMPLOYEES, PARTNERS, AND AGENTS ("SIMPLEX PARTIES") FROM ANY CLAIM, COMPLAINT, CAUSE OF ACTION, CONTROVERSY, OR DISPUTE (TOGETHER, "CLAIM") AND DAMAGES, KNOWN AND UNKNOWN, RELATING TO, ARISING OUT OF, OR IN ANY WAY CONNECTED WITH ANY SUCH CLAIM YOU HAVE AGAINST ANY THIRD PARTIES. +**Disclaimers**. YOU USE SIMPLEX APPLICATIONS AT YOUR OWN RISK AND SUBJECT TO THE FOLLOWING DISCLAIMERS. SIMPLEX CHAT LTD PROVIDES APPLICATIONS ON AN “AS IS” BASIS WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, AND FREEDOM FROM COMPUTER VIRUS OR OTHER HARMFUL CODE. SIMPLEX CHAT LTD DOES NOT WARRANT THAT ANY INFORMATION PROVIDED BY THEM IS ACCURATE, COMPLETE, OR USEFUL, THAT THEIR APPLICATIONS WILL BE OPERATIONAL, ERROR-FREE, SECURE, OR SAFE, OR THAT THEIR APPLICATIONS WILL FUNCTION WITHOUT DISRUPTIONS, DELAYS, OR IMPERFECTIONS. SIMPLEX CHAT LTD AND OTHER PRESET OPERATORS DO NOT CONTROL, AND ARE NOT RESPONSIBLE FOR, CONTROLLING HOW OR WHEN THE USERS USE APPLICATIONS. SIMPLEX CHAT LTD AND OTHER PRESET OPERATORS ARE NOT RESPONSIBLE FOR THE ACTIONS OR INFORMATION (INCLUDING CONTENT) OF THEIR USERS OR OTHER THIRD PARTIES. YOU RELEASE SIMPLEX CHAT LTD, OTHER PRESET OPERATORS, AFFILIATES, DIRECTORS, OFFICERS, EMPLOYEES, PARTNERS, AND AGENTS ("SIMPLEX PARTIES") FROM ANY CLAIM, COMPLAINT, CAUSE OF ACTION, CONTROVERSY, OR DISPUTE (TOGETHER, "CLAIM") AND DAMAGES, KNOWN AND UNKNOWN, RELATING TO, ARISING OUT OF, OR IN ANY WAY CONNECTED WITH ANY SUCH CLAIM YOU HAVE AGAINST ANY THIRD PARTIES. -**Limitation of liability**. THE SIMPLEX PARTIES WILL NOT BE LIABLE TO YOU FOR ANY LOST PROFITS OR CONSEQUENTIAL, SPECIAL, PUNITIVE, INDIRECT, OR INCIDENTAL DAMAGES RELATING TO, ARISING OUT OF, OR IN ANY WAY IN CONNECTION WITH OUR CONDITIONS, US, OR OUR APPLICATIONS, EVEN IF THE SIMPLEX PARTIES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. OUR AGGREGATE LIABILITY RELATING TO, ARISING OUT OF, OR IN ANY WAY IN CONNECTION WITH OUR CONDITIONS, US, OR OUR APPLICATIONS WILL NOT EXCEED ONE DOLLAR ($1). THE FOREGOING DISCLAIMER OF CERTAIN DAMAGES AND LIMITATION OF LIABILITY WILL APPLY TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW. THE LAWS OF SOME JURISDICTIONS MAY NOT ALLOW THE EXCLUSION OR LIMITATION OF CERTAIN DAMAGES, SO SOME OR ALL OF THE EXCLUSIONS AND LIMITATIONS SET FORTH ABOVE MAY NOT APPLY TO YOU. NOTWITHSTANDING ANYTHING TO THE CONTRARY IN OUR CONDITIONS, IN SUCH CASES, THE LIABILITY OF THE SIMPLEX PARTIES WILL BE LIMITED TO THE EXTENT PERMITTED BY APPLICABLE LAW. +**Limitation of liability**. THE SIMPLEX PARTIES WILL NOT BE LIABLE TO YOU FOR ANY LOST PROFITS OR CONSEQUENTIAL, SPECIAL, PUNITIVE, INDIRECT, OR INCIDENTAL DAMAGES RELATING TO, ARISING OUT OF, OR IN ANY WAY IN CONNECTION WITH OUR CONDITIONS, US, OR SIMPLEX APPLICATIONS, EVEN IF THE SIMPLEX PARTIES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE AGGREGATE LIABILITY OF THE SIMPLEX PARTIES RELATING TO, ARISING OUT OF, OR IN ANY WAY IN CONNECTION WITH THESE CONDITIONS, THE SIMPLEX PARTIES, OR THE APPLICATIONS WILL NOT EXCEED ONE DOLLAR ($1). THE FOREGOING DISCLAIMER OF CERTAIN DAMAGES AND LIMITATION OF LIABILITY WILL APPLY TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW. THE LAWS OF SOME JURISDICTIONS MAY NOT ALLOW THE EXCLUSION OR LIMITATION OF CERTAIN DAMAGES, SO SOME OR ALL OF THE EXCLUSIONS AND LIMITATIONS SET FORTH ABOVE MAY NOT APPLY TO YOU. NOTWITHSTANDING ANYTHING TO THE CONTRARY IN THE CONDITIONS, IN SUCH CASES, THE LIABILITY OF THE SIMPLEX PARTIES WILL BE LIMITED TO THE EXTENT PERMITTED BY APPLICABLE LAW. -**Availability**. Our Applications may be disrupted, including for maintenance, upgrades, or network or equipment failures. We may discontinue some or all of our Applications, including certain features and the support for certain devices and platforms, at any time. +**Availability**. The Applications may be disrupted, including for maintenance, upgrades, or network or equipment failures. SimpleX Chat Ltd may discontinue some or all of their Applications, including certain features and the support for certain devices and platforms, at any time. Preset server operators may discontinue providing the servers, at any time. -**Resolving disputes**. You agree to resolve any Claim you have with us relating to or arising from our Conditions, us, or our Applications in the courts of England and Wales. You also agree to submit to the personal jurisdiction of such courts for the purpose of resolving all such disputes. The laws of England govern our Conditions, as well as any disputes, whether in court or arbitration, which might arise between SimpleX Chat Ltd and you, without regard to conflict of law provisions. +**Resolving disputes**. You agree to resolve any Claim you have with SimpleX Chat Ltd and/or preset server operators relating to or arising from these Conditions, them, or the Applications in the courts of England and Wales. You also agree to submit to the personal jurisdiction of such courts for the purpose of resolving all such disputes. The laws of England govern these Conditions, as well as any disputes, whether in court or arbitration, which might arise between SimpleX Chat Ltd (or preset server operators) and you, without regard to conflict of law provisions. -**Changes to the conditions**. SimpleX Chat Ltd may update the Conditions from time to time. Your continued use of our Applications confirms your acceptance of our updated Conditions and supersedes any prior Conditions. You will comply with all applicable export control and trade sanctions laws. Our Conditions cover the entire agreement between you and SimpleX Chat Ltd regarding our Applications. If you do not agree with our Conditions, you should stop using our Applications. +**Changes to the conditions**. SimpleX Chat Ltd may update the Conditions from time to time. The updated conditions have to be accepted within 30 days. Even if you fail to accept updated conditions, your continued use of SimpleX Chat Applications confirms your acceptance of the updated Conditions and supersedes any prior Conditions. You will comply with all applicable export control and trade sanctions laws. These Conditions cover the entire agreement between you and SimpleX Chat Ltd, and any preset server operators where applicable, regarding SimpleX Chat Applications. If you do not agree with these Conditions, you should stop using the Applications. -**Enforcing the conditions**. If we fail to enforce any of our Conditions, that does not mean we waive the right to enforce them. If any provision of the Conditions is deemed unlawful, void, or unenforceable, that provision shall be deemed severable from our Conditions and shall not affect the enforceability of the remaining provisions. Our Applications are not intended for distribution to or use in any country where such distribution or use would violate local law or would subject us to any regulations in another country. We reserve the right to limit our Applications in any country. If you have specific questions about these Conditions, please contact us at chat@simplex.chat. +**Enforcing the conditions**. If SimpleX Chat Ltd or preset server operators fail to enforce any of these Conditions, that does not mean they waive the right to enforce them. If any provision of the Conditions is deemed unlawful, void, or unenforceable, that provision shall be deemed severable from the Conditions and shall not affect the enforceability of the remaining provisions. The Applications are not intended for distribution to or use in any country where such distribution or use would violate local law or would subject SimpleX Chat Ltd to any regulations in another country. SimpleX Chat Ltd reserve the right to limit the access to the Applications in any country. Preset operators reserve the right to limit access to their servers in any country. If you have specific questions about these Conditions, please contact SimpleX Chat Ltd at chat@simplex.chat. -**Ending these conditions**. You may end these Conditions with SimpleX Chat Ltd at any time by deleting our Applications from your devices and discontinuing use of our Infrastructure. The provisions related to Licenses, Disclaimers, Limitation of Liability, Resolving dispute, Availability, Changes to the conditions, Enforcing the conditions, and Ending these conditions will survive termination of your relationship with SimpleX Chat Ltd. +**Ending these conditions**. You may end these Conditions with SimpleX Chat Ltd and preset server operators at any time by deleting the Applications from your devices and discontinuing use of the Infrastructure of SimpleX Chat Ltd and preset server operators. The provisions related to Licenses, Disclaimers, Limitation of Liability, Resolving dispute, Availability, Changes to the conditions, Enforcing the conditions, and Ending these conditions will survive termination of your relationship with SimpleX Chat Ltd and/or preset server operators. -Updated February 24, 2024 +Updated March 3, 2025 diff --git a/README.md b/README.md index 4cd7b2b787..554c6068d9 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,13 @@ [![Join on Reddit](https://img.shields.io/reddit/subreddit-subscribers/SimpleXChat?style=social)](https://www.reddit.com/r/SimpleXChat) ![Follow on Mastodon](https://img.shields.io/mastodon/follow/108619463746856738?domain=https%3A%2F%2Fmastodon.social&style=social) -| 30/03/2023 | EN, [FR](/docs/lang/fr/README.md), [CZ](/docs/lang/cs/README.md) | +| 30/03/2023 | EN, [FR](/docs/lang/fr/README.md), [CZ](/docs/lang/cs/README.md), [PL](/docs/lang/pl/README.md) | SimpleX logo # SimpleX - the first messaging platform that has no user identifiers of any kind - 100% private by design! -[](http://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html)     [](https://www.privacyguides.org/en/real-time-communication/#simplex-chat)     [](https://www.kuketz-blog.de/simplex-eindruecke-vom-messenger-ohne-identifier/) +[](http://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html)     [](https://www.privacyguides.org/en/real-time-communication/#simplex-chat)     [](https://www.whonix.org/wiki/Chat#Recommendation)     [](https://www.kuketz-blog.de/simplex-eindruecke-vom-messenger-ohne-identifier/) ## Welcome to SimpleX Chat! @@ -18,7 +18,7 @@ 2. ↔️ [Connect to the team](#connect-to-the-team), [join user groups](#join-user-groups) and [follow our updates](#follow-our-updates). 3. 🤝 [Make a private connection](#make-a-private-connection) with a friend. 4. 🔤 [Help translating SimpleX Chat](#help-translating-simplex-chat). -5. ⚡️ [Contribute](#contribute) and [help us with donations](#help-us-with-donations). +5. ⚡️ [Contribute](#contribute) and [support us with donations](#please-support-us-with-your-donations). [Learn more about SimpleX Chat](#contents). @@ -72,9 +72,9 @@ You must: Messages not following these rules will be deleted, the right to send messages may be revoked, and the access to the new members to the group may be temporarily restricted, to prevent re-joining under a different name - our imperfect group moderation does not have a better solution at the moment. -You can join an English-speaking users group if you want to ask any questions: [#SimpleX users group](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2Fos8FftfoV8zjb2T89fUEjJtF7y64p5av%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAQqMgh0fw2lPhjn3PDIEfAKA_E0-gf8Hr8zzhYnDivRs%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22lBPiveK2mjfUH43SN77R0w%3D%3D%22%7D) +You can join an English-speaking users group if you want to ask any questions: [#SimpleX users group](https://simplex.chat/contact#/?v=2-7&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FiBkJE72asZX1NUZaYFIeKRVk6oVjb-iv%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEAinqu3j74AMjODLoIRR487ZW6ysip_dlpD6Zxk18SPFY%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22groupLinkId%22%3A%223wAFGCLygQHR5AwynZOHlQ%3D%3D%22%7D) -There is also a group [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2F6eHqy7uAbZPOcA6qBtrQgQquVlt4Ll91%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAqV_pg3FF00L98aCXp4D3bOs4Sxv_UmSd-gb0juVoQVs%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22XonlixcHBIb2ijCehbZoiw%3D%3D%22%7D) for developers who build on SimpleX platform: +There is also a group [#simplex-devs](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FvYCRjIflKNMGYlfTkuHe4B40qSlQ0439%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAHNdcqNbzXZhyMoSBjT2R0-Eb1EPaLyUg3KZjn-kmM1w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22PD20tcXjw7IpkkMCfR6HLA%3D%3D%22%7D) for developers who build on SimpleX platform: - chat bots and automations - integrations with other apps @@ -83,7 +83,7 @@ There is also a group [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=s There are groups in other languages, that we have the apps interface translated into. These groups are for testing, and asking questions to other SimpleX Chat users: -[\#SimpleX-DE](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FkIEl7OQzcp-J6aDmjdlQbRJwqkcZE7XR%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAR16PCu02MobRmKAsjzhDWMZcWP9hS8l5AUZi-Gs8z18%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22puYPMCQt11yPUvgmI5jCiw%3D%3D%22%7D) (German-speaking), [\#SimpleX-ES](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FaJ8O1O8A8GbeoaHTo_V8dcefaCl7ouPb%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEA034qWTA3sWcTsi6aWhNf9BA34vKVCFaEBdP2R66z6Ao%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22wiZ1v_wNjLPlT-nCSB-bRA%3D%3D%22%7D) (Spanish-speaking), [\#SimpleX-FR](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FvIHQDxTor53nwnWWTy5cHNwQQAdWN5Hw%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAPdgK1eBnETmgiqEQufbUkydKBJafoRx4iRrtrC2NAGc%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%221FyUryBPza-1ZFFE80Ekbg%3D%3D%22%7D) (French-speaking), [\#SimpleX-RU](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FXZyt3hJmWsycpN7Dqve_wbrAqb6myk1R%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAMFVIoytozTEa_QXOgoZFq_oe0IwZBYKvW50trSFXzXo%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22xz05ngjA3pNIxLZ32a8Vxg%3D%3D%22%7D) (Russian-speaking), [\#SimpleX-IT](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2F0weR-ZgDUl7ruOtI_8TZwEsnJP6UiImA%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAq4PSThO9Fvb5ydF48wB0yNbpzCbuQJCW3vZ9BGUfcxk%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22e-iceLA0SctC62eARgYDWg%3D%3D%22%7D) (Italian-speaking). +[\#SimpleX-DE](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FmfiivxDKWFuowXrQOp11jsY8TuP__rBL%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAiz3pKNwvKudckFYMUfgoT0s96B0jfZ7ALHAu7rtE9HQ%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22jZeJpXGrRXQJU_-MSJ_v2A%3D%3D%22%7D) (German-speaking), [\#SimpleX-ES](https://simplex.chat/contact#/?v=2-4&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FJ5ES83pJimY2BRklS8fvy_iQwIU37xra%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEA0F0STP6UqN_12_k2cjjTrIjFgBGeWhOAmbY1qlk3pnM%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22VmUU0fqmYdCRmVCyvStvHA%3D%3D%22%7D) (Spanish-speaking), [\#SimpleX-FR](https://simplex.chat/contact#/?v=2-7&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FxCHBE_6PBRMqNEpm4UQDHXb9cz-mN7dd%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEAetqlcM7zTCRw-iatnwCrvpJSto7lq5Yv6AsBMWv7GSM%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22foO5Xw4hhjOa_x7zET7otw%3D%3D%22%7D) (French-speaking), [\#SimpleX-RU](https://simplex.chat/contact#/?v=2-4&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FVXQTB0J2lLjYkgjWByhl6-1qmb5fgZHh%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAI6JaEWezfSwvcoTEkk6au-gkjrXR2ew2OqZYMYBvayk%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22ORH9OEe8Duissh-hslfeVg%3D%3D%22%7D) (Russian-speaking), [\#SimpleX-IT](https://simplex.chat/contact#/?v=2-7&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FqpHu0psOUdYfc11yQCzSyq5JhijrBzZT%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEACZ_7fbwlM45wl6cGif8cY47oPQ_AMdP0ATqOYLA6zHY%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%229uRQRTir3ealdcSfB0zsrw%3D%3D%22%7D) (Italian-speaking). You can join either by opening these links in the app or by opening them in a desktop browser and scanning the QR code. @@ -110,6 +110,15 @@ After you connect, you can [verify connection security code](./blog/20230103-sim Read about the app features and settings in the new [User guide](./docs/guide/README.md). +## Contribute + +We would love to have you join the development! You can help us with: + +- [share the color theme](./docs/THEMES.md) you use in Android app! +- writing a tutorial or recipes about hosting servers, chat bot automations, etc. +- contributing to SimpleX Chat knowledge-base. +- developing features - please connect to us via chat so we can help you get started. + ## Help translating SimpleX Chat Thanks to our users and [Weblate](https://hosted.weblate.org/engage/simplex-chat/), SimpleX Chat apps, website and documents are translated to many other languages. @@ -141,16 +150,7 @@ Join our translators to help SimpleX grow! Languages in progress: Arabic, Japanese, Korean, Portuguese and [others](https://hosted.weblate.org/projects/simplex-chat/#languages). We will be adding more languages as some of the already added are completed – please suggest new languages, review the [translation guide](./docs/TRANSLATIONS.md) and get in touch with us! -## Contribute - -We would love to have you join the development! You can help us with: - -- [share the color theme](./docs/THEMES.md) you use in Android app! -- writing a tutorial or recipes about hosting servers, chat bot automations, etc. -- contributing to SimpleX Chat knowledge-base. -- developing features - please connect to us via chat so we can help you get started. - -## Help us with donations +## Please support us with your donations Huge thank you to everybody who donated to SimpleX Chat! @@ -158,20 +158,21 @@ We are prioritizing users privacy and security - it would be impossible without Our pledge to our users is that SimpleX protocols are and will remain open, and in public domain, - so anybody can build the future implementations of the clients and the servers. We are building SimpleX platform based on the same principles as email and web, but much more private and secure. -Your donations help us raise more funds – any amount, even the price of the cup of coffee, would make a big difference for us. +Your donations help us raise more funds - any amount, even the price of the cup of coffee, would make a big difference for us. It is possible to donate via: -- [GitHub](https://github.com/sponsors/simplex-chat) - it is commission-free for us. -- [OpenCollective](https://opencollective.com/simplex-chat) - it charges a commission, and also accepts donations in crypto-currencies. -- Monero: 8568eeVjaJ1RQ65ZUn9PRQ8ENtqeX9VVhcCYYhnVLxhV4JtBqw42so2VEUDQZNkFfsH5sXCuV7FN8VhRQ21DkNibTZP57Qt -- Bitcoin: 1bpefFkzuRoMY3ZuBbZNZxycbg7NYPYTG -- BCH: 1bpefFkzuRoMY3ZuBbZNZxycbg7NYPYTG -- USDT: - - BNB Smart Chain: 0x83fd788f7241a2be61780ea9dc72d2151e6843e2 - - Tron: TNnTrKLBmdy2Wn3cAQR98dAVvWhLskQGfW -- Ethereum: 0x83fd788f7241a2be61780ea9dc72d2151e6843e2 -- Solana: 43tWFWDczgAcn4Rzwkpqg2mqwnQETSiTwznmCgA2tf1L +- [GitHub](https://github.com/sponsors/simplex-chat) (commission-free) or [OpenCollective](https://opencollective.com/simplex-chat) (~10% commission). +- BTC: bc1q2gy6f02nn6vvcxs0pnu29tpnpyz0qf66505d4u +- XMR: 8568eeVjaJ1RQ65ZUn9PRQ8ENtqeX9VVhcCYYhnVLxhV4JtBqw42so2VEUDQZNkFfsH5sXCuV7FN8VhRQ21DkNibTZP57Qt +- BCH: bitcoincash:qq6c8vfvxqrk6rhdysgvkhqc24sggkfsx5nqvdlqcg +- ETH: 0xD9ee7Db0AD0dc1Dfa7eD53290199ED06beA04692 +- USDT (Ethereum): 0xD9ee7Db0AD0dc1Dfa7eD53290199ED06beA04692 +- ZEC: t1fwjQW5gpFhDqXNhxqDWyF9j9WeKvVS5Jg +- ZEC shielded: u16rnvkflumf5uw9frngc2lymvmzgdr2mmc9unyu0l44unwfmdcpfm0axujd2w34ct3ye709azxsqge45705lpvvqu264ltzvfay55ygyq +- DOGE: D99pV4n9TrPxBPCkQGx4w4SMSa6QjRBxPf +- SOL: 7JCf5m3TiHmYKZVr6jCu1KeZVtb9Y1jRMQDU69p5ARnu +- please ask if you want to donate any other coins. Thank you, @@ -234,26 +235,28 @@ You can use SimpleX with your own servers and still communicate with people usin Recent and important updates: +[Mar 8, 2025. SimpleX Chat v6.3: new user experience and safety in public groups](./blog/20250308-simplex-chat-v6-3-new-user-experience-safety-in-public-groups.md) + +[Jan 14, 2025. SimpleX network: large groups and privacy-preserving content moderation](./blog/20250114-simplex-network-large-groups-privacy-preserving-content-moderation.md) + +[Dec 10, 2024. SimpleX network: preset servers operated by Flux, business chats and more with v6.2 of the apps](./20241210-simplex-network-v6-2-servers-by-flux-business-chats.md) + +[Oct 14, 2024. SimpleX network: security review of protocols design by Trail of Bits, v6.1 released with better calls and user experience.](./blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md) + +[Aug 14, 2024. SimpleX network: the investment from Jack Dorsey and Asymmetric, v6.0 released with the new user experience and private message routing](./blog/20240814-simplex-chat-vision-funding-v6-private-routing-new-user-experience.md) + +[Jun 4, 2024. SimpleX network: private message routing, v5.8 released with IP address protection and chat themes](./blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md) + [Mar 14, 2024. SimpleX Chat v5.6 beta: adding quantum resistance to Signal double ratchet algorithm.](./blog/20240314-simplex-chat-v5-6-quantum-resistance-signal-double-ratchet-algorithm.md) -[Jan 24, 2024. SimpleX Chat: free infrastructure from Linode, v5.5 released with private notes, group history and a simpler UX to connect.](./blog/20240124-simplex-chat-infrastructure-costs-v5-5-simplex-ux-private-notes-group-history.md) - [Nov 25, 2023. SimpleX Chat v5.4 released: link mobile and desktop apps via quantum resistant protocol, and much better groups](./blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.md). -[Sep 25, 2023. SimpleX Chat v5.3 released: desktop app, local file encryption, improved groups and directory service](./blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.md). - -[Jul 22, 2023. SimpleX Chat: v5.2 released with message delivery receipts](./blog/20230722-simplex-chat-v5-2-message-delivery-receipts.md). - -[May 23, 2023. SimpleX Chat: v5.1 released with message reactions and self-destruct passcode](./blog/20230523-simplex-chat-v5-1-message-reactions-self-destruct-passcode.md). - [Apr 22, 2023. SimpleX Chat: vision and funding, v5.0 released with videos and files up to 1gb](./blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.md). [Mar 1, 2023. SimpleX File Transfer Protocol – send large files efficiently, privately and securely, soon to be integrated into SimpleX Chat apps.](./blog/20230301-simplex-file-transfer-protocol.md). [Nov 8, 2022. Security audit by Trail of Bits, the new website and v4.2 released](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). -[Sep 28, 2022. v4.0: encrypted local chat database and many other changes](./blog/20220928-simplex-chat-v4-encrypted-database.md). - [All updates](./blog) ## :zap: Quick installation of a terminal app @@ -293,25 +296,28 @@ What is already implemented: 1. Instead of user profile identifiers used by all other platforms, even the most private ones, SimpleX uses [pairwise per-queue identifiers](./docs/GLOSSARY.md#pairwise-pseudonymous-identifier) (2 addresses for each unidirectional message queue, with an optional 3rd address for push notifications on iOS, 2 queues in each connection between the users). It makes observing the network graph on the application level more difficult, as for `n` users there can be up to `n * (n-1)` message queues. 2. [End-to-end encryption](./docs/GLOSSARY.md#end-to-end-encryption) in each message queue using [NaCl cryptobox](https://nacl.cr.yp.to/box.html). This is added to allow redundancy in the future (passing each message via several servers), to avoid having the same ciphertext in different queues (that would only be visible to the attacker if TLS is compromised). The encryption keys used for this encryption are not rotated, instead we are planning to rotate the queues. Curve25519 keys are used for key negotiation. 3. [Double ratchet](./docs/GLOSSARY.md#double-ratchet-algorithm) end-to-end encryption in each conversation between two users (or group members). This is the same algorithm that is used in Signal and many other messaging apps; it provides OTR messaging with [forward secrecy](./docs/GLOSSARY.md#forward-secrecy) (each message is encrypted by its own ephemeral key) and [break-in recovery](./docs/GLOSSARY.md#post-compromise-security) (the keys are frequently re-negotiated as part of the message exchange). Two pairs of Curve448 keys are used for the initial [key agreement](./docs/GLOSSARY.md#key-agreement-protocol), initiating party passes these keys via the connection link, accepting side - in the header of the confirmation message. -4. Additional layer of encryption using NaCL cryptobox for the messages delivered from the server to the recipient. This layer avoids having any ciphertext in common between sent and received traffic of the server inside TLS (and there are no identifiers in common as well). -5. Several levels of [content padding](./docs/GLOSSARY.md#message-padding) to frustrate message size attacks. -6. All message metadata, including the time when the message was received by the server (rounded to a second) is sent to the recipients inside an encrypted envelope, so even if TLS is compromised it cannot be observed. -7. Only TLS 1.2/1.3 are allowed for client-server connections, limited to cryptographic algorithms: CHACHA20POLY1305_SHA256, Ed25519/Ed448, Curve25519/Curve448. -8. To protect against replay attacks SimpleX servers require [tlsunique channel binding](https://www.rfc-editor.org/rfc/rfc5929.html) as session ID in each client command signed with per-queue ephemeral key. -9. To protect your IP address all SimpleX Chat clients support accessing messaging servers via Tor - see [v3.1 release announcement](./blog/20220808-simplex-chat-v3.1-chat-groups.md) for more details. -10. Local database encryption with passphrase - your contacts, groups and all sent and received messages are stored encrypted. If you used SimpleX Chat before v4.0 you need to enable the encryption via the app settings. -11. Transport isolation - different TCP connections and Tor circuits are used for traffic of different user profiles, optionally - for different contacts and group member connections. -12. Manual messaging queue rotations to move conversation to another SMP relay. -13. Sending end-to-end encrypted files using [XFTP protocol](https://simplex.chat/blog/20230301-simplex-file-transfer-protocol.html). -14. Local files encryption. +4. [Post-quantum resistant key exchange](./docs/GLOSSARY.md#post-quantum-cryptography) in double ratchet protocol *on every ratchet step*. Read more in [this post](./blog/20240314-simplex-chat-v5-6-quantum-resistance-signal-double-ratchet-algorithm.md) and also see this [publication by Apple]( https://security.apple.com/blog/imessage-pq3/) explaining the need for post-quantum key rotation. +5. Additional layer of encryption using NaCL cryptobox for the messages delivered from the server to the recipient. This layer avoids having any ciphertext in common between sent and received traffic of the server inside TLS (and there are no identifiers in common as well). +6. Several levels of [content padding](./docs/GLOSSARY.md#message-padding) to frustrate message size attacks. +7. All message metadata, including the time when the message was received by the server (rounded to a second) is sent to the recipients inside an encrypted envelope, so even if TLS is compromised it cannot be observed. +8. Only TLS 1.2/1.3 are allowed for client-server connections, limited to cryptographic algorithms: CHACHA20POLY1305_SHA256, Ed25519/Ed448, Curve25519/Curve448. +9. To protect against replay attacks SimpleX servers require [tlsunique channel binding](https://www.rfc-editor.org/rfc/rfc5929.html) as session ID in each client command signed with per-queue ephemeral key. +10. To protect your IP address from unknown messaging relays, and for per-message transport anonymity (compared with Tor/VPN per-connection anonymity), from v6.0 all SimpleX Chat clients use private message routing by default. Read more in [this post](./blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md#private-message-routing). +11. To protect your IP address from unknown file relays, when SOCKS proxy is not enabled SimpleX Chat clients ask for a confirmation before downloading the files from unknown servers. +12. To protect your IP address from known servers all SimpleX Chat clients support accessing messaging servers via Tor - see [v3.1 release announcement](./blog/20220808-simplex-chat-v3.1-chat-groups.md) for more details. +13. Local database encryption with passphrase - your contacts, groups and all sent and received messages are stored encrypted. If you used SimpleX Chat before v4.0 you need to enable the encryption via the app settings. +14. Transport isolation - different TCP connections and Tor circuits are used for traffic of different user profiles, optionally - for different contacts and group member connections. +15. Manual messaging queue rotations to move conversation to another SMP relay. +16. Sending end-to-end encrypted files using [XFTP protocol](https://simplex.chat/blog/20230301-simplex-file-transfer-protocol.html). +17. Local files encryption. +18. [Reproducible server builds](./docs/SERVER.md#reproduce-builds). We plan to add: -1. Senders' SMP relays and recipients' XFTP relays to reduce traffic and conceal IP addresses from the relays chosen, and potentially controlled, by another party. -2. Post-quantum resistant key exchange in double ratchet protocol. -3. Automatic message queue rotation and redundancy. Currently the queues created between two users are used until the queue is manually changed by the user or contact is deleted. We are planning to add automatic queue rotation to make these identifiers temporary and rotate based on some schedule TBC (e.g., every X messages, or every X hours/days). -4. Message "mixing" - adding latency to message delivery, to protect against traffic correlation by message time. -5. Reproducible builds – this is the limitation of the development stack, but we will be investing into solving this problem. Users can still build all applications and services from the source code. +1. Automatic message queue rotation and redundancy. Currently the queues created between two users are used until the queue is manually changed by the user or contact is deleted. We are planning to add automatic queue rotation to make these identifiers temporary and rotate based on some schedule TBC (e.g., every X messages, or every X hours/days). +2. Message "mixing" - adding latency to message delivery, to protect against traffic correlation by message time. +3. Reproducible clients builds – this is a complex problem, but we are aiming to have it in 2025 at least partially. +4. Recipients' XFTP relays to reduce traffic and conceal IP addresses from the relays chosen, and potentially controlled, by another party. ## For developers @@ -377,10 +383,13 @@ Please also join [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=smp%3A - ✅ Using mobile profiles from the desktop app. - ✅ Private notes. - ✅ Improve sending videos (including encryption of locally stored videos). -- 🏗 Improve experience for the new users. -- 🏗 Post-quantum resistant key exchange in double ratchet protocol. +- ✅ Post-quantum resistant key exchange in double ratchet protocol. +- ✅ Message delivery relay for senders (to conceal IP address from the recipients' servers and to reduce the traffic). +- ✅ Support multiple network operators in the app. - 🏗 Large groups, communities and public channels. -- 🏗 Message delivery relay for senders (to conceal IP address from the recipients' servers and to reduce the traffic). +- 🏗 Short links to connect and join groups. +- 🏗 Improve stability and reduce battery usage. +- 🏗 Improve experience for the new users. - Privacy & security slider - a simple way to set all settings at once. - SMP queue redundancy and rotation (manual is supported). - Include optional message into connection request sent via contact address. @@ -399,7 +408,9 @@ Please also join [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=smp%3A [SimpleX protocols and security model](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md) was reviewed, and had many breaking changes and improvements in v1.0.0. -The security audit was performed in October 2022 by [Trail of Bits](https://www.trailofbits.com/about), and most fixes were released in v4.2.0 – see [the announcement](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). +The implementation security assessment of SimpleX cryptography and networking was done in October 2022 by [Trail of Bits](https://www.trailofbits.com/about) – see [the announcement](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md). + +The cryptographic review of SimpleX protocols was done in July 2024 by Trail of Bits – see [the announcement](./blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md). SimpleX Chat is still a relatively early stage platform (the mobile apps were released in March 2022), so you may discover some bugs and missing features. We would really appreciate if you let us know anything that needs to be fixed or improved. @@ -409,13 +420,13 @@ We have never provided or have been requested access to our servers or any infor We do not log IP addresses of the users and we do not perform any traffic correlation on our servers. If transport level security is critical you must use Tor or some other similar network to access messaging servers. We will be improving the client applications to reduce the opportunities for traffic correlation. -Please read more in [Terms & privacy policy](./PRIVACY.md). +Please read more in [Privacy Policy](./PRIVACY.md). ## Security contact -To report a security vulnerability, please send us email to chat@simplex.chat. We will coordinate the fix and disclosure. Please do NOT report security vulnerabilities via GitHub issues. +Please see our [Security Policy](./docs/SECURITY.md) on how to report security vulnerabilities to us. We will coordinate the fix and disclosure. -Please treat any findings of possible traffic correlation attacks allowing to correlate two different conversations to the same user, other than covered in [the threat model](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/overview-tjr.md#threat-model), as security vulnerabilities, and follow this disclosure process. +Please do NOT report security vulnerabilities via GitHub issues. ## License diff --git a/apps/ios/Shared/AppDelegate.swift b/apps/ios/Shared/AppDelegate.swift index 7204625ad4..3f6998c9ec 100644 --- a/apps/ios/Shared/AppDelegate.swift +++ b/apps/ios/Shared/AppDelegate.swift @@ -9,39 +9,18 @@ import Foundation import UIKit import SimpleXChat +import SwiftUI class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { logger.debug("AppDelegate: didFinishLaunchingWithOptions") application.registerForRemoteNotifications() - if #available(iOS 17.0, *) { trackKeyboard() } - NotificationCenter.default.addObserver(self, selector: #selector(pasteboardChanged), name: UIPasteboard.changedNotification, object: nil) removePasscodesIfReinstalled() + prepareForLaunch() + deleteOldChatArchive() return true } - @available(iOS 17.0, *) - private func trackKeyboard() { - NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil) - } - - @available(iOS 17.0, *) - @objc func keyboardWillShow(_ notification: Notification) { - if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue { - ChatModel.shared.keyboardHeight = keyboardFrame.cgRectValue.height - } - } - - @available(iOS 17.0, *) - @objc func keyboardWillHide(_ notification: Notification) { - ChatModel.shared.keyboardHeight = 0 - } - - @objc func pasteboardChanged() { - ChatModel.shared.pasteboardHasStrings = UIPasteboard.general.hasStrings - } - func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let token = deviceToken.map { String(format: "%02hhx", $0) }.joined() logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)") @@ -75,7 +54,7 @@ class AppDelegate: NSObject, UIApplicationDelegate { try await apiVerifyToken(token: token, nonce: nonce, code: verification) m.tokenStatus = .active } catch { - if let cr = error as? ChatResponse, case .chatCmdError(_, .errorAgent(.NTF(.AUTH))) = cr { + if let cr = error as? ChatError, case .errorAgent(.NTF(.AUTH)) = cr { m.tokenStatus = .expired } logger.error("AppDelegate: didReceiveRemoteNotification: apiVerifyToken or apiIntervalNofication error: \(responseError(error))") @@ -141,6 +120,10 @@ class AppDelegate: NSObject, UIApplicationDelegate { } } + private func prepareForLaunch() { + try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true) + } + static func keepScreenOn(_ on: Bool) { UIApplication.shared.isIdleTimerDisabled = on } @@ -148,13 +131,79 @@ class AppDelegate: NSObject, UIApplicationDelegate { class SceneDelegate: NSObject, ObservableObject, UIWindowSceneDelegate { var window: UIWindow? + static var windowStatic: UIWindow? var windowScene: UIWindowScene? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + UITableView.appearance().backgroundColor = .clear guard let windowScene = scene as? UIWindowScene else { return } self.windowScene = windowScene window = windowScene.keyWindow - window?.tintColor = UIColor(cgColor: getUIAccentColorDefault()) - window?.overrideUserInterfaceStyle = getUserInterfaceStyleDefault() + SceneDelegate.windowStatic = windowScene.keyWindow + migrateAccentColorAndTheme() + ThemeManager.applyTheme(currentThemeDefault.get()) + ThemeManager.adjustWindowStyle() + } + + private func migrateAccentColorAndTheme() { + let defs = UserDefaults.standard + /// For checking migration +// themeOverridesDefault.set([]) +// currentThemeDefault.set(DefaultTheme.SYSTEM_THEME_NAME) +// defs.set(0.5, forKey: DEFAULT_ACCENT_COLOR_RED) +// defs.set(0.3, forKey: DEFAULT_ACCENT_COLOR_GREEN) +// defs.set(0.8, forKey: DEFAULT_ACCENT_COLOR_BLUE) + + let userInterfaceStyle = getUserInterfaceStyleDefault() + if defs.double(forKey: DEFAULT_ACCENT_COLOR_GREEN) == 0 && userInterfaceStyle == .unspecified { + // No migration needed or already migrated + return + } + + let defaultAccentColor = Color(cgColor: CGColor(red: 0.000, green: 0.533, blue: 1.000, alpha: 1)) + let accentColor = Color(cgColor: getUIAccentColorDefault()) + if accentColor != defaultAccentColor { + let colors = ThemeColors(primary: accentColor.toReadableHex()) + var overrides = themeOverridesDefault.get() + var themeIds = currentThemeIdsDefault.get() + switch userInterfaceStyle { + case .light: + let light = ThemeOverrides(base: DefaultTheme.LIGHT, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename)) + overrides.append(light) + themeOverridesDefault.set(overrides) + themeIds[DefaultTheme.LIGHT.themeName] = light.themeId + currentThemeIdsDefault.set(themeIds) + ThemeManager.applyTheme(DefaultTheme.LIGHT.themeName) + case .dark: + let dark = ThemeOverrides(base: DefaultTheme.DARK, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename)) + overrides.append(dark) + themeOverridesDefault.set(overrides) + themeIds[DefaultTheme.DARK.themeName] = dark.themeId + currentThemeIdsDefault.set(themeIds) + ThemeManager.applyTheme(DefaultTheme.DARK.themeName) + case .unspecified: + let light = ThemeOverrides(base: DefaultTheme.LIGHT, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename)) + let dark = ThemeOverrides(base: DefaultTheme.DARK, colors: colors, wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename)) + overrides.append(light) + overrides.append(dark) + themeOverridesDefault.set(overrides) + themeIds[DefaultTheme.LIGHT.themeName] = light.themeId + themeIds[DefaultTheme.DARK.themeName] = dark.themeId + currentThemeIdsDefault.set(themeIds) + ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME) + @unknown default: () + } + } else if userInterfaceStyle != .unspecified { + let themeName = switch userInterfaceStyle { + case .light: DefaultTheme.LIGHT.themeName + case .dark: DefaultTheme.DARK.themeName + default: DefaultTheme.SYSTEM_THEME_NAME + } + ThemeManager.applyTheme(themeName) + } + defs.removeObject(forKey: DEFAULT_ACCENT_COLOR_RED) + defs.removeObject(forKey: DEFAULT_ACCENT_COLOR_GREEN) + defs.removeObject(forKey: DEFAULT_ACCENT_COLOR_BLUE) + defs.removeObject(forKey: DEFAULT_USER_INTERFACE_STYLE) } } diff --git a/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/Contents.json b/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/Contents.json new file mode 100644 index 0000000000..8e38b499dd --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info": { + "author": "xcode", + "version": 1 + }, + "symbols": [ + { + "filename": "checkmark.2.svg", + "idiom": "universal" + } + ] +} \ No newline at end of file diff --git a/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/checkmark.2.svg b/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/checkmark.2.svg new file mode 100644 index 0000000000..577fa1db76 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/checkmark.2.svg @@ -0,0 +1,227 @@ + + + checkmark.2 + + + + + + + Weight/Scale Variations + + + Ultralight + + + Thin + + + Light + + + Regular + + + Medium + + + Semibold + + + Bold + + + Heavy + + + Black + + + + + + + + + + + + + Design Variations + + + Symbols are supported in up to nine weights and three scales. + + + For optimal layout with text and other symbols, vertically align + + + symbols with the adjacent text. + + + + + + + + + Margins + + + Leading and trailing margins on the left and right side of each symbol + + + + can be adjusted by modifying the x-location of the margin guidelines. + + + + Modifications are automatically applied proportionally to all + + + scales and weights. + + + + + + Exporting + + + Symbols should be outlined when exporting to ensure the + + + design is preserved when submitting to Xcode. + + + Template v.5.0 + + + Requires Xcode 15 or greater + + + Generated from double.checkmark + + + Typeset at 100.0 points + + + Small + + + Medium + + + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/Contents.json b/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/Contents.json new file mode 100644 index 0000000000..11a91cb811 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info": { + "author": "xcode", + "version": 1 + }, + "symbols": [ + { + "filename": "checkmark.wide.svg", + "idiom": "universal" + } + ] +} \ No newline at end of file diff --git a/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/checkmark.wide.svg b/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/checkmark.wide.svg new file mode 100644 index 0000000000..b5dfc6b3de --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/checkmark.wide.svg @@ -0,0 +1,218 @@ + + + checkmark.wide + + + + + + + Weight/Scale Variations + + + Ultralight + + + Thin + + + Light + + + Regular + + + Medium + + + Semibold + + + Bold + + + Heavy + + + Black + + + + + + + + + + + + + Design Variations + + + Symbols are supported in up to nine weights and three scales. + + + For optimal layout with text and other symbols, vertically align + + + symbols with the adjacent text. + + + + + + + + + Margins + + + Leading and trailing margins on the left and right side of each symbol + + + + can be adjusted by modifying the x-location of the margin guidelines. + + + + Modifications are automatically applied proportionally to all + + + scales and weights. + + + + + + Exporting + + + Symbols should be outlined when exporting to ensure the + + + design is preserved when submitting to Xcode. + + + Template v.5.0 + + + Requires Xcode 15 or greater + + + Generated from double.checkmark + + + Typeset at 100.0 points + + + Small + + + Medium + + + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/ios/Shared/Assets.xcassets/flux_logo-light.imageset/Contents.json b/apps/ios/Shared/Assets.xcassets/flux_logo-light.imageset/Contents.json new file mode 100644 index 0000000000..d3a15f9a33 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/flux_logo-light.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Flux_logo_blue_white.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/Shared/Assets.xcassets/flux_logo-light.imageset/Flux_logo_blue_white.png b/apps/ios/Shared/Assets.xcassets/flux_logo-light.imageset/Flux_logo_blue_white.png new file mode 100644 index 0000000000..e1d6dda4fe Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/flux_logo-light.imageset/Flux_logo_blue_white.png differ diff --git a/apps/ios/Shared/Assets.xcassets/flux_logo.imageset/Contents.json b/apps/ios/Shared/Assets.xcassets/flux_logo.imageset/Contents.json new file mode 100644 index 0000000000..ad18e60448 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/flux_logo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Flux_logo_blue.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/Shared/Assets.xcassets/flux_logo.imageset/Flux_logo_blue.png b/apps/ios/Shared/Assets.xcassets/flux_logo.imageset/Flux_logo_blue.png new file mode 100644 index 0000000000..87f1373d75 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/flux_logo.imageset/Flux_logo_blue.png differ diff --git a/apps/ios/Shared/Assets.xcassets/flux_logo_symbol.imageset/Contents.json b/apps/ios/Shared/Assets.xcassets/flux_logo_symbol.imageset/Contents.json new file mode 100644 index 0000000000..16686bdf80 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/flux_logo_symbol.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Flux_symbol_blue-white.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/Shared/Assets.xcassets/flux_logo_symbol.imageset/Flux_symbol_blue-white.png b/apps/ios/Shared/Assets.xcassets/flux_logo_symbol.imageset/Flux_symbol_blue-white.png new file mode 100644 index 0000000000..0793b0ee85 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/flux_logo_symbol.imageset/Flux_symbol_blue-white.png differ diff --git a/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/Contents.json b/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/Contents.json new file mode 100644 index 0000000000..cb29f09fe1 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "vertical_logo_x1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "vertical_logo_x2.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "vertical_logo_x3.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/vertical_logo_x1.png b/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/vertical_logo_x1.png new file mode 100644 index 0000000000..f916e43ea9 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/vertical_logo_x1.png differ diff --git a/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/vertical_logo_x2.png b/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/vertical_logo_x2.png new file mode 100644 index 0000000000..bb35878f0c Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/vertical_logo_x2.png differ diff --git a/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/vertical_logo_x3.png b/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/vertical_logo_x3.png new file mode 100644 index 0000000000..c55f481b36 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/vertical_logo.imageset/vertical_logo_x3.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/Contents.json b/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/Contents.json new file mode 100644 index 0000000000..a1747ab6ba --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "wallpaper_cats@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "wallpaper_cats@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "wallpaper_cats@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/wallpaper_cats@1x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/wallpaper_cats@1x.png new file mode 100644 index 0000000000..7d4624c3f9 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/wallpaper_cats@1x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/wallpaper_cats@2x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/wallpaper_cats@2x.png new file mode 100644 index 0000000000..1015139393 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/wallpaper_cats@2x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/wallpaper_cats@3x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/wallpaper_cats@3x.png new file mode 100644 index 0000000000..9bff3eb3d0 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_cats.imageset/wallpaper_cats@3x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/Contents.json b/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/Contents.json new file mode 100644 index 0000000000..c6bc439be2 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "wallpaper_flowers@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "wallpaper_flowers@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "wallpaper_flowers@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/wallpaper_flowers@1x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/wallpaper_flowers@1x.png new file mode 100644 index 0000000000..965f552599 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/wallpaper_flowers@1x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/wallpaper_flowers@2x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/wallpaper_flowers@2x.png new file mode 100644 index 0000000000..0cb219acd3 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/wallpaper_flowers@2x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/wallpaper_flowers@3x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/wallpaper_flowers@3x.png new file mode 100644 index 0000000000..59246eb50d Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_flowers.imageset/wallpaper_flowers@3x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/Contents.json b/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/Contents.json new file mode 100644 index 0000000000..556d01a6f2 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "wallpaper_hearts@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "wallpaper_hearts@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "wallpaper_hearts@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/wallpaper_hearts@1x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/wallpaper_hearts@1x.png new file mode 100644 index 0000000000..780ff13513 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/wallpaper_hearts@1x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/wallpaper_hearts@2x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/wallpaper_hearts@2x.png new file mode 100644 index 0000000000..cee89e57d9 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/wallpaper_hearts@2x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/wallpaper_hearts@3x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/wallpaper_hearts@3x.png new file mode 100644 index 0000000000..35da7c7aed Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_hearts.imageset/wallpaper_hearts@3x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/Contents.json b/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/Contents.json new file mode 100644 index 0000000000..aba5903ec0 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "wallpaper_kids@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "wallpaper_kids@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "wallpaper_kids@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/wallpaper_kids@1x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/wallpaper_kids@1x.png new file mode 100644 index 0000000000..83e48b4f78 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/wallpaper_kids@1x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/wallpaper_kids@2x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/wallpaper_kids@2x.png new file mode 100644 index 0000000000..1927c2fe2a Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/wallpaper_kids@2x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/wallpaper_kids@3x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/wallpaper_kids@3x.png new file mode 100644 index 0000000000..f5f15d3643 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_kids.imageset/wallpaper_kids@3x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/Contents.json b/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/Contents.json new file mode 100644 index 0000000000..59c209b134 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "wallpaper_school@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "wallpaper_school@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "wallpaper_school@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/wallpaper_school@1x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/wallpaper_school@1x.png new file mode 100644 index 0000000000..c95ac60b6e Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/wallpaper_school@1x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/wallpaper_school@2x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/wallpaper_school@2x.png new file mode 100644 index 0000000000..81a3a3d94d Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/wallpaper_school@2x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/wallpaper_school@3x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/wallpaper_school@3x.png new file mode 100644 index 0000000000..f6e1cce383 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_school.imageset/wallpaper_school@3x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/Contents.json b/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/Contents.json new file mode 100644 index 0000000000..4e56988263 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "wallpaper_travel@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "wallpaper_travel@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "wallpaper_travel@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/wallpaper_travel@1x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/wallpaper_travel@1x.png new file mode 100644 index 0000000000..c1d825b86e Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/wallpaper_travel@1x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/wallpaper_travel@2x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/wallpaper_travel@2x.png new file mode 100644 index 0000000000..d640f10c7c Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/wallpaper_travel@2x.png differ diff --git a/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/wallpaper_travel@3x.png b/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/wallpaper_travel@3x.png new file mode 100644 index 0000000000..64ec137331 Binary files /dev/null and b/apps/ios/Shared/Assets.xcassets/wallpaper_travel.imageset/wallpaper_travel@3x.png differ diff --git a/apps/ios/Shared/ContentView.swift b/apps/ios/Shared/ContentView.swift index acea38e69e..2ad8d546f2 100644 --- a/apps/ios/Shared/ContentView.swift +++ b/apps/ios/Shared/ContentView.swift @@ -9,11 +9,24 @@ import SwiftUI import Intents import SimpleXChat +private enum NoticesSheet: Identifiable { + case whatsNew(updatedConditions: Bool) + + var id: String { + switch self { + case .whatsNew: return "whatsNew" + } + } +} + struct ContentView: View { @EnvironmentObject var chatModel: ChatModel @ObservedObject var alertManager = AlertManager.shared @ObservedObject var callController = CallController.shared + @ObservedObject var appSheetState = AppSheetState.shared @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme + @EnvironmentObject var sceneDelegate: SceneDelegate var contentAccessAuthenticationExtended: Bool @@ -27,14 +40,15 @@ struct ContentView: View { @AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false @AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false @AppStorage(DEFAULT_NOTIFICATION_ALERT_SHOWN) private var notificationAlertShown = false - @State private var showSettings = false - @State private var showWhatsNew = false + @State private var noticesShown = false + @State private var noticesSheetItem: NoticesSheet? = nil @State private var showChooseLAMode = false @State private var showSetPasscode = false @State private var waitingForOrPassedAuth = true @State private var chatListActionSheet: ChatListActionSheet? = nil + @State private var chatListUserPickerSheet: UserPickerSheet? = nil - private let callTopPadding: CGFloat = 50 + private let callTopPadding: CGFloat = 40 private enum ChatListActionSheet: Identifiable { case planAndConnectSheet(sheet: PlanAndConnectActionSheet) @@ -51,6 +65,16 @@ struct ContentView: View { } var body: some View { + if #available(iOS 16.0, *) { + allViews() + .scrollContentBackground(.hidden) + } else { + // on iOS 15 scroll view background disabled in SceneDelegate + allViews() + } + } + + func allViews() -> some View { ZStack { let showCallArea = chatModel.activeCall != nil && chatModel.activeCall?.callState != .waitCapabilities && chatModel.activeCall?.callState != .invitationAccepted // contentView() has to be in a single branch, so that enabling authentication doesn't trigger re-rendering and close settings. @@ -74,7 +98,7 @@ struct ContentView: View { callView(call) } - if !showSettings, let la = chatModel.laRequest { + if chatListUserPickerSheet == nil, let la = chatModel.laRequest { LocalAuthView(authRequest: la) .onDisappear { // this flag is separate from accessAuthenticated to show initializationView while we wait for authentication @@ -97,9 +121,6 @@ struct ContentView: View { } } .alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! } - .sheet(isPresented: $showSettings) { - SettingsView(showSettings: $showSettings) - } .confirmationDialog("SimpleX Lock mode", isPresented: $showChooseLAMode, titleVisibility: .visible) { Button("System authentication") { initialEnableLA() } Button("Passcode entry") { showSetPasscode = true } @@ -138,6 +159,17 @@ struct ContentView: View { break } } + .onAppear { + reactOnDarkThemeChanges(systemInDarkThemeCurrently) + } + .onChange(of: colorScheme) { scheme in + // It's needed to update UI colors when iOS wants to make screenshot after going to background, + // so when a user changes his global theme from dark to light or back, the app will adapt to it + reactOnDarkThemeChanges(scheme == .dark) + } + .onChange(of: theme.name) { _ in + ThemeManager.adjustWindowStyle() + } } @ViewBuilder private func contentView() -> some View { @@ -177,14 +209,14 @@ struct ContentView: View { } } - @ViewBuilder private func activeCallInteractiveArea(_ call: Call) -> some View { + private func activeCallInteractiveArea(_ call: Call) -> some View { HStack { Text(call.contact.displayName).font(.body).foregroundColor(.white) Spacer() CallDuration(call: call) } .padding(.horizontal) - .frame(height: callTopPadding - 10) + .frame(height: callTopPadding) .background(Color(uiColor: UIColor(red: 47/255, green: 208/255, blue: 88/255, alpha: 1))) .onTapGesture { chatModel.activeCallViewIsCollapsed = false @@ -224,13 +256,14 @@ struct ContentView: View { .frame(maxWidth: .infinity, maxHeight: .infinity ) .background( Rectangle() - .fill(.background) - ) + .fill(theme.colors.background) + ) } private func mainView() -> some View { ZStack(alignment: .top) { - ChatListView(showSettings: $showSettings).privacySensitive(protectScreen) + ChatListView(activeUserPickerSheet: $chatListUserPickerSheet) + .redacted(reason: appSheetState.redactionReasons(protectScreen)) .onAppear { requestNtfAuthorization() // Local Authentication notice is to be shown on next start after onboarding is complete @@ -239,17 +272,31 @@ struct ContentView: View { alertManager.showAlert(laNoticeAlert()) } else if !chatModel.showCallView && CallController.shared.activeCallInvitation == nil { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - if !showWhatsNew { - showWhatsNew = shouldShowWhatsNew() + if !noticesShown { + let showWhatsNew = shouldShowWhatsNew() + let showUpdatedConditions = chatModel.conditions.conditionsAction?.showNotice ?? false + noticesShown = showWhatsNew || showUpdatedConditions + if showWhatsNew || showUpdatedConditions { + noticesSheetItem = .whatsNew(updatedConditions: showUpdatedConditions) + } } } } prefShowLANotice = true connectViaUrl() + showReRegisterTokenAlert() } .onChange(of: chatModel.appOpenUrl) { _ in connectViaUrl() } - .sheet(isPresented: $showWhatsNew) { - WhatsNewView() + .onChange(of: chatModel.reRegisterTknStatus) { _ in showReRegisterTokenAlert() } + .sheet(item: $noticesSheetItem) { item in + switch item { + case let .whatsNew(updatedConditions): + WhatsNewView(updatedConditions: updatedConditions) + .modifier(ThemedBackground()) + .if(updatedConditions) { v in + v.task { await setConditionsNotified_() } + } + } } if chatModel.setDeliveryReceipts { SetDeliveryReceiptsView() @@ -259,6 +306,21 @@ struct ContentView: View { .onContinueUserActivity("INStartCallIntent", perform: processUserActivity) .onContinueUserActivity("INStartAudioCallIntent", perform: processUserActivity) .onContinueUserActivity("INStartVideoCallIntent", perform: processUserActivity) + .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in + if let url = userActivity.webpageURL { + logger.debug("onContinueUserActivity.NSUserActivityTypeBrowsingWeb: \(url)") + chatModel.appOpenUrl = url + } + } + } + + private func setConditionsNotified_() async { + do { + let conditionsId = ChatModel.shared.conditions.currentConditions.conditionsId + try await setConditionsNotified(conditionsId: conditionsId) + } catch let error { + logger.error("setConditionsNotified error: \(responseError(error))") + } } private func processUserActivity(_ activity: NSUserActivity) { @@ -277,9 +339,18 @@ struct ContentView: View { if let contactId = contacts?.first?.personHandle?.value, let chat = chatModel.getChat(contactId), case let .direct(contact) = chat.chatInfo { - logger.debug("callToRecentContact: schedule call") - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - CallController.shared.startCall(contact, mediaType) + let activeCall = chatModel.activeCall + // This line works when a user clicks on a video button in CallKit UI while in call. + // The app tries to make another call to the same contact and overwite activeCall instance making its state broken + if let activeCall, contactId == activeCall.contact.id, mediaType == .video, !activeCall.hasVideo { + Task { + await chatModel.callCommand.processCommand(.media(source: .camera, enable: true)) + } + } else if activeCall == nil { + logger.debug("callToRecentContact: schedule call") + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + CallController.shared.startCall(contact, mediaType) + } } } } @@ -372,12 +443,12 @@ struct ContentView: View { } func connectViaUrl() { - dismissAllSheets() { - let m = ChatModel.shared - if let url = m.appOpenUrl { - m.appOpenUrl = nil + let m = ChatModel.shared + if let url = m.appOpenUrl { + m.appOpenUrl = nil + dismissAllSheets() { var path = url.path - if (path == "/contact" || path == "/invitation") { + if (path == "/contact" || path == "/invitation" || path == "/a" || path == "/c" || path == "/g" || path == "/i") { path.removeFirst() let link = url.absoluteString.replacingOccurrences(of: "///\(path)", with: "/\(path)") planAndConnect( @@ -394,6 +465,21 @@ struct ContentView: View { } } + func showReRegisterTokenAlert() { + dismissAllSheets() { + let m = ChatModel.shared + if let errorTknStatus = m.reRegisterTknStatus, let token = chatModel.deviceToken { + chatModel.reRegisterTknStatus = nil + AlertManager.shared.showAlert(Alert( + title: Text("Notifications error"), + message: Text(tokenStatusInfo(errorTknStatus, register: true)), + primaryButton: .default(Text("Register")) { reRegisterToken(token: token) }, + secondaryButton: .cancel() + )) + } + } + } + private func showPlanAndConnectAlert(_ alert: PlanAndConnectAlert) { AlertManager.shared.showAlert(planAndConnectAlert(alert, dismiss: false)) } diff --git a/apps/ios/Shared/Model/AppAPITypes.swift b/apps/ios/Shared/Model/AppAPITypes.swift new file mode 100644 index 0000000000..3bf4cb7b56 --- /dev/null +++ b/apps/ios/Shared/Model/AppAPITypes.swift @@ -0,0 +1,2281 @@ +// +// APITypes.swift +// SimpleX +// +// Created by EP on 01/05/2025. +// Copyright © 2025 SimpleX Chat. All rights reserved. +// + +import SimpleXChat +import SwiftUI + +// some constructors are used in SEChatCommand or NSEChatCommand types as well - they must be syncronised +enum ChatCommand: ChatCmdProtocol { + case showActiveUser + case createActiveUser(profile: Profile?, pastTimestamp: Bool) + case listUsers + case apiSetActiveUser(userId: Int64, viewPwd: String?) + case setAllContactReceipts(enable: Bool) + case apiSetUserContactReceipts(userId: Int64, userMsgReceiptSettings: UserMsgReceiptSettings) + case apiSetUserGroupReceipts(userId: Int64, userMsgReceiptSettings: UserMsgReceiptSettings) + case apiHideUser(userId: Int64, viewPwd: String) + case apiUnhideUser(userId: Int64, viewPwd: String) + case apiMuteUser(userId: Int64) + case apiUnmuteUser(userId: Int64) + case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?) + case startChat(mainApp: Bool, enableSndFiles: Bool) + case checkChatRunning + case apiStopChat + case apiActivateChat(restoreChat: Bool) + case apiSuspendChat(timeoutMicroseconds: Int) + case apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) + case apiSetEncryptLocalFiles(enable: Bool) + case apiExportArchive(config: ArchiveConfig) + case apiImportArchive(config: ArchiveConfig) + case apiDeleteStorage + case apiStorageEncryption(config: DBEncryptionConfig) + case testStorageEncryption(key: String) + case apiSaveSettings(settings: AppSettings) + case apiGetSettings(settings: AppSettings) + case apiGetChatTags(userId: Int64) + case apiGetChats(userId: Int64) + case apiGetChat(chatId: ChatId, pagination: ChatPagination, search: String) + case apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) + case apiSendMessages(type: ChatType, id: Int64, live: Bool, ttl: Int?, composedMessages: [ComposedMessage]) + case apiCreateChatTag(tag: ChatTagData) + case apiSetChatTags(type: ChatType, id: Int64, tagIds: [Int64]) + case apiDeleteChatTag(tagId: Int64) + case apiUpdateChatTag(tagId: Int64, tagData: ChatTagData) + case apiReorderChatTags(tagIds: [Int64]) + case apiCreateChatItems(noteFolderId: Int64, composedMessages: [ComposedMessage]) + case apiReportMessage(groupId: Int64, chatItemId: Int64, reportReason: ReportReason, reportText: String) + case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, updatedMessage: UpdatedMessage, live: Bool) + case apiDeleteChatItem(type: ChatType, id: Int64, itemIds: [Int64], mode: CIDeleteMode) + case apiDeleteMemberChatItem(groupId: Int64, itemIds: [Int64]) + case apiArchiveReceivedReports(groupId: Int64) + case apiDeleteReceivedReports(groupId: Int64, itemIds: [Int64], mode: CIDeleteMode) + case apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction) + case apiGetReactionMembers(userId: Int64, groupId: Int64, itemId: Int64, reaction: MsgReaction) + case apiPlanForwardChatItems(toChatType: ChatType, toChatId: Int64, itemIds: [Int64]) + case apiForwardChatItems(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemIds: [Int64], ttl: Int?) + case apiGetNtfToken + case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode) + case apiVerifyToken(token: DeviceToken, nonce: String, code: String) + case apiCheckToken(token: DeviceToken) + case apiDeleteToken(token: DeviceToken) + case apiGetNtfConns(nonce: String, encNtfInfo: String) + case apiGetConnNtfMessages(connMsgReqs: [ConnMsgReq]) + case apiNewGroup(userId: Int64, incognito: Bool, groupProfile: GroupProfile) + case apiAddMember(groupId: Int64, contactId: Int64, memberRole: GroupMemberRole) + case apiJoinGroup(groupId: Int64) + case apiMembersRole(groupId: Int64, memberIds: [Int64], memberRole: GroupMemberRole) + case apiBlockMembersForAll(groupId: Int64, memberIds: [Int64], blocked: Bool) + case apiRemoveMembers(groupId: Int64, memberIds: [Int64], withMessages: Bool) + case apiLeaveGroup(groupId: Int64) + case apiListMembers(groupId: Int64) + case apiUpdateGroupProfile(groupId: Int64, groupProfile: GroupProfile) + case apiCreateGroupLink(groupId: Int64, memberRole: GroupMemberRole, short: Bool) + case apiGroupLinkMemberRole(groupId: Int64, memberRole: GroupMemberRole) + case apiDeleteGroupLink(groupId: Int64) + case apiGetGroupLink(groupId: Int64) + case apiCreateMemberContact(groupId: Int64, groupMemberId: Int64) + case apiSendMemberContactInvitation(contactId: Int64, msg: MsgContent) + case apiTestProtoServer(userId: Int64, server: String) + case apiGetServerOperators + case apiSetServerOperators(operators: [ServerOperator]) + case apiGetUserServers(userId: Int64) + case apiSetUserServers(userId: Int64, userServers: [UserOperatorServers]) + case apiValidateServers(userId: Int64, userServers: [UserOperatorServers]) + case apiGetUsageConditions + case apiSetConditionsNotified(conditionsId: Int64) + case apiAcceptConditions(conditionsId: Int64, operatorIds: [Int64]) + case apiSetChatItemTTL(userId: Int64, seconds: Int64) + case apiGetChatItemTTL(userId: Int64) + case apiSetChatTTL(userId: Int64, type: ChatType, id: Int64, seconds: Int64?) + case apiSetNetworkConfig(networkConfig: NetCfg) + case apiGetNetworkConfig + case apiSetNetworkInfo(networkInfo: UserNetworkInfo) + case reconnectAllServers + case reconnectServer(userId: Int64, smpServer: String) + case apiSetChatSettings(type: ChatType, id: Int64, chatSettings: ChatSettings) + case apiSetMemberSettings(groupId: Int64, groupMemberId: Int64, memberSettings: GroupMemberSettings) + case apiContactInfo(contactId: Int64) + case apiGroupMemberInfo(groupId: Int64, groupMemberId: Int64) + case apiContactQueueInfo(contactId: Int64) + case apiGroupMemberQueueInfo(groupId: Int64, groupMemberId: Int64) + case apiSwitchContact(contactId: Int64) + case apiSwitchGroupMember(groupId: Int64, groupMemberId: Int64) + case apiAbortSwitchContact(contactId: Int64) + case apiAbortSwitchGroupMember(groupId: Int64, groupMemberId: Int64) + case apiSyncContactRatchet(contactId: Int64, force: Bool) + case apiSyncGroupMemberRatchet(groupId: Int64, groupMemberId: Int64, force: Bool) + case apiGetContactCode(contactId: Int64) + case apiGetGroupMemberCode(groupId: Int64, groupMemberId: Int64) + case apiVerifyContact(contactId: Int64, connectionCode: String?) + case apiVerifyGroupMember(groupId: Int64, groupMemberId: Int64, connectionCode: String?) + case apiAddContact(userId: Int64, short: Bool, incognito: Bool) + case apiSetConnectionIncognito(connId: Int64, incognito: Bool) + case apiChangeConnectionUser(connId: Int64, userId: Int64) + case apiConnectPlan(userId: Int64, connLink: String) + case apiConnect(userId: Int64, incognito: Bool, connLink: CreatedConnLink) + case apiConnectContactViaAddress(userId: Int64, incognito: Bool, contactId: Int64) + case apiDeleteChat(type: ChatType, id: Int64, chatDeleteMode: ChatDeleteMode) + case apiClearChat(type: ChatType, id: Int64) + case apiListContacts(userId: Int64) + case apiUpdateProfile(userId: Int64, profile: Profile) + case apiSetContactPrefs(contactId: Int64, preferences: Preferences) + case apiSetContactAlias(contactId: Int64, localAlias: String) + case apiSetGroupAlias(groupId: Int64, localAlias: String) + case apiSetConnectionAlias(connId: Int64, localAlias: String) + case apiSetUserUIThemes(userId: Int64, themes: ThemeModeOverrides?) + case apiSetChatUIThemes(chatId: String, themes: ThemeModeOverrides?) + case apiCreateMyAddress(userId: Int64, short: Bool) + case apiDeleteMyAddress(userId: Int64) + case apiShowMyAddress(userId: Int64) + case apiSetProfileAddress(userId: Int64, on: Bool) + case apiAddressAutoAccept(userId: Int64, autoAccept: AutoAccept?) + case apiAcceptContact(incognito: Bool, contactReqId: Int64) + case apiRejectContact(contactReqId: Int64) + // WebRTC calls + case apiSendCallInvitation(contact: Contact, callType: CallType) + case apiRejectCall(contact: Contact) + case apiSendCallOffer(contact: Contact, callOffer: WebRTCCallOffer) + case apiSendCallAnswer(contact: Contact, answer: WebRTCSession) + case apiSendCallExtraInfo(contact: Contact, extraInfo: WebRTCExtraInfo) + case apiEndCall(contact: Contact) + case apiGetCallInvitations + case apiCallStatus(contact: Contact, callStatus: WebRTCCallStatus) + // WebRTC calls / + case apiGetNetworkStatuses + case apiChatRead(type: ChatType, id: Int64) + case apiChatItemsRead(type: ChatType, id: Int64, itemIds: [Int64]) + case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) + case receiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?, inline: Bool?) + case setFileToReceive(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?) + case cancelFile(fileId: Int64) + // remote desktop commands + case setLocalDeviceName(displayName: String) + case connectRemoteCtrl(xrcpInvitation: String) + case findKnownRemoteCtrl + case confirmRemoteCtrl(remoteCtrlId: Int64) + case verifyRemoteCtrlSession(sessionCode: String) + case listRemoteCtrls + case stopRemoteCtrl + case deleteRemoteCtrl(remoteCtrlId: Int64) + case apiUploadStandaloneFile(userId: Int64, file: CryptoFile) + case apiDownloadStandaloneFile(userId: Int64, url: String, file: CryptoFile) + case apiStandaloneFileInfo(url: String) + // misc + case showVersion + case getAgentSubsTotal(userId: Int64) + case getAgentServersSummary(userId: Int64) + case resetAgentServersStats + case string(String) + + var cmdString: String { + get { + switch self { + case .showActiveUser: return "/u" + case let .createActiveUser(profile, pastTimestamp): + let user = NewUser(profile: profile, pastTimestamp: pastTimestamp) + return "/_create user \(encodeJSON(user))" + case .listUsers: return "/users" + case let .apiSetActiveUser(userId, viewPwd): return "/_user \(userId)\(maybePwd(viewPwd))" + case let .setAllContactReceipts(enable): return "/set receipts all \(onOff(enable))" + case let .apiSetUserContactReceipts(userId, userMsgReceiptSettings): + let umrs = userMsgReceiptSettings + return "/_set receipts contacts \(userId) \(onOff(umrs.enable)) clear_overrides=\(onOff(umrs.clearOverrides))" + case let .apiSetUserGroupReceipts(userId, userMsgReceiptSettings): + let umrs = userMsgReceiptSettings + return "/_set receipts groups \(userId) \(onOff(umrs.enable)) clear_overrides=\(onOff(umrs.clearOverrides))" + case let .apiHideUser(userId, viewPwd): return "/_hide user \(userId) \(encodeJSON(viewPwd))" + case let .apiUnhideUser(userId, viewPwd): return "/_unhide user \(userId) \(encodeJSON(viewPwd))" + case let .apiMuteUser(userId): return "/_mute user \(userId)" + case let .apiUnmuteUser(userId): return "/_unmute user \(userId)" + case let .apiDeleteUser(userId, delSMPQueues, viewPwd): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))\(maybePwd(viewPwd))" + case let .startChat(mainApp, enableSndFiles): return "/_start main=\(onOff(mainApp)) snd_files=\(onOff(enableSndFiles))" + case .checkChatRunning: return "/_check running" + case .apiStopChat: return "/_stop" + case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))" + case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)" + case let .apiSetAppFilePaths(filesFolder, tempFolder, assetsFolder): return "/set file paths \(encodeJSON(AppFilePaths(appFilesFolder: filesFolder, appTempFolder: tempFolder, appAssetsFolder: assetsFolder)))" + case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))" + case let .apiExportArchive(cfg): return "/_db export \(encodeJSON(cfg))" + case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))" + case .apiDeleteStorage: return "/_db delete" + case let .apiStorageEncryption(cfg): return "/_db encryption \(encodeJSON(cfg))" + case let .testStorageEncryption(key): return "/db test key \(key)" + case let .apiSaveSettings(settings): return "/_save app settings \(encodeJSON(settings))" + case let .apiGetSettings(settings): return "/_get app settings \(encodeJSON(settings))" + case let .apiGetChatTags(userId): return "/_get tags \(userId)" + case let .apiGetChats(userId): return "/_get chats \(userId) pcc=on" + case let .apiGetChat(chatId, pagination, search): return "/_get chat \(chatId) \(pagination.cmdString)" + + (search == "" ? "" : " search=\(search)") + case let .apiGetChatItemInfo(type, id, itemId): return "/_get item info \(ref(type, id)) \(itemId)" + case let .apiSendMessages(type, id, live, ttl, composedMessages): + let msgs = encodeJSON(composedMessages) + let ttlStr = ttl != nil ? "\(ttl!)" : "default" + return "/_send \(ref(type, id)) live=\(onOff(live)) ttl=\(ttlStr) json \(msgs)" + case let .apiCreateChatTag(tag): return "/_create tag \(encodeJSON(tag))" + case let .apiSetChatTags(type, id, tagIds): return "/_tags \(ref(type, id)) \(tagIds.map({ "\($0)" }).joined(separator: ","))" + case let .apiDeleteChatTag(tagId): return "/_delete tag \(tagId)" + case let .apiUpdateChatTag(tagId, tagData): return "/_update tag \(tagId) \(encodeJSON(tagData))" + case let .apiReorderChatTags(tagIds): return "/_reorder tags \(tagIds.map({ "\($0)" }).joined(separator: ","))" + case let .apiCreateChatItems(noteFolderId, composedMessages): + let msgs = encodeJSON(composedMessages) + return "/_create *\(noteFolderId) json \(msgs)" + case let .apiReportMessage(groupId, chatItemId, reportReason, reportText): + return "/_report #\(groupId) \(chatItemId) reason=\(reportReason) \(reportText)" + case let .apiUpdateChatItem(type, id, itemId, um, live): return "/_update item \(ref(type, id)) \(itemId) live=\(onOff(live)) \(um.cmdString)" + case let .apiDeleteChatItem(type, id, itemIds, mode): return "/_delete item \(ref(type, id)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) \(mode.rawValue)" + case let .apiDeleteMemberChatItem(groupId, itemIds): return "/_delete member item #\(groupId) \(itemIds.map({ "\($0)" }).joined(separator: ","))" + case let .apiArchiveReceivedReports(groupId): return "/_archive reports #\(groupId)" + case let .apiDeleteReceivedReports(groupId, itemIds, mode): return "/_delete reports #\(groupId) \(itemIds.map({ "\($0)" }).joined(separator: ",")) \(mode.rawValue)" + case let .apiChatItemReaction(type, id, itemId, add, reaction): return "/_reaction \(ref(type, id)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))" + case let .apiGetReactionMembers(userId, groupId, itemId, reaction): return "/_reaction members \(userId) #\(groupId) \(itemId) \(encodeJSON(reaction))" + case let .apiPlanForwardChatItems(type, id, itemIds): return "/_forward plan \(ref(type, id)) \(itemIds.map({ "\($0)" }).joined(separator: ","))" + case let .apiForwardChatItems(toChatType, toChatId, fromChatType, fromChatId, itemIds, ttl): + let ttlStr = ttl != nil ? "\(ttl!)" : "default" + return "/_forward \(ref(toChatType, toChatId)) \(ref(fromChatType, fromChatId)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) ttl=\(ttlStr)" + case .apiGetNtfToken: return "/_ntf get " + case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)" + case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)" + case let .apiCheckToken(token): return "/_ntf check \(token.cmdString)" + case let .apiDeleteToken(token): return "/_ntf delete \(token.cmdString)" + case let .apiGetNtfConns(nonce, encNtfInfo): return "/_ntf conns \(nonce) \(encNtfInfo)" + case let .apiGetConnNtfMessages(connMsgReqs): return "/_ntf conn messages \(connMsgReqs.map { $0.cmdString }.joined(separator: ","))" + case let .apiNewGroup(userId, incognito, groupProfile): return "/_group \(userId) incognito=\(onOff(incognito)) \(encodeJSON(groupProfile))" + case let .apiAddMember(groupId, contactId, memberRole): return "/_add #\(groupId) \(contactId) \(memberRole)" + case let .apiJoinGroup(groupId): return "/_join #\(groupId)" + case let .apiMembersRole(groupId, memberIds, memberRole): return "/_member role #\(groupId) \(memberIds.map({ "\($0)" }).joined(separator: ",")) \(memberRole.rawValue)" + case let .apiBlockMembersForAll(groupId, memberIds, blocked): return "/_block #\(groupId) \(memberIds.map({ "\($0)" }).joined(separator: ",")) blocked=\(onOff(blocked))" + case let .apiRemoveMembers(groupId, memberIds, withMessages): return "/_remove #\(groupId) \(memberIds.map({ "\($0)" }).joined(separator: ",")) messages=\(onOff(withMessages))" + case let .apiLeaveGroup(groupId): return "/_leave #\(groupId)" + case let .apiListMembers(groupId): return "/_members #\(groupId)" + case let .apiUpdateGroupProfile(groupId, groupProfile): return "/_group_profile #\(groupId) \(encodeJSON(groupProfile))" + case let .apiCreateGroupLink(groupId, memberRole, short): return "/_create link #\(groupId) \(memberRole) short=\(onOff(short))" + case let .apiGroupLinkMemberRole(groupId, memberRole): return "/_set link role #\(groupId) \(memberRole)" + case let .apiDeleteGroupLink(groupId): return "/_delete link #\(groupId)" + case let .apiGetGroupLink(groupId): return "/_get link #\(groupId)" + case let .apiCreateMemberContact(groupId, groupMemberId): return "/_create member contact #\(groupId) \(groupMemberId)" + case let .apiSendMemberContactInvitation(contactId, mc): return "/_invite member contact @\(contactId) \(mc.cmdString)" + case let .apiTestProtoServer(userId, server): return "/_server test \(userId) \(server)" + case .apiGetServerOperators: return "/_operators" + case let .apiSetServerOperators(operators): return "/_operators \(encodeJSON(operators))" + case let .apiGetUserServers(userId): return "/_servers \(userId)" + case let .apiSetUserServers(userId, userServers): return "/_servers \(userId) \(encodeJSON(userServers))" + case let .apiValidateServers(userId, userServers): return "/_validate_servers \(userId) \(encodeJSON(userServers))" + case .apiGetUsageConditions: return "/_conditions" + case let .apiSetConditionsNotified(conditionsId): return "/_conditions_notified \(conditionsId)" + case let .apiAcceptConditions(conditionsId, operatorIds): return "/_accept_conditions \(conditionsId) \(joinedIds(operatorIds))" + case let .apiSetChatItemTTL(userId, seconds): return "/_ttl \(userId) \(chatItemTTLStr(seconds: seconds))" + case let .apiGetChatItemTTL(userId): return "/_ttl \(userId)" + case let .apiSetChatTTL(userId, type, id, seconds): return "/_ttl \(userId) \(ref(type, id)) \(chatItemTTLStr(seconds: seconds))" + case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))" + case .apiGetNetworkConfig: return "/network" + case let .apiSetNetworkInfo(networkInfo): return "/_network info \(encodeJSON(networkInfo))" + case .reconnectAllServers: return "/reconnect" + case let .reconnectServer(userId, smpServer): return "/reconnect \(userId) \(smpServer)" + case let .apiSetChatSettings(type, id, chatSettings): return "/_settings \(ref(type, id)) \(encodeJSON(chatSettings))" + case let .apiSetMemberSettings(groupId, groupMemberId, memberSettings): return "/_member settings #\(groupId) \(groupMemberId) \(encodeJSON(memberSettings))" + case let .apiContactInfo(contactId): return "/_info @\(contactId)" + case let .apiGroupMemberInfo(groupId, groupMemberId): return "/_info #\(groupId) \(groupMemberId)" + case let .apiContactQueueInfo(contactId): return "/_queue info @\(contactId)" + case let .apiGroupMemberQueueInfo(groupId, groupMemberId): return "/_queue info #\(groupId) \(groupMemberId)" + case let .apiSwitchContact(contactId): return "/_switch @\(contactId)" + case let .apiSwitchGroupMember(groupId, groupMemberId): return "/_switch #\(groupId) \(groupMemberId)" + case let .apiAbortSwitchContact(contactId): return "/_abort switch @\(contactId)" + case let .apiAbortSwitchGroupMember(groupId, groupMemberId): return "/_abort switch #\(groupId) \(groupMemberId)" + case let .apiSyncContactRatchet(contactId, force): if force { + return "/_sync @\(contactId) force=on" + } else { + return "/_sync @\(contactId)" + } + case let .apiSyncGroupMemberRatchet(groupId, groupMemberId, force): if force { + return "/_sync #\(groupId) \(groupMemberId) force=on" + } else { + return "/_sync #\(groupId) \(groupMemberId)" + } + case let .apiGetContactCode(contactId): return "/_get code @\(contactId)" + case let .apiGetGroupMemberCode(groupId, groupMemberId): return "/_get code #\(groupId) \(groupMemberId)" + case let .apiVerifyContact(contactId, .some(connectionCode)): return "/_verify code @\(contactId) \(connectionCode)" + case let .apiVerifyContact(contactId, .none): return "/_verify code @\(contactId)" + case let .apiVerifyGroupMember(groupId, groupMemberId, .some(connectionCode)): return "/_verify code #\(groupId) \(groupMemberId) \(connectionCode)" + case let .apiVerifyGroupMember(groupId, groupMemberId, .none): return "/_verify code #\(groupId) \(groupMemberId)" + case let .apiAddContact(userId, short, incognito): return "/_connect \(userId) short=\(onOff(short)) incognito=\(onOff(incognito))" + case let .apiSetConnectionIncognito(connId, incognito): return "/_set incognito :\(connId) \(onOff(incognito))" + case let .apiChangeConnectionUser(connId, userId): return "/_set conn user :\(connId) \(userId)" + case let .apiConnectPlan(userId, connLink): return "/_connect plan \(userId) \(connLink)" + case let .apiConnect(userId, incognito, connLink): return "/_connect \(userId) incognito=\(onOff(incognito)) \(connLink.connFullLink) \(connLink.connShortLink ?? "")" + case let .apiConnectContactViaAddress(userId, incognito, contactId): return "/_connect contact \(userId) incognito=\(onOff(incognito)) \(contactId)" + case let .apiDeleteChat(type, id, chatDeleteMode): return "/_delete \(ref(type, id)) \(chatDeleteMode.cmdString)" + case let .apiClearChat(type, id): return "/_clear chat \(ref(type, id))" + case let .apiListContacts(userId): return "/_contacts \(userId)" + case let .apiUpdateProfile(userId, profile): return "/_profile \(userId) \(encodeJSON(profile))" + case let .apiSetContactPrefs(contactId, preferences): return "/_set prefs @\(contactId) \(encodeJSON(preferences))" + case let .apiSetContactAlias(contactId, localAlias): return "/_set alias @\(contactId) \(localAlias.trimmingCharacters(in: .whitespaces))" + case let .apiSetGroupAlias(groupId, localAlias): return "/_set alias #\(groupId) \(localAlias.trimmingCharacters(in: .whitespaces))" + case let .apiSetConnectionAlias(connId, localAlias): return "/_set alias :\(connId) \(localAlias.trimmingCharacters(in: .whitespaces))" + case let .apiSetUserUIThemes(userId, themes): return "/_set theme user \(userId) \(themes != nil ? encodeJSON(themes) : "")" + case let .apiSetChatUIThemes(chatId, themes): return "/_set theme \(chatId) \(themes != nil ? encodeJSON(themes) : "")" + case let .apiCreateMyAddress(userId, short): return "/_address \(userId) short=\(onOff(short))" + case let .apiDeleteMyAddress(userId): return "/_delete_address \(userId)" + case let .apiShowMyAddress(userId): return "/_show_address \(userId)" + case let .apiSetProfileAddress(userId, on): return "/_profile_address \(userId) \(onOff(on))" + case let .apiAddressAutoAccept(userId, autoAccept): return "/_auto_accept \(userId) \(AutoAccept.cmdString(autoAccept))" + case let .apiAcceptContact(incognito, contactReqId): return "/_accept incognito=\(onOff(incognito)) \(contactReqId)" + case let .apiRejectContact(contactReqId): return "/_reject \(contactReqId)" + case let .apiSendCallInvitation(contact, callType): return "/_call invite @\(contact.apiId) \(encodeJSON(callType))" + case let .apiRejectCall(contact): return "/_call reject @\(contact.apiId)" + case let .apiSendCallOffer(contact, callOffer): return "/_call offer @\(contact.apiId) \(encodeJSON(callOffer))" + case let .apiSendCallAnswer(contact, answer): return "/_call answer @\(contact.apiId) \(encodeJSON(answer))" + case let .apiSendCallExtraInfo(contact, extraInfo): return "/_call extra @\(contact.apiId) \(encodeJSON(extraInfo))" + case let .apiEndCall(contact): return "/_call end @\(contact.apiId)" + case .apiGetCallInvitations: return "/_call get" + case let .apiCallStatus(contact, callStatus): return "/_call status @\(contact.apiId) \(callStatus.rawValue)" + case .apiGetNetworkStatuses: return "/_network_statuses" + case let .apiChatRead(type, id): return "/_read chat \(ref(type, id))" + case let .apiChatItemsRead(type, id, itemIds): return "/_read chat items \(ref(type, id)) \(joinedIds(itemIds))" + case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))" + case let .receiveFile(fileId, userApprovedRelays, encrypt, inline): return "/freceive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))" + case let .setFileToReceive(fileId, userApprovedRelays, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))" + case let .cancelFile(fileId): return "/fcancel \(fileId)" + case let .setLocalDeviceName(displayName): return "/set device name \(displayName)" + case let .connectRemoteCtrl(xrcpInv): return "/connect remote ctrl \(xrcpInv)" + case .findKnownRemoteCtrl: return "/find remote ctrl" + case let .confirmRemoteCtrl(rcId): return "/confirm remote ctrl \(rcId)" + case let .verifyRemoteCtrlSession(sessCode): return "/verify remote ctrl \(sessCode)" + case .listRemoteCtrls: return "/list remote ctrls" + case .stopRemoteCtrl: return "/stop remote ctrl" + case let .deleteRemoteCtrl(rcId): return "/delete remote ctrl \(rcId)" + case let .apiUploadStandaloneFile(userId, file): return "/_upload \(userId) \(file.filePath)" + case let .apiDownloadStandaloneFile(userId, link, file): return "/_download \(userId) \(link) \(file.filePath)" + case let .apiStandaloneFileInfo(link): return "/_download info \(link)" + case .showVersion: return "/version" + case let .getAgentSubsTotal(userId): return "/get subs total \(userId)" + case let .getAgentServersSummary(userId): return "/get servers summary \(userId)" + case .resetAgentServersStats: return "/reset servers stats" + case let .string(str): return str + } + } + } + + var cmdType: String { + get { + switch self { + case .showActiveUser: return "showActiveUser" + case .createActiveUser: return "createActiveUser" + case .listUsers: return "listUsers" + case .apiSetActiveUser: return "apiSetActiveUser" + case .setAllContactReceipts: return "setAllContactReceipts" + case .apiSetUserContactReceipts: return "apiSetUserContactReceipts" + case .apiSetUserGroupReceipts: return "apiSetUserGroupReceipts" + case .apiHideUser: return "apiHideUser" + case .apiUnhideUser: return "apiUnhideUser" + case .apiMuteUser: return "apiMuteUser" + case .apiUnmuteUser: return "apiUnmuteUser" + case .apiDeleteUser: return "apiDeleteUser" + case .startChat: return "startChat" + case .checkChatRunning: return "checkChatRunning" + case .apiStopChat: return "apiStopChat" + case .apiActivateChat: return "apiActivateChat" + case .apiSuspendChat: return "apiSuspendChat" + case .apiSetAppFilePaths: return "apiSetAppFilePaths" + case .apiSetEncryptLocalFiles: return "apiSetEncryptLocalFiles" + case .apiExportArchive: return "apiExportArchive" + case .apiImportArchive: return "apiImportArchive" + case .apiDeleteStorage: return "apiDeleteStorage" + case .apiStorageEncryption: return "apiStorageEncryption" + case .testStorageEncryption: return "testStorageEncryption" + case .apiSaveSettings: return "apiSaveSettings" + case .apiGetSettings: return "apiGetSettings" + case .apiGetChatTags: return "apiGetChatTags" + case .apiGetChats: return "apiGetChats" + case .apiGetChat: return "apiGetChat" + case .apiGetChatItemInfo: return "apiGetChatItemInfo" + case .apiSendMessages: return "apiSendMessages" + case .apiCreateChatTag: return "apiCreateChatTag" + case .apiSetChatTags: return "apiSetChatTags" + case .apiDeleteChatTag: return "apiDeleteChatTag" + case .apiUpdateChatTag: return "apiUpdateChatTag" + case .apiReorderChatTags: return "apiReorderChatTags" + case .apiCreateChatItems: return "apiCreateChatItems" + case .apiReportMessage: return "apiReportMessage" + case .apiUpdateChatItem: return "apiUpdateChatItem" + case .apiDeleteChatItem: return "apiDeleteChatItem" + case .apiConnectContactViaAddress: return "apiConnectContactViaAddress" + case .apiDeleteMemberChatItem: return "apiDeleteMemberChatItem" + case .apiArchiveReceivedReports: return "apiArchiveReceivedReports" + case .apiDeleteReceivedReports: return "apiDeleteReceivedReports" + case .apiChatItemReaction: return "apiChatItemReaction" + case .apiGetReactionMembers: return "apiGetReactionMembers" + case .apiPlanForwardChatItems: return "apiPlanForwardChatItems" + case .apiForwardChatItems: return "apiForwardChatItems" + case .apiGetNtfToken: return "apiGetNtfToken" + case .apiRegisterToken: return "apiRegisterToken" + case .apiVerifyToken: return "apiVerifyToken" + case .apiCheckToken: return "apiCheckToken" + case .apiDeleteToken: return "apiDeleteToken" + case .apiGetNtfConns: return "apiGetNtfConns" + case .apiGetConnNtfMessages: return "apiGetConnNtfMessages" + case .apiNewGroup: return "apiNewGroup" + case .apiAddMember: return "apiAddMember" + case .apiJoinGroup: return "apiJoinGroup" + case .apiMembersRole: return "apiMembersRole" + case .apiBlockMembersForAll: return "apiBlockMembersForAll" + case .apiRemoveMembers: return "apiRemoveMembers" + case .apiLeaveGroup: return "apiLeaveGroup" + case .apiListMembers: return "apiListMembers" + case .apiUpdateGroupProfile: return "apiUpdateGroupProfile" + case .apiCreateGroupLink: return "apiCreateGroupLink" + case .apiGroupLinkMemberRole: return "apiGroupLinkMemberRole" + case .apiDeleteGroupLink: return "apiDeleteGroupLink" + case .apiGetGroupLink: return "apiGetGroupLink" + case .apiCreateMemberContact: return "apiCreateMemberContact" + case .apiSendMemberContactInvitation: return "apiSendMemberContactInvitation" + case .apiTestProtoServer: return "apiTestProtoServer" + case .apiGetServerOperators: return "apiGetServerOperators" + case .apiSetServerOperators: return "apiSetServerOperators" + case .apiGetUserServers: return "apiGetUserServers" + case .apiSetUserServers: return "apiSetUserServers" + case .apiValidateServers: return "apiValidateServers" + case .apiGetUsageConditions: return "apiGetUsageConditions" + case .apiSetConditionsNotified: return "apiSetConditionsNotified" + case .apiAcceptConditions: return "apiAcceptConditions" + case .apiSetChatItemTTL: return "apiSetChatItemTTL" + case .apiGetChatItemTTL: return "apiGetChatItemTTL" + case .apiSetChatTTL: return "apiSetChatTTL" + case .apiSetNetworkConfig: return "apiSetNetworkConfig" + case .apiGetNetworkConfig: return "apiGetNetworkConfig" + case .apiSetNetworkInfo: return "apiSetNetworkInfo" + case .reconnectAllServers: return "reconnectAllServers" + case .reconnectServer: return "reconnectServer" + case .apiSetChatSettings: return "apiSetChatSettings" + case .apiSetMemberSettings: return "apiSetMemberSettings" + case .apiContactInfo: return "apiContactInfo" + case .apiGroupMemberInfo: return "apiGroupMemberInfo" + case .apiContactQueueInfo: return "apiContactQueueInfo" + case .apiGroupMemberQueueInfo: return "apiGroupMemberQueueInfo" + case .apiSwitchContact: return "apiSwitchContact" + case .apiSwitchGroupMember: return "apiSwitchGroupMember" + case .apiAbortSwitchContact: return "apiAbortSwitchContact" + case .apiAbortSwitchGroupMember: return "apiAbortSwitchGroupMember" + case .apiSyncContactRatchet: return "apiSyncContactRatchet" + case .apiSyncGroupMemberRatchet: return "apiSyncGroupMemberRatchet" + case .apiGetContactCode: return "apiGetContactCode" + case .apiGetGroupMemberCode: return "apiGetGroupMemberCode" + case .apiVerifyContact: return "apiVerifyContact" + case .apiVerifyGroupMember: return "apiVerifyGroupMember" + case .apiAddContact: return "apiAddContact" + case .apiSetConnectionIncognito: return "apiSetConnectionIncognito" + case .apiChangeConnectionUser: return "apiChangeConnectionUser" + case .apiConnectPlan: return "apiConnectPlan" + case .apiConnect: return "apiConnect" + case .apiDeleteChat: return "apiDeleteChat" + case .apiClearChat: return "apiClearChat" + case .apiListContacts: return "apiListContacts" + case .apiUpdateProfile: return "apiUpdateProfile" + case .apiSetContactPrefs: return "apiSetContactPrefs" + case .apiSetContactAlias: return "apiSetContactAlias" + case .apiSetGroupAlias: return "apiSetGroupAlias" + case .apiSetConnectionAlias: return "apiSetConnectionAlias" + case .apiSetUserUIThemes: return "apiSetUserUIThemes" + case .apiSetChatUIThemes: return "apiSetChatUIThemes" + case .apiCreateMyAddress: return "apiCreateMyAddress" + case .apiDeleteMyAddress: return "apiDeleteMyAddress" + case .apiShowMyAddress: return "apiShowMyAddress" + case .apiSetProfileAddress: return "apiSetProfileAddress" + case .apiAddressAutoAccept: return "apiAddressAutoAccept" + case .apiAcceptContact: return "apiAcceptContact" + case .apiRejectContact: return "apiRejectContact" + case .apiSendCallInvitation: return "apiSendCallInvitation" + case .apiRejectCall: return "apiRejectCall" + case .apiSendCallOffer: return "apiSendCallOffer" + case .apiSendCallAnswer: return "apiSendCallAnswer" + case .apiSendCallExtraInfo: return "apiSendCallExtraInfo" + case .apiEndCall: return "apiEndCall" + case .apiGetCallInvitations: return "apiGetCallInvitations" + case .apiCallStatus: return "apiCallStatus" + case .apiGetNetworkStatuses: return "apiGetNetworkStatuses" + case .apiChatRead: return "apiChatRead" + case .apiChatItemsRead: return "apiChatItemsRead" + case .apiChatUnread: return "apiChatUnread" + case .receiveFile: return "receiveFile" + case .setFileToReceive: return "setFileToReceive" + case .cancelFile: return "cancelFile" + case .setLocalDeviceName: return "setLocalDeviceName" + case .connectRemoteCtrl: return "connectRemoteCtrl" + case .findKnownRemoteCtrl: return "findKnownRemoteCtrl" + case .confirmRemoteCtrl: return "confirmRemoteCtrl" + case .verifyRemoteCtrlSession: return "verifyRemoteCtrlSession" + case .listRemoteCtrls: return "listRemoteCtrls" + case .stopRemoteCtrl: return "stopRemoteCtrl" + case .deleteRemoteCtrl: return "deleteRemoteCtrl" + case .apiUploadStandaloneFile: return "apiUploadStandaloneFile" + case .apiDownloadStandaloneFile: return "apiDownloadStandaloneFile" + case .apiStandaloneFileInfo: return "apiStandaloneFileInfo" + case .showVersion: return "showVersion" + case .getAgentSubsTotal: return "getAgentSubsTotal" + case .getAgentServersSummary: return "getAgentServersSummary" + case .resetAgentServersStats: return "resetAgentServersStats" + case .string: return "console command" + } + } + } + + func ref(_ type: ChatType, _ id: Int64) -> String { + "\(type.rawValue)\(id)" + } + + func joinedIds(_ ids: [Int64]) -> String { + ids.map { "\($0)" }.joined(separator: ",") + } + + func chatItemTTLStr(seconds: Int64?) -> String { + if let seconds = seconds { + return String(seconds) + } else { + return "default" + } + } + + var obfuscated: ChatCommand { + switch self { + case let .apiStorageEncryption(cfg): + return .apiStorageEncryption(config: DBEncryptionConfig(currentKey: obfuscate(cfg.currentKey), newKey: obfuscate(cfg.newKey))) + case let .apiSetActiveUser(userId, viewPwd): + return .apiSetActiveUser(userId: userId, viewPwd: obfuscate(viewPwd)) + case let .apiHideUser(userId, viewPwd): + return .apiHideUser(userId: userId, viewPwd: obfuscate(viewPwd)) + case let .apiUnhideUser(userId, viewPwd): + return .apiUnhideUser(userId: userId, viewPwd: obfuscate(viewPwd)) + case let .apiDeleteUser(userId, delSMPQueues, viewPwd): + return .apiDeleteUser(userId: userId, delSMPQueues: delSMPQueues, viewPwd: obfuscate(viewPwd)) + case let .testStorageEncryption(key): + return .testStorageEncryption(key: obfuscate(key)) + default: return self + } + } + + private func obfuscate(_ s: String) -> String { + s == "" ? "" : "***" + } + + private func obfuscate(_ s: String?) -> String? { + if let s = s { + return obfuscate(s) + } + return nil + } + + private func onOffParam(_ param: String, _ b: Bool?) -> String { + if let b = b { + return " \(param)=\(onOff(b))" + } + return "" + } + + private func maybePwd(_ pwd: String?) -> String { + pwd == "" || pwd == nil ? "" : " " + encodeJSON(pwd) + } +} + +// ChatResponse is split to three enums to reduce stack size used when parsing it, parsing large enums is very inefficient. +enum ChatResponse0: Decodable, ChatAPIResult { + case activeUser(user: User) + case usersList(users: [UserInfo]) + case chatStarted + case chatRunning + case chatStopped + case apiChats(user: UserRef, chats: [ChatData]) + case apiChat(user: UserRef, chat: ChatData, navInfo: NavigationInfo?) + case chatTags(user: UserRef, userTags: [ChatTag]) + case chatItemInfo(user: UserRef, chatItem: AChatItem, chatItemInfo: ChatItemInfo) + case serverTestResult(user: UserRef, testServer: String, testFailure: ProtocolTestFailure?) + case serverOperatorConditions(conditions: ServerOperatorConditions) + case userServers(user: UserRef, userServers: [UserOperatorServers]) + case userServersValidation(user: UserRef, serverErrors: [UserServersError]) + case usageConditions(usageConditions: UsageConditions, conditionsText: String, acceptedConditions: UsageConditions?) + case chatItemTTL(user: UserRef, chatItemTTL: Int64?) + case networkConfig(networkConfig: NetCfg) + case contactInfo(user: UserRef, contact: Contact, connectionStats_: ConnectionStats?, customUserProfile: Profile?) + case groupMemberInfo(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats_: ConnectionStats?) + case queueInfo(user: UserRef, rcvMsgInfo: RcvMsgInfo?, queueInfo: ServerQueueInfo) + case contactSwitchStarted(user: UserRef, contact: Contact, connectionStats: ConnectionStats) + case groupMemberSwitchStarted(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats) + case contactSwitchAborted(user: UserRef, contact: Contact, connectionStats: ConnectionStats) + case groupMemberSwitchAborted(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats) + case contactRatchetSyncStarted(user: UserRef, contact: Contact, connectionStats: ConnectionStats) + case groupMemberRatchetSyncStarted(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats) + case contactCode(user: UserRef, contact: Contact, connectionCode: String) + case groupMemberCode(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionCode: String) + case connectionVerified(user: UserRef, verified: Bool, expectedCode: String) + case tagsUpdated(user: UserRef, userTags: [ChatTag], chatTags: [Int64]) + + var responseType: String { + switch self { + case .activeUser: "activeUser" + case .usersList: "usersList" + case .chatStarted: "chatStarted" + case .chatRunning: "chatRunning" + case .chatStopped: "chatStopped" + case .apiChats: "apiChats" + case .apiChat: "apiChat" + case .chatTags: "chatTags" + case .chatItemInfo: "chatItemInfo" + case .serverTestResult: "serverTestResult" + case .serverOperatorConditions: "serverOperators" + case .userServers: "userServers" + case .userServersValidation: "userServersValidation" + case .usageConditions: "usageConditions" + case .chatItemTTL: "chatItemTTL" + case .networkConfig: "networkConfig" + case .contactInfo: "contactInfo" + case .groupMemberInfo: "groupMemberInfo" + case .queueInfo: "queueInfo" + case .contactSwitchStarted: "contactSwitchStarted" + case .groupMemberSwitchStarted: "groupMemberSwitchStarted" + case .contactSwitchAborted: "contactSwitchAborted" + case .groupMemberSwitchAborted: "groupMemberSwitchAborted" + case .contactRatchetSyncStarted: "contactRatchetSyncStarted" + case .groupMemberRatchetSyncStarted: "groupMemberRatchetSyncStarted" + case .contactCode: "contactCode" + case .groupMemberCode: "groupMemberCode" + case .connectionVerified: "connectionVerified" + case .tagsUpdated: "tagsUpdated" + } + } + + var details: String { + switch self { + case let .activeUser(user): return String(describing: user) + case let .usersList(users): return String(describing: users) + case .chatStarted: return noDetails + case .chatRunning: return noDetails + case .chatStopped: return noDetails + case let .apiChats(u, chats): return withUser(u, String(describing: chats)) + case let .apiChat(u, chat, navInfo): return withUser(u, "chat: \(String(describing: chat))\nnavInfo: \(String(describing: navInfo))") + case let .chatTags(u, userTags): return withUser(u, "userTags: \(String(describing: userTags))") + case let .chatItemInfo(u, chatItem, chatItemInfo): return withUser(u, "chatItem: \(String(describing: chatItem))\nchatItemInfo: \(String(describing: chatItemInfo))") + case let .serverTestResult(u, server, testFailure): return withUser(u, "server: \(server)\nresult: \(String(describing: testFailure))") + case let .serverOperatorConditions(conditions): return "conditions: \(String(describing: conditions))" + case let .userServers(u, userServers): return withUser(u, "userServers: \(String(describing: userServers))") + case let .userServersValidation(u, serverErrors): return withUser(u, "serverErrors: \(String(describing: serverErrors))") + case let .usageConditions(usageConditions, _, acceptedConditions): return "usageConditions: \(String(describing: usageConditions))\nacceptedConditions: \(String(describing: acceptedConditions))" + case let .chatItemTTL(u, chatItemTTL): return withUser(u, String(describing: chatItemTTL)) + case let .networkConfig(networkConfig): return String(describing: networkConfig) + case let .contactInfo(u, contact, connectionStats_, customUserProfile): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats_: \(String(describing: connectionStats_))\ncustomUserProfile: \(String(describing: customUserProfile))") + case let .groupMemberInfo(u, groupInfo, member, connectionStats_): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats_: \(String(describing: connectionStats_))") + case let .queueInfo(u, rcvMsgInfo, queueInfo): + let msgInfo = if let info = rcvMsgInfo { encodeJSON(info) } else { "none" } + return withUser(u, "rcvMsgInfo: \(msgInfo)\nqueueInfo: \(encodeJSON(queueInfo))") + case let .contactSwitchStarted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))") + case let .groupMemberSwitchStarted(u, groupInfo, member, connectionStats): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats: \(String(describing: connectionStats))") + case let .contactSwitchAborted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))") + case let .groupMemberSwitchAborted(u, groupInfo, member, connectionStats): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats: \(String(describing: connectionStats))") + case let .contactRatchetSyncStarted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))") + case let .groupMemberRatchetSyncStarted(u, groupInfo, member, connectionStats): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats: \(String(describing: connectionStats))") + case let .contactCode(u, contact, connectionCode): return withUser(u, "contact: \(String(describing: contact))\nconnectionCode: \(connectionCode)") + case let .groupMemberCode(u, groupInfo, member, connectionCode): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionCode: \(connectionCode)") + case let .connectionVerified(u, verified, expectedCode): return withUser(u, "verified: \(verified)\nconnectionCode: \(expectedCode)") + case let .tagsUpdated(u, userTags, chatTags): return withUser(u, "userTags: \(String(describing: userTags))\nchatTags: \(String(describing: chatTags))") + } + } + + static func fallbackResult(_ type: String, _ json: NSDictionary) -> ChatResponse0? { + if type == "apiChats" { + if let r = parseApiChats(json) { + return .apiChats(user: r.user, chats: r.chats) + } + } else if type == "apiChat" { + if let jApiChat = json["apiChat"] as? NSDictionary, + let user: UserRef = try? decodeObject(jApiChat["user"] as Any), + let jChat = jApiChat["chat"] as? NSDictionary, + let (chat, navInfo) = try? parseChatData(jChat, jApiChat["navInfo"] as? NSDictionary) { + return .apiChat(user: user, chat: chat, navInfo: navInfo) + } + } + return nil + } +} + +enum ChatResponse1: Decodable, ChatAPIResult { + case invitation(user: UserRef, connLinkInvitation: CreatedConnLink, connection: PendingContactConnection) + case connectionIncognitoUpdated(user: UserRef, toConnection: PendingContactConnection) + case connectionUserChanged(user: UserRef, fromConnection: PendingContactConnection, toConnection: PendingContactConnection, newUser: UserRef) + case connectionPlan(user: UserRef, connLink: CreatedConnLink, connectionPlan: ConnectionPlan) + case sentConfirmation(user: UserRef, connection: PendingContactConnection) + case sentInvitation(user: UserRef, connection: PendingContactConnection) + case sentInvitationToContact(user: UserRef, contact: Contact, customUserProfile: Profile?) + case contactAlreadyExists(user: UserRef, contact: Contact) + case contactDeleted(user: UserRef, contact: Contact) + case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection) + case groupDeletedUser(user: UserRef, groupInfo: GroupInfo) + case chatCleared(user: UserRef, chatInfo: ChatInfo) + case userProfileNoChange(user: User) + case userProfileUpdated(user: User, fromProfile: Profile, toProfile: Profile, updateSummary: UserProfileUpdateSummary) + case userPrivacy(user: User, updatedUser: User) + case contactAliasUpdated(user: UserRef, toContact: Contact) + case groupAliasUpdated(user: UserRef, toGroup: GroupInfo) + case connectionAliasUpdated(user: UserRef, toConnection: PendingContactConnection) + case contactPrefsUpdated(user: User, fromContact: Contact, toContact: Contact) + case userContactLink(user: User, contactLink: UserContactLink) + case userContactLinkUpdated(user: User, contactLink: UserContactLink) + case userContactLinkCreated(user: User, connLinkContact: CreatedConnLink) + case userContactLinkDeleted(user: User) + case acceptingContactRequest(user: UserRef, contact: Contact) + case contactRequestRejected(user: UserRef) + case networkStatuses(user_: UserRef?, networkStatuses: [ConnNetworkStatus]) + case newChatItems(user: UserRef, chatItems: [AChatItem]) + case groupChatItemsDeleted(user: UserRef, groupInfo: GroupInfo, chatItemIDs: Set, byUser: Bool, member_: GroupMember?) + case forwardPlan(user: UserRef, chatItemIds: [Int64], forwardConfirmation: ForwardConfirmation?) + case chatItemUpdated(user: UserRef, chatItem: AChatItem) + case chatItemNotChanged(user: UserRef, chatItem: AChatItem) + case chatItemReaction(user: UserRef, added: Bool, reaction: ACIReaction) + case reactionMembers(user: UserRef, memberReactions: [MemberReaction]) + case chatItemsDeleted(user: UserRef, chatItemDeletions: [ChatItemDeletion], byUser: Bool) + case contactsList(user: UserRef, contacts: [Contact]) + + var responseType: String { + switch self { + case .invitation: "invitation" + case .connectionIncognitoUpdated: "connectionIncognitoUpdated" + case .connectionUserChanged: "connectionUserChanged" + case .connectionPlan: "connectionPlan" + case .sentConfirmation: "sentConfirmation" + case .sentInvitation: "sentInvitation" + case .sentInvitationToContact: "sentInvitationToContact" + case .contactAlreadyExists: "contactAlreadyExists" + case .contactDeleted: "contactDeleted" + case .contactConnectionDeleted: "contactConnectionDeleted" + case .groupDeletedUser: "groupDeletedUser" + case .chatCleared: "chatCleared" + case .userProfileNoChange: "userProfileNoChange" + case .userProfileUpdated: "userProfileUpdated" + case .userPrivacy: "userPrivacy" + case .contactAliasUpdated: "contactAliasUpdated" + case .groupAliasUpdated: "groupAliasUpdated" + case .connectionAliasUpdated: "connectionAliasUpdated" + case .contactPrefsUpdated: "contactPrefsUpdated" + case .userContactLink: "userContactLink" + case .userContactLinkUpdated: "userContactLinkUpdated" + case .userContactLinkCreated: "userContactLinkCreated" + case .userContactLinkDeleted: "userContactLinkDeleted" + case .acceptingContactRequest: "acceptingContactRequest" + case .contactRequestRejected: "contactRequestRejected" + case .networkStatuses: "networkStatuses" + case .newChatItems: "newChatItems" + case .groupChatItemsDeleted: "groupChatItemsDeleted" + case .forwardPlan: "forwardPlan" + case .chatItemUpdated: "chatItemUpdated" + case .chatItemNotChanged: "chatItemNotChanged" + case .chatItemReaction: "chatItemReaction" + case .reactionMembers: "reactionMembers" + case .chatItemsDeleted: "chatItemsDeleted" + case .contactsList: "contactsList" + } + } + + var details: String { + switch self { + case let .contactDeleted(u, contact): return withUser(u, String(describing: contact)) + case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection)) + case let .groupDeletedUser(u, groupInfo): return withUser(u, String(describing: groupInfo)) + case let .chatCleared(u, chatInfo): return withUser(u, String(describing: chatInfo)) + case .userProfileNoChange: return noDetails + case let .userProfileUpdated(u, _, toProfile, _): return withUser(u, String(describing: toProfile)) + case let .userPrivacy(u, updatedUser): return withUser(u, String(describing: updatedUser)) + case let .contactAliasUpdated(u, toContact): return withUser(u, String(describing: toContact)) + case let .groupAliasUpdated(u, toGroup): return withUser(u, String(describing: toGroup)) + case let .connectionAliasUpdated(u, toConnection): return withUser(u, String(describing: toConnection)) + case let .contactPrefsUpdated(u, fromContact, toContact): return withUser(u, "fromContact: \(String(describing: fromContact))\ntoContact: \(String(describing: toContact))") + case let .userContactLink(u, contactLink): return withUser(u, contactLink.responseDetails) + case let .userContactLinkUpdated(u, contactLink): return withUser(u, contactLink.responseDetails) + case let .userContactLinkCreated(u, connLink): return withUser(u, String(describing: connLink)) + case .userContactLinkDeleted: return noDetails + case let .acceptingContactRequest(u, contact): return withUser(u, String(describing: contact)) + case .contactRequestRejected: return noDetails + case let .networkStatuses(u, statuses): return withUser(u, String(describing: statuses)) + case let .newChatItems(u, chatItems): + let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n") + return withUser(u, itemsString) + case let .groupChatItemsDeleted(u, gInfo, chatItemIDs, byUser, member_): + return withUser(u, "chatItemIDs: \(String(describing: chatItemIDs))\ngroupInfo: \(String(describing: gInfo))\nbyUser: \(byUser)\nmember_: \(String(describing: member_))") + case let .forwardPlan(u, chatItemIds, forwardConfirmation): return withUser(u, "items: \(chatItemIds) forwardConfirmation: \(String(describing: forwardConfirmation))") + case let .chatItemUpdated(u, chatItem): return withUser(u, String(describing: chatItem)) + case let .chatItemNotChanged(u, chatItem): return withUser(u, String(describing: chatItem)) + case let .chatItemReaction(u, added, reaction): return withUser(u, "added: \(added)\n\(String(describing: reaction))") + case let .reactionMembers(u, reaction): return withUser(u, "memberReactions: \(String(describing: reaction))") + case let .chatItemsDeleted(u, items, byUser): + let itemsString = items.map { item in + "deletedChatItem:\n\(String(describing: item.deletedChatItem))\ntoChatItem:\n\(String(describing: item.toChatItem))" }.joined(separator: "\n") + return withUser(u, itemsString + "\nbyUser: \(byUser)") + case let .contactsList(u, contacts): return withUser(u, String(describing: contacts)) + case let .invitation(u, connLinkInvitation, connection): return withUser(u, "connLinkInvitation: \(connLinkInvitation)\nconnection: \(connection)") + case let .connectionIncognitoUpdated(u, toConnection): return withUser(u, String(describing: toConnection)) + case let .connectionUserChanged(u, fromConnection, toConnection, newUser): return withUser(u, "fromConnection: \(String(describing: fromConnection))\ntoConnection: \(String(describing: toConnection))\nnewUserId: \(String(describing: newUser.userId))") + case let .connectionPlan(u, connLink, connectionPlan): return withUser(u, "connLink: \(String(describing: connLink))\nconnectionPlan: \(String(describing: connectionPlan))") + case let .sentConfirmation(u, connection): return withUser(u, String(describing: connection)) + case let .sentInvitation(u, connection): return withUser(u, String(describing: connection)) + case let .sentInvitationToContact(u, contact, _): return withUser(u, String(describing: contact)) + case let .contactAlreadyExists(u, contact): return withUser(u, String(describing: contact)) + } + } +} + +enum ChatResponse2: Decodable, ChatAPIResult { + // group responses + case groupCreated(user: UserRef, groupInfo: GroupInfo) + case sentGroupInvitation(user: UserRef, groupInfo: GroupInfo, contact: Contact, member: GroupMember) + case userAcceptedGroupSent(user: UserRef, groupInfo: GroupInfo, hostContact: Contact?) + case userDeletedMembers(user: UserRef, groupInfo: GroupInfo, members: [GroupMember], withMessages: Bool) + case leftMemberUser(user: UserRef, groupInfo: GroupInfo) + case groupMembers(user: UserRef, group: SimpleXChat.Group) + case membersRoleUser(user: UserRef, groupInfo: GroupInfo, members: [GroupMember], toRole: GroupMemberRole) + case membersBlockedForAllUser(user: UserRef, groupInfo: GroupInfo, members: [GroupMember], blocked: Bool) + case groupUpdated(user: UserRef, toGroup: GroupInfo) + case groupLinkCreated(user: UserRef, groupInfo: GroupInfo, connLinkContact: CreatedConnLink, memberRole: GroupMemberRole) + case groupLink(user: UserRef, groupInfo: GroupInfo, connLinkContact: CreatedConnLink, memberRole: GroupMemberRole) + case groupLinkDeleted(user: UserRef, groupInfo: GroupInfo) + case newMemberContact(user: UserRef, contact: Contact, groupInfo: GroupInfo, member: GroupMember) + case newMemberContactSentInv(user: UserRef, contact: Contact, groupInfo: GroupInfo, member: GroupMember) + // receiving file responses + case rcvFileAccepted(user: UserRef, chatItem: AChatItem) + case rcvFileAcceptedSndCancelled(user: UserRef, rcvFileTransfer: RcvFileTransfer) + case standaloneFileInfo(fileMeta: MigrationFileLinkData?) + case rcvStandaloneFileCreated(user: UserRef, rcvFileTransfer: RcvFileTransfer) + case rcvFileCancelled(user: UserRef, chatItem_: AChatItem?, rcvFileTransfer: RcvFileTransfer) + // sending file responses + case sndFileCancelled(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, sndFileTransfers: [SndFileTransfer]) + case sndStandaloneFileCreated(user: UserRef, fileTransferMeta: FileTransferMeta) // returned by _upload + case sndFileStartXFTP(user: UserRef, chatItem: AChatItem, fileTransferMeta: FileTransferMeta) // not used + case sndFileCancelledXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta) + // call invitations + case callInvitations(callInvitations: [RcvCallInvitation]) + // notifications + case ntfTokenStatus(status: NtfTknStatus) + case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode, ntfServer: String) + case ntfConns(ntfConns: [NtfConn]) + case connNtfMessages(receivedMsgs: [RcvNtfMsgInfo]) + // remote desktop responses + case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo]) + case remoteCtrlConnecting(remoteCtrl_: RemoteCtrlInfo?, ctrlAppInfo: CtrlAppInfo, appVersion: String) + case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo) + // misc + case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration]) + case cmdOk(user_: UserRef?) + case agentSubsTotal(user: UserRef, subsTotal: SMPServerSubs, hasSession: Bool) + case agentServersSummary(user: UserRef, serversSummary: PresentedServersSummary) + case agentSubsSummary(user: UserRef, subsSummary: SMPServerSubs) + case archiveExported(archiveErrors: [ArchiveError]) + case archiveImported(archiveErrors: [ArchiveError]) + case appSettings(appSettings: AppSettings) + + var responseType: String { + switch self { + case .groupCreated: "groupCreated" + case .sentGroupInvitation: "sentGroupInvitation" + case .userAcceptedGroupSent: "userAcceptedGroupSent" + case .userDeletedMembers: "userDeletedMembers" + case .leftMemberUser: "leftMemberUser" + case .groupMembers: "groupMembers" + case .membersRoleUser: "membersRoleUser" + case .membersBlockedForAllUser: "membersBlockedForAllUser" + case .groupUpdated: "groupUpdated" + case .groupLinkCreated: "groupLinkCreated" + case .groupLink: "groupLink" + case .groupLinkDeleted: "groupLinkDeleted" + case .newMemberContact: "newMemberContact" + case .newMemberContactSentInv: "newMemberContactSentInv" + case .rcvFileAccepted: "rcvFileAccepted" + case .rcvFileAcceptedSndCancelled: "rcvFileAcceptedSndCancelled" + case .standaloneFileInfo: "standaloneFileInfo" + case .rcvStandaloneFileCreated: "rcvStandaloneFileCreated" + case .rcvFileCancelled: "rcvFileCancelled" + case .sndFileCancelled: "sndFileCancelled" + case .sndStandaloneFileCreated: "sndStandaloneFileCreated" + case .sndFileStartXFTP: "sndFileStartXFTP" + case .sndFileCancelledXFTP: "sndFileCancelledXFTP" + case .callInvitations: "callInvitations" + case .ntfTokenStatus: "ntfTokenStatus" + case .ntfToken: "ntfToken" + case .ntfConns: "ntfConns" + case .connNtfMessages: "connNtfMessages" + case .remoteCtrlList: "remoteCtrlList" + case .remoteCtrlConnecting: "remoteCtrlConnecting" + case .remoteCtrlConnected: "remoteCtrlConnected" + case .versionInfo: "versionInfo" + case .cmdOk: "cmdOk" + case .agentSubsTotal: "agentSubsTotal" + case .agentServersSummary: "agentServersSummary" + case .agentSubsSummary: "agentSubsSummary" + case .archiveExported: "archiveExported" + case .archiveImported: "archiveImported" + case .appSettings: "appSettings" + } + } + + var details: String { + switch self { + case let .groupCreated(u, groupInfo): return withUser(u, String(describing: groupInfo)) + case let .sentGroupInvitation(u, groupInfo, contact, member): return withUser(u, "groupInfo: \(groupInfo)\ncontact: \(contact)\nmember: \(member)") + case let .userAcceptedGroupSent(u, groupInfo, hostContact): return withUser(u, "groupInfo: \(groupInfo)\nhostContact: \(String(describing: hostContact))") + case let .userDeletedMembers(u, groupInfo, members, withMessages): return withUser(u, "groupInfo: \(groupInfo)\nmembers: \(members)\nwithMessages: \(withMessages)") + case let .leftMemberUser(u, groupInfo): return withUser(u, String(describing: groupInfo)) + case let .groupMembers(u, group): return withUser(u, String(describing: group)) + case let .membersRoleUser(u, groupInfo, members, toRole): return withUser(u, "groupInfo: \(groupInfo)\nmembers: \(members)\ntoRole: \(toRole)") + case let .membersBlockedForAllUser(u, groupInfo, members, blocked): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(members)\nblocked: \(blocked)") + case let .groupUpdated(u, toGroup): return withUser(u, String(describing: toGroup)) + case let .groupLinkCreated(u, groupInfo, connLinkContact, memberRole): return withUser(u, "groupInfo: \(groupInfo)\nconnLinkContact: \(connLinkContact)\nmemberRole: \(memberRole)") + case let .groupLink(u, groupInfo, connLinkContact, memberRole): return withUser(u, "groupInfo: \(groupInfo)\nconnLinkContact: \(connLinkContact)\nmemberRole: \(memberRole)") + case let .groupLinkDeleted(u, groupInfo): return withUser(u, String(describing: groupInfo)) + case let .newMemberContact(u, contact, groupInfo, member): return withUser(u, "contact: \(contact)\ngroupInfo: \(groupInfo)\nmember: \(member)") + case let .newMemberContactSentInv(u, contact, groupInfo, member): return withUser(u, "contact: \(contact)\ngroupInfo: \(groupInfo)\nmember: \(member)") + case let .rcvFileAccepted(u, chatItem): return withUser(u, String(describing: chatItem)) + case .rcvFileAcceptedSndCancelled: return noDetails + case let .standaloneFileInfo(fileMeta): return String(describing: fileMeta) + case .rcvStandaloneFileCreated: return noDetails + case let .rcvFileCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .sndFileCancelled(u, chatItem, _, _): return withUser(u, String(describing: chatItem)) + case .sndStandaloneFileCreated: return noDetails + case let .sndFileStartXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .sndFileCancelledXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .callInvitations(invs): return String(describing: invs) + case let .ntfTokenStatus(status): return String(describing: status) + case let .ntfToken(token, status, ntfMode, ntfServer): return "token: \(token)\nstatus: \(status.rawValue)\nntfMode: \(ntfMode.rawValue)\nntfServer: \(ntfServer)" + case let .ntfConns(ntfConns): return String(describing: ntfConns) + case let .connNtfMessages(receivedMsgs): return "receivedMsgs: \(String(describing: receivedMsgs))" + case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls) + case let .remoteCtrlConnecting(remoteCtrl_, ctrlAppInfo, appVersion): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nctrlAppInfo:\n\(String(describing: ctrlAppInfo))\nappVersion: \(appVersion)" + case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl) + case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))" + case .cmdOk: return noDetails + case let .agentSubsTotal(u, subsTotal, hasSession): return withUser(u, "subsTotal: \(String(describing: subsTotal))\nhasSession: \(hasSession)") + case let .agentServersSummary(u, serversSummary): return withUser(u, String(describing: serversSummary)) + case let .agentSubsSummary(u, subsSummary): return withUser(u, String(describing: subsSummary)) + case let .archiveExported(archiveErrors): return String(describing: archiveErrors) + case let .archiveImported(archiveErrors): return String(describing: archiveErrors) + case let .appSettings(appSettings): return String(describing: appSettings) + } + } +} + +enum ChatEvent: Decodable, ChatAPIResult { + case chatSuspended + case contactSwitch(user: UserRef, contact: Contact, switchProgress: SwitchProgress) + case groupMemberSwitch(user: UserRef, groupInfo: GroupInfo, member: GroupMember, switchProgress: SwitchProgress) + case contactRatchetSync(user: UserRef, contact: Contact, ratchetSyncProgress: RatchetSyncProgress) + case groupMemberRatchetSync(user: UserRef, groupInfo: GroupInfo, member: GroupMember, ratchetSyncProgress: RatchetSyncProgress) + case contactDeletedByContact(user: UserRef, contact: Contact) + case contactConnected(user: UserRef, contact: Contact, userCustomProfile: Profile?) + case contactConnecting(user: UserRef, contact: Contact) + case contactSndReady(user: UserRef, contact: Contact) + case receivedContactRequest(user: UserRef, contactRequest: UserContactRequest) + case contactUpdated(user: UserRef, toContact: Contact) + case groupMemberUpdated(user: UserRef, groupInfo: GroupInfo, fromMember: GroupMember, toMember: GroupMember) + case contactsMerged(user: UserRef, intoContact: Contact, mergedContact: Contact) + case networkStatus(networkStatus: NetworkStatus, connections: [String]) + case networkStatuses(user_: UserRef?, networkStatuses: [ConnNetworkStatus]) + case newChatItems(user: UserRef, chatItems: [AChatItem]) + case chatItemsStatusesUpdated(user: UserRef, chatItems: [AChatItem]) + case chatItemUpdated(user: UserRef, chatItem: AChatItem) + case chatItemReaction(user: UserRef, added: Bool, reaction: ACIReaction) + case chatItemsDeleted(user: UserRef, chatItemDeletions: [ChatItemDeletion], byUser: Bool) + // group events + case groupChatItemsDeleted(user: UserRef, groupInfo: GroupInfo, chatItemIDs: Set, byUser: Bool, member_: GroupMember?) + case receivedGroupInvitation(user: UserRef, groupInfo: GroupInfo, contact: Contact, memberRole: GroupMemberRole) + case userAcceptedGroupSent(user: UserRef, groupInfo: GroupInfo, hostContact: Contact?) + case groupLinkConnecting(user: UserRef, groupInfo: GroupInfo, hostMember: GroupMember) + case businessLinkConnecting(user: UserRef, groupInfo: GroupInfo, hostMember: GroupMember, fromContact: Contact) + case joinedGroupMemberConnecting(user: UserRef, groupInfo: GroupInfo, hostMember: GroupMember, member: GroupMember) + case memberRole(user: UserRef, groupInfo: GroupInfo, byMember: GroupMember, member: GroupMember, fromRole: GroupMemberRole, toRole: GroupMemberRole) + case memberBlockedForAll(user: UserRef, groupInfo: GroupInfo, byMember: GroupMember, member: GroupMember, blocked: Bool) + case deletedMemberUser(user: UserRef, groupInfo: GroupInfo, member: GroupMember, withMessages: Bool) + case deletedMember(user: UserRef, groupInfo: GroupInfo, byMember: GroupMember, deletedMember: GroupMember, withMessages: Bool) + case leftMember(user: UserRef, groupInfo: GroupInfo, member: GroupMember) + case groupDeleted(user: UserRef, groupInfo: GroupInfo, member: GroupMember) + case userJoinedGroup(user: UserRef, groupInfo: GroupInfo) + case joinedGroupMember(user: UserRef, groupInfo: GroupInfo, member: GroupMember) + case connectedToGroupMember(user: UserRef, groupInfo: GroupInfo, member: GroupMember, memberContact: Contact?) + case groupUpdated(user: UserRef, toGroup: GroupInfo) + case newMemberContactReceivedInv(user: UserRef, contact: Contact, groupInfo: GroupInfo, member: GroupMember) + // receiving file events + case rcvFileAccepted(user: UserRef, chatItem: AChatItem) + case rcvFileAcceptedSndCancelled(user: UserRef, rcvFileTransfer: RcvFileTransfer) + case rcvFileStart(user: UserRef, chatItem: AChatItem) // send by chats + case rcvFileProgressXFTP(user: UserRef, chatItem_: AChatItem?, receivedSize: Int64, totalSize: Int64, rcvFileTransfer: RcvFileTransfer) + case rcvFileComplete(user: UserRef, chatItem: AChatItem) + case rcvStandaloneFileComplete(user: UserRef, targetPath: String, rcvFileTransfer: RcvFileTransfer) + case rcvFileSndCancelled(user: UserRef, chatItem: AChatItem, rcvFileTransfer: RcvFileTransfer) + case rcvFileError(user: UserRef, chatItem_: AChatItem?, agentError: AgentErrorType, rcvFileTransfer: RcvFileTransfer) + case rcvFileWarning(user: UserRef, chatItem_: AChatItem?, agentError: AgentErrorType, rcvFileTransfer: RcvFileTransfer) + // sending file events + case sndFileStart(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer) + case sndFileComplete(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer) + case sndFileRcvCancelled(user: UserRef, chatItem_: AChatItem?, sndFileTransfer: SndFileTransfer) + case sndFileProgressXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, sentSize: Int64, totalSize: Int64) + case sndFileRedirectStartXFTP(user: UserRef, fileTransferMeta: FileTransferMeta, redirectMeta: FileTransferMeta) + case sndFileCompleteXFTP(user: UserRef, chatItem: AChatItem, fileTransferMeta: FileTransferMeta) + case sndStandaloneFileComplete(user: UserRef, fileTransferMeta: FileTransferMeta, rcvURIs: [String]) + case sndFileError(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, errorMessage: String) + case sndFileWarning(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, errorMessage: String) + // call events + case callInvitation(callInvitation: RcvCallInvitation) + case callOffer(user: UserRef, contact: Contact, callType: CallType, offer: WebRTCSession, sharedKey: String?, askConfirmation: Bool) + case callAnswer(user: UserRef, contact: Contact, answer: WebRTCSession) + case callExtraInfo(user: UserRef, contact: Contact, extraInfo: WebRTCExtraInfo) + case callEnded(user: UserRef, contact: Contact) + case contactDisabled(user: UserRef, contact: Contact) + // notification marker + case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgAckInfo) + // remote desktop responses + case remoteCtrlFound(remoteCtrl: RemoteCtrlInfo, ctrlAppInfo_: CtrlAppInfo?, appVersion: String, compatible: Bool) + case remoteCtrlSessionCode(remoteCtrl_: RemoteCtrlInfo?, sessionCode: String) + case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo) + case remoteCtrlStopped(rcsState: RemoteCtrlSessionState, rcStopReason: RemoteCtrlStopReason) + // pq + case contactPQEnabled(user: UserRef, contact: Contact, pqEnabled: Bool) + + var responseType: String { + switch self { + case .chatSuspended: "chatSuspended" + case .contactSwitch: "contactSwitch" + case .groupMemberSwitch: "groupMemberSwitch" + case .contactRatchetSync: "contactRatchetSync" + case .groupMemberRatchetSync: "groupMemberRatchetSync" + case .contactDeletedByContact: "contactDeletedByContact" + case .contactConnected: "contactConnected" + case .contactConnecting: "contactConnecting" + case .contactSndReady: "contactSndReady" + case .receivedContactRequest: "receivedContactRequest" + case .contactUpdated: "contactUpdated" + case .groupMemberUpdated: "groupMemberUpdated" + case .contactsMerged: "contactsMerged" + case .networkStatus: "networkStatus" + case .networkStatuses: "networkStatuses" + case .newChatItems: "newChatItems" + case .chatItemsStatusesUpdated: "chatItemsStatusesUpdated" + case .chatItemUpdated: "chatItemUpdated" + case .chatItemReaction: "chatItemReaction" + case .chatItemsDeleted: "chatItemsDeleted" + case .groupChatItemsDeleted: "groupChatItemsDeleted" + case .receivedGroupInvitation: "receivedGroupInvitation" + case .userAcceptedGroupSent: "userAcceptedGroupSent" + case .groupLinkConnecting: "groupLinkConnecting" + case .businessLinkConnecting: "businessLinkConnecting" + case .joinedGroupMemberConnecting: "joinedGroupMemberConnecting" + case .memberRole: "memberRole" + case .memberBlockedForAll: "memberBlockedForAll" + case .deletedMemberUser: "deletedMemberUser" + case .deletedMember: "deletedMember" + case .leftMember: "leftMember" + case .groupDeleted: "groupDeleted" + case .userJoinedGroup: "userJoinedGroup" + case .joinedGroupMember: "joinedGroupMember" + case .connectedToGroupMember: "connectedToGroupMember" + case .groupUpdated: "groupUpdated" + case .newMemberContactReceivedInv: "newMemberContactReceivedInv" + case .rcvFileAccepted: "rcvFileAccepted" + case .rcvFileAcceptedSndCancelled: "rcvFileAcceptedSndCancelled" + case .rcvFileStart: "rcvFileStart" + case .rcvFileProgressXFTP: "rcvFileProgressXFTP" + case .rcvFileComplete: "rcvFileComplete" + case .rcvStandaloneFileComplete: "rcvStandaloneFileComplete" + case .rcvFileSndCancelled: "rcvFileSndCancelled" + case .rcvFileError: "rcvFileError" + case .rcvFileWarning: "rcvFileWarning" + case .sndFileStart: "sndFileStart" + case .sndFileComplete: "sndFileComplete" + case .sndFileRcvCancelled: "sndFileRcvCancelled" + case .sndFileProgressXFTP: "sndFileProgressXFTP" + case .sndFileRedirectStartXFTP: "sndFileRedirectStartXFTP" + case .sndFileCompleteXFTP: "sndFileCompleteXFTP" + case .sndStandaloneFileComplete: "sndStandaloneFileComplete" + case .sndFileError: "sndFileError" + case .sndFileWarning: "sndFileWarning" + case .callInvitation: "callInvitation" + case .callOffer: "callOffer" + case .callAnswer: "callAnswer" + case .callExtraInfo: "callExtraInfo" + case .callEnded: "callEnded" + case .contactDisabled: "contactDisabled" + case .ntfMessage: "ntfMessage" + case .remoteCtrlFound: "remoteCtrlFound" + case .remoteCtrlSessionCode: "remoteCtrlSessionCode" + case .remoteCtrlConnected: "remoteCtrlConnected" + case .remoteCtrlStopped: "remoteCtrlStopped" + case .contactPQEnabled: "contactPQEnabled" + } + } + + var details: String { + switch self { + case .chatSuspended: return noDetails + case let .contactSwitch(u, contact, switchProgress): return withUser(u, "contact: \(String(describing: contact))\nswitchProgress: \(String(describing: switchProgress))") + case let .groupMemberSwitch(u, groupInfo, member, switchProgress): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nswitchProgress: \(String(describing: switchProgress))") + case let .contactRatchetSync(u, contact, ratchetSyncProgress): return withUser(u, "contact: \(String(describing: contact))\nratchetSyncProgress: \(String(describing: ratchetSyncProgress))") + case let .groupMemberRatchetSync(u, groupInfo, member, ratchetSyncProgress): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nratchetSyncProgress: \(String(describing: ratchetSyncProgress))") + case let .contactDeletedByContact(u, contact): return withUser(u, String(describing: contact)) + case let .contactConnected(u, contact, _): return withUser(u, String(describing: contact)) + case let .contactConnecting(u, contact): return withUser(u, String(describing: contact)) + case let .contactSndReady(u, contact): return withUser(u, String(describing: contact)) + case let .receivedContactRequest(u, contactRequest): return withUser(u, String(describing: contactRequest)) + case let .contactUpdated(u, toContact): return withUser(u, String(describing: toContact)) + case let .groupMemberUpdated(u, groupInfo, fromMember, toMember): return withUser(u, "groupInfo: \(groupInfo)\nfromMember: \(fromMember)\ntoMember: \(toMember)") + case let .contactsMerged(u, intoContact, mergedContact): return withUser(u, "intoContact: \(intoContact)\nmergedContact: \(mergedContact)") + case let .networkStatus(status, conns): return "networkStatus: \(String(describing: status))\nconnections: \(String(describing: conns))" + case let .networkStatuses(u, statuses): return withUser(u, String(describing: statuses)) + case let .newChatItems(u, chatItems): + let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n") + return withUser(u, itemsString) + case let .chatItemsStatusesUpdated(u, chatItems): + let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n") + return withUser(u, itemsString) + case let .chatItemUpdated(u, chatItem): return withUser(u, String(describing: chatItem)) + case let .chatItemReaction(u, added, reaction): return withUser(u, "added: \(added)\n\(String(describing: reaction))") + case let .chatItemsDeleted(u, items, byUser): + let itemsString = items.map { item in + "deletedChatItem:\n\(String(describing: item.deletedChatItem))\ntoChatItem:\n\(String(describing: item.toChatItem))" }.joined(separator: "\n") + return withUser(u, itemsString + "\nbyUser: \(byUser)") + case let .groupChatItemsDeleted(u, gInfo, chatItemIDs, byUser, member_): + return withUser(u, "chatItemIDs: \(String(describing: chatItemIDs))\ngroupInfo: \(String(describing: gInfo))\nbyUser: \(byUser)\nmember_: \(String(describing: member_))") + case let .receivedGroupInvitation(u, groupInfo, contact, memberRole): return withUser(u, "groupInfo: \(groupInfo)\ncontact: \(contact)\nmemberRole: \(memberRole)") + case let .userAcceptedGroupSent(u, groupInfo, hostContact): return withUser(u, "groupInfo: \(groupInfo)\nhostContact: \(String(describing: hostContact))") + case let .groupLinkConnecting(u, groupInfo, hostMember): return withUser(u, "groupInfo: \(groupInfo)\nhostMember: \(String(describing: hostMember))") + case let .businessLinkConnecting(u, groupInfo, hostMember, fromContact): return withUser(u, "groupInfo: \(groupInfo)\nhostMember: \(String(describing: hostMember))\nfromContact: \(String(describing: fromContact))") + case let .joinedGroupMemberConnecting(u, groupInfo, hostMember, member): return withUser(u, "groupInfo: \(groupInfo)\nhostMember: \(hostMember)\nmember: \(member)") + case let .memberRole(u, groupInfo, byMember, member, fromRole, toRole): return withUser(u, "groupInfo: \(groupInfo)\nbyMember: \(byMember)\nmember: \(member)\nfromRole: \(fromRole)\ntoRole: \(toRole)") + case let .memberBlockedForAll(u, groupInfo, byMember, member, blocked): return withUser(u, "groupInfo: \(groupInfo)\nbyMember: \(byMember)\nmember: \(member)\nblocked: \(blocked)") + case let .deletedMemberUser(u, groupInfo, member, withMessages): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)\nwithMessages: \(withMessages)") + case let .deletedMember(u, groupInfo, byMember, deletedMember, withMessages): return withUser(u, "groupInfo: \(groupInfo)\nbyMember: \(byMember)\ndeletedMember: \(deletedMember)\nwithMessages: \(withMessages)") + case let .leftMember(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)") + case let .groupDeleted(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)") + case let .userJoinedGroup(u, groupInfo): return withUser(u, String(describing: groupInfo)) + case let .joinedGroupMember(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)") + case let .connectedToGroupMember(u, groupInfo, member, memberContact): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)\nmemberContact: \(String(describing: memberContact))") + case let .groupUpdated(u, toGroup): return withUser(u, String(describing: toGroup)) + case let .newMemberContactReceivedInv(u, contact, groupInfo, member): return withUser(u, "contact: \(contact)\ngroupInfo: \(groupInfo)\nmember: \(member)") + case let .rcvFileAccepted(u, chatItem): return withUser(u, String(describing: chatItem)) + case .rcvFileAcceptedSndCancelled: return noDetails + case let .rcvFileStart(u, chatItem): return withUser(u, String(describing: chatItem)) + case let .rcvFileProgressXFTP(u, chatItem, receivedSize, totalSize, _): return withUser(u, "chatItem: \(String(describing: chatItem))\nreceivedSize: \(receivedSize)\ntotalSize: \(totalSize)") + case let .rcvStandaloneFileComplete(u, targetPath, _): return withUser(u, targetPath) + case let .rcvFileComplete(u, chatItem): return withUser(u, String(describing: chatItem)) + case let .rcvFileSndCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .rcvFileError(u, chatItem, agentError, _): return withUser(u, "agentError: \(String(describing: agentError))\nchatItem: \(String(describing: chatItem))") + case let .rcvFileWarning(u, chatItem, agentError, _): return withUser(u, "agentError: \(String(describing: agentError))\nchatItem: \(String(describing: chatItem))") + case let .sndFileStart(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .sndFileComplete(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .sndFileRcvCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .sndFileProgressXFTP(u, chatItem, _, sentSize, totalSize): return withUser(u, "chatItem: \(String(describing: chatItem))\nsentSize: \(sentSize)\ntotalSize: \(totalSize)") + case let .sndFileRedirectStartXFTP(u, _, redirectMeta): return withUser(u, String(describing: redirectMeta)) + case let .sndFileCompleteXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .sndStandaloneFileComplete(u, _, rcvURIs): return withUser(u, String(rcvURIs.count)) + case let .sndFileError(u, chatItem, _, err): return withUser(u, "error: \(String(describing: err))\nchatItem: \(String(describing: chatItem))") + case let .sndFileWarning(u, chatItem, _, err): return withUser(u, "error: \(String(describing: err))\nchatItem: \(String(describing: chatItem))") + case let .callInvitation(inv): return String(describing: inv) + case let .callOffer(u, contact, callType, offer, sharedKey, askConfirmation): return withUser(u, "contact: \(contact.id)\ncallType: \(String(describing: callType))\nsharedKey: \(sharedKey ?? "")\naskConfirmation: \(askConfirmation)\noffer: \(String(describing: offer))") + case let .callAnswer(u, contact, answer): return withUser(u, "contact: \(contact.id)\nanswer: \(String(describing: answer))") + case let .callExtraInfo(u, contact, extraInfo): return withUser(u, "contact: \(contact.id)\nextraInfo: \(String(describing: extraInfo))") + case let .callEnded(u, contact): return withUser(u, "contact: \(contact.id)") + case let .contactDisabled(u, contact): return withUser(u, String(describing: contact)) + case let .ntfMessage(u, connEntity, ntfMessage): return withUser(u, "connEntity: \(String(describing: connEntity))\nntfMessage: \(String(describing: ntfMessage))") + case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): return "remoteCtrl:\n\(String(describing: remoteCtrl))\nctrlAppInfo_:\n\(String(describing: ctrlAppInfo_))\nappVersion: \(appVersion)\ncompatible: \(compatible)" + case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)" + case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl) + case let .remoteCtrlStopped(rcsState, rcStopReason): return "rcsState: \(String(describing: rcsState))\nrcStopReason: \(String(describing: rcStopReason))" + case let .contactPQEnabled(u, contact, pqEnabled): return withUser(u, "contact: \(String(describing: contact))\npqEnabled: \(pqEnabled)") + } + } +} + +struct NewUser: Encodable { + var profile: Profile? + var pastTimestamp: Bool +} + +enum ChatPagination { + static let INITIAL_COUNT = 75 + static let PRELOAD_COUNT = 100 + static let UNTIL_PRELOAD_COUNT = 50 + + case last(count: Int) + case after(chatItemId: Int64, count: Int) + case before(chatItemId: Int64, count: Int) + case around(chatItemId: Int64, count: Int) + case initial(count: Int) + + var cmdString: String { + switch self { + case let .last(count): return "count=\(count)" + case let .after(chatItemId, count): return "after=\(chatItemId) count=\(count)" + case let .before(chatItemId, count): return "before=\(chatItemId) count=\(count)" + case let .around(chatItemId, count): return "around=\(chatItemId) count=\(count)" + case let .initial(count): return "initial=\(count)" + } + } +} + +enum ConnectionPlan: Decodable, Hashable { + case invitationLink(invitationLinkPlan: InvitationLinkPlan) + case contactAddress(contactAddressPlan: ContactAddressPlan) + case groupLink(groupLinkPlan: GroupLinkPlan) + case error(chatError: ChatError) +} + +enum InvitationLinkPlan: Decodable, Hashable { + case ok + case ownLink + case connecting(contact_: Contact?) + case known(contact: Contact) +} + +enum ContactAddressPlan: Decodable, Hashable { + case ok + case ownLink + case connectingConfirmReconnect + case connectingProhibit(contact: Contact) + case known(contact: Contact) + case contactViaAddress(contact: Contact) +} + +enum GroupLinkPlan: Decodable, Hashable { + case ok + case ownLink(groupInfo: GroupInfo) + case connectingConfirmReconnect + case connectingProhibit(groupInfo_: GroupInfo?) + case known(groupInfo: GroupInfo) +} + +struct ChatTagData: Encodable { + var emoji: String? + var text: String +} + +struct UpdatedMessage: Encodable { + var msgContent: MsgContent + var mentions: [String: Int64] + + var cmdString: String { + "json \(encodeJSON(self))" + } +} + +enum ChatDeleteMode: Codable { + case full(notify: Bool) + case entity(notify: Bool) + case messages + + var cmdString: String { + switch self { + case let .full(notify): "full notify=\(onOff(notify))" + case let .entity(notify): "entity notify=\(onOff(notify))" + case .messages: "messages" + } + } + + var isEntity: Bool { + switch self { + case .entity: return true + default: return false + } + } +} + +enum NetworkStatus: Decodable, Equatable { + case unknown + case connected + case disconnected + case error(connectionError: String) + + var statusString: LocalizedStringKey { + switch self { + case .connected: "connected" + case .error: "error" + default: "connecting" + } + } + + var statusExplanation: LocalizedStringKey { + switch self { + case .connected: "You are connected to the server used to receive messages from this contact." + case let .error(err): "Trying to connect to the server used to receive messages from this contact (error: \(err))." + default: "Trying to connect to the server used to receive messages from this contact." + } + } + + var imageName: String { + switch self { + case .unknown: "circle.dotted" + case .connected: "circle.fill" + case .disconnected: "ellipsis.circle.fill" + case .error: "exclamationmark.circle.fill" + } + } +} + +enum ForwardConfirmation: Decodable, Hashable { + case filesNotAccepted(fileIds: [Int64]) + case filesInProgress(filesCount: Int) + case filesMissing(filesCount: Int) + case filesFailed(filesCount: Int) +} + +struct ConnNetworkStatus: Decodable { + var agentConnId: String + var networkStatus: NetworkStatus +} + +struct UserMsgReceiptSettings: Codable { + var enable: Bool + var clearOverrides: Bool +} + + +struct UserContactLink: Decodable, Hashable { + var connLinkContact: CreatedConnLink + var autoAccept: AutoAccept? + + var responseDetails: String { + "connLinkContact: \(connLinkContact)\nautoAccept: \(AutoAccept.cmdString(autoAccept))" + } +} + +struct AutoAccept: Codable, Hashable { + var businessAddress: Bool + var acceptIncognito: Bool + var autoReply: MsgContent? + + static func cmdString(_ autoAccept: AutoAccept?) -> String { + guard let autoAccept = autoAccept else { return "off" } + var s = "on" + if autoAccept.acceptIncognito { + s += " incognito=on" + } else if autoAccept.businessAddress { + s += " business" + } + guard let msg = autoAccept.autoReply else { return s } + return s + " " + msg.cmdString + } +} + +struct DeviceToken: Decodable { + var pushProvider: PushProvider + var token: String + + var cmdString: String { + "\(pushProvider) \(token)" + } +} + +enum PushEnvironment: String { + case development + case production +} + +enum PushProvider: String, Decodable { + case apns_dev + case apns_prod + + init(env: PushEnvironment) { + switch env { + case .development: self = .apns_dev + case .production: self = .apns_prod + } + } +} + +// This notification mode is for app core, UI uses AppNotificationsMode.off to mean completely disable, +// and .local for periodic background checks +enum NotificationsMode: String, Decodable, SelectableItem { + case off = "OFF" + case periodic = "PERIODIC" + case instant = "INSTANT" + + var label: LocalizedStringKey { + switch self { + case .off: "No push server" + case .periodic: "Periodic" + case .instant: "Instant" + } + } + + var icon: String { + switch self { + case .off: return "arrow.clockwise" + case .periodic: return "timer" + case .instant: return "bolt" + } + } + + var id: String { self.rawValue } + + static var values: [NotificationsMode] = [.instant, .periodic, .off] +} + +struct RemoteCtrlInfo: Decodable { + var remoteCtrlId: Int64 + var ctrlDeviceName: String + var sessionState: RemoteCtrlSessionState? + + var deviceViewName: String { + ctrlDeviceName == "" ? "\(remoteCtrlId)" : ctrlDeviceName + } +} + +enum RemoteCtrlSessionState: Decodable { + case starting + case searching + case connecting + case pendingConfirmation(sessionCode: String) + case connected(sessionCode: String) +} + +enum RemoteCtrlStopReason: Decodable { + case discoveryFailed(chatError: ChatError) + case connectionFailed(chatError: ChatError) + case setupFailed(chatError: ChatError) + case disconnected +} + +struct CtrlAppInfo: Decodable { + var appVersionRange: AppVersionRange + var deviceName: String +} + +struct AppVersionRange: Decodable { + var minVersion: String + var maxVersion: String +} + +struct CoreVersionInfo: Decodable { + var version: String + var simplexmqVersion: String + var simplexmqCommit: String +} + +struct ArchiveConfig: Encodable { + var archivePath: String + var disableCompression: Bool? +} + +struct DBEncryptionConfig: Codable { + var currentKey: String + var newKey: String +} + +enum OperatorTag: String, Codable { + case simplex = "simplex" + case flux = "flux" +} + +struct ServerOperatorInfo { + var description: [String] + var website: URL + var selfhost: (text: String, link: URL)? = nil + var logo: String + var largeLogo: String + var logoDarkMode: String + var largeLogoDarkMode: String +} + +let operatorsInfo: Dictionary = [ + .simplex: ServerOperatorInfo( + description: [ + "SimpleX Chat is the first communication network that has no user profile IDs of any kind, not even random numbers or identity keys.", + "SimpleX Chat Ltd develops the communication software for SimpleX network." + ], + website: URL(string: "https://simplex.chat")!, + logo: "decentralized", + largeLogo: "logo", + logoDarkMode: "decentralized-light", + largeLogoDarkMode: "logo-light" + ), + .flux: ServerOperatorInfo( + description: [ + "Flux is the largest decentralized cloud, based on a global network of user-operated nodes.", + "Flux offers a powerful, scalable, and affordable cutting edge technology platform for all.", + "Flux operates servers in SimpleX network to improve its privacy and decentralization." + ], + website: URL(string: "https://runonflux.com")!, + selfhost: (text: "Self-host SimpleX servers on Flux", link: URL(string: "https://home.runonflux.io/apps/marketplace?q=simplex")!), + logo: "flux_logo_symbol", + largeLogo: "flux_logo", + logoDarkMode: "flux_logo_symbol", + largeLogoDarkMode: "flux_logo-light" + ), +] + +struct UsageConditions: Decodable { + var conditionsId: Int64 + var conditionsCommit: String + var notifiedAt: Date? + var createdAt: Date + + static var sampleData = UsageConditions( + conditionsId: 1, + conditionsCommit: "11a44dc1fd461a93079f897048b46998db55da5c", + notifiedAt: nil, + createdAt: Date.now + ) +} + +enum UsageConditionsAction: Decodable { + case review(operators: [ServerOperator], deadline: Date?, showNotice: Bool) + case accepted(operators: [ServerOperator]) + + var showNotice: Bool { + switch self { + case let .review(_, _, showNotice): showNotice + case .accepted: false + } + } +} + +struct ServerOperatorConditions: Decodable { + var serverOperators: [ServerOperator] + var currentConditions: UsageConditions + var conditionsAction: UsageConditionsAction? + + static var empty = ServerOperatorConditions( + serverOperators: [], + currentConditions: UsageConditions(conditionsId: 0, conditionsCommit: "empty", notifiedAt: nil, createdAt: .now), + conditionsAction: nil + ) +} + +enum ConditionsAcceptance: Equatable, Codable, Hashable { + case accepted(acceptedAt: Date?, autoAccepted: Bool) + // If deadline is present, it means there's a grace period to review and accept conditions during which user can continue to use the operator. + // No deadline indicates it's required to accept conditions for the operator to start using it. + case required(deadline: Date?) + + var conditionsAccepted: Bool { + switch self { + case .accepted: true + case .required: false + } + } + + var usageAllowed: Bool { + switch self { + case .accepted: true + case let .required(deadline): deadline != nil + } + } +} + +struct ServerOperator: Identifiable, Equatable, Codable { + var operatorId: Int64 + var operatorTag: OperatorTag? + var tradeName: String + var legalName: String? + var serverDomains: [String] + var conditionsAcceptance: ConditionsAcceptance + var enabled: Bool + var smpRoles: ServerRoles + var xftpRoles: ServerRoles + + var id: Int64 { operatorId } + + static func == (l: ServerOperator, r: ServerOperator) -> Bool { + l.operatorId == r.operatorId && l.operatorTag == r.operatorTag && l.tradeName == r.tradeName && l.legalName == r.legalName && + l.serverDomains == r.serverDomains && l.conditionsAcceptance == r.conditionsAcceptance && l.enabled == r.enabled && + l.smpRoles == r.smpRoles && l.xftpRoles == r.xftpRoles + } + + var legalName_: String { + legalName ?? tradeName + } + + var info: ServerOperatorInfo { + return if let operatorTag = operatorTag { + operatorsInfo[operatorTag] ?? ServerOperator.dummyOperatorInfo + } else { + ServerOperator.dummyOperatorInfo + } + } + + static let dummyOperatorInfo = ServerOperatorInfo( + description: ["Default"], + website: URL(string: "https://simplex.chat")!, + logo: "decentralized", + largeLogo: "logo", + logoDarkMode: "decentralized-light", + largeLogoDarkMode: "logo-light" + ) + + func logo(_ colorScheme: ColorScheme) -> String { + colorScheme == .light ? info.logo : info.logoDarkMode + } + + func largeLogo(_ colorScheme: ColorScheme) -> String { + colorScheme == .light ? info.largeLogo : info.largeLogoDarkMode + } + + static var sampleData1 = ServerOperator( + operatorId: 1, + operatorTag: .simplex, + tradeName: "SimpleX Chat", + legalName: "SimpleX Chat Ltd", + serverDomains: ["simplex.im"], + conditionsAcceptance: .accepted(acceptedAt: nil, autoAccepted: false), + enabled: true, + smpRoles: ServerRoles(storage: true, proxy: true), + xftpRoles: ServerRoles(storage: true, proxy: true) + ) +} + +struct ServerRoles: Equatable, Codable { + var storage: Bool + var proxy: Bool +} + +struct UserOperatorServers: Identifiable, Equatable, Codable { + var `operator`: ServerOperator? + var smpServers: [UserServer] + var xftpServers: [UserServer] + + var id: String { + if let op = self.operator { + "\(op.operatorId)" + } else { + "nil operator" + } + } + + var operator_: ServerOperator { + get { + self.operator ?? ServerOperator( + operatorId: 0, + operatorTag: nil, + tradeName: "", + legalName: "", + serverDomains: [], + conditionsAcceptance: .accepted(acceptedAt: nil, autoAccepted: false), + enabled: false, + smpRoles: ServerRoles(storage: true, proxy: true), + xftpRoles: ServerRoles(storage: true, proxy: true) + ) + } + set { `operator` = newValue } + } + + static var sampleData1 = UserOperatorServers( + operator: ServerOperator.sampleData1, + smpServers: [UserServer.sampleData.preset], + xftpServers: [UserServer.sampleData.xftpPreset] + ) + + static var sampleDataNilOperator = UserOperatorServers( + operator: nil, + smpServers: [UserServer.sampleData.preset], + xftpServers: [UserServer.sampleData.xftpPreset] + ) +} + +enum UserServersError: Decodable { + case noServers(protocol: ServerProtocol, user: UserRef?) + case storageMissing(protocol: ServerProtocol, user: UserRef?) + case proxyMissing(protocol: ServerProtocol, user: UserRef?) + case duplicateServer(protocol: ServerProtocol, duplicateServer: String, duplicateHost: String) + + var globalError: String? { + switch self { + case let .noServers(`protocol`, _): + switch `protocol` { + case .smp: return globalSMPError + case .xftp: return globalXFTPError + } + case let .storageMissing(`protocol`, _): + switch `protocol` { + case .smp: return globalSMPError + case .xftp: return globalXFTPError + } + case let .proxyMissing(`protocol`, _): + switch `protocol` { + case .smp: return globalSMPError + case .xftp: return globalXFTPError + } + default: return nil + } + } + + var globalSMPError: String? { + switch self { + case let .noServers(.smp, user): + let text = NSLocalizedString("No message servers.", comment: "servers error") + if let user = user { + return userStr(user) + " " + text + } else { + return text + } + case let .storageMissing(.smp, user): + let text = NSLocalizedString("No servers to receive messages.", comment: "servers error") + if let user = user { + return userStr(user) + " " + text + } else { + return text + } + case let .proxyMissing(.smp, user): + let text = NSLocalizedString("No servers for private message routing.", comment: "servers error") + if let user = user { + return userStr(user) + " " + text + } else { + return text + } + default: + return nil + } + } + + var globalXFTPError: String? { + switch self { + case let .noServers(.xftp, user): + let text = NSLocalizedString("No media & file servers.", comment: "servers error") + if let user = user { + return userStr(user) + " " + text + } else { + return text + } + case let .storageMissing(.xftp, user): + let text = NSLocalizedString("No servers to send files.", comment: "servers error") + if let user = user { + return userStr(user) + " " + text + } else { + return text + } + case let .proxyMissing(.xftp, user): + let text = NSLocalizedString("No servers to receive files.", comment: "servers error") + if let user = user { + return userStr(user) + " " + text + } else { + return text + } + default: + return nil + } + } + + private func userStr(_ user: UserRef) -> String { + String.localizedStringWithFormat(NSLocalizedString("For chat profile %@:", comment: "servers error"), user.localDisplayName) + } +} + +struct UserServer: Identifiable, Equatable, Codable, Hashable { + var serverId: Int64? + var server: String + var preset: Bool + var tested: Bool? + var enabled: Bool + var deleted: Bool + var createdAt = Date() + + static func == (l: UserServer, r: UserServer) -> Bool { + l.serverId == r.serverId && l.server == r.server && l.preset == r.preset && l.tested == r.tested && + l.enabled == r.enabled && l.deleted == r.deleted + } + + var id: String { "\(server) \(createdAt)" } + + static var empty = UserServer(serverId: nil, server: "", preset: false, tested: nil, enabled: false, deleted: false) + + var isEmpty: Bool { + server.trimmingCharacters(in: .whitespaces) == "" + } + + struct SampleData { + var preset: UserServer + var custom: UserServer + var untested: UserServer + var xftpPreset: UserServer + } + + static var sampleData = SampleData( + preset: UserServer( + serverId: 1, + server: "smp://abcd@smp8.simplex.im", + preset: true, + tested: true, + enabled: true, + deleted: false + ), + custom: UserServer( + serverId: 2, + server: "smp://abcd@smp9.simplex.im", + preset: false, + tested: false, + enabled: false, + deleted: false + ), + untested: UserServer( + serverId: 3, + server: "smp://abcd@smp10.simplex.im", + preset: false, + tested: nil, + enabled: true, + deleted: false + ), + xftpPreset: UserServer( + serverId: 4, + server: "xftp://abcd@xftp8.simplex.im", + preset: true, + tested: true, + enabled: true, + deleted: false + ) + ) + + enum CodingKeys: CodingKey { + case serverId + case server + case preset + case tested + case enabled + case deleted + } +} + +enum ProtocolTestStep: String, Decodable, Equatable { + case connect + case disconnect + case createQueue + case secureQueue + case deleteQueue + case createFile + case uploadFile + case downloadFile + case compareFile + case deleteFile + + var text: String { + switch self { + case .connect: return NSLocalizedString("Connect", comment: "server test step") + case .disconnect: return NSLocalizedString("Disconnect", comment: "server test step") + case .createQueue: return NSLocalizedString("Create queue", comment: "server test step") + case .secureQueue: return NSLocalizedString("Secure queue", comment: "server test step") + case .deleteQueue: return NSLocalizedString("Delete queue", comment: "server test step") + case .createFile: return NSLocalizedString("Create file", comment: "server test step") + case .uploadFile: return NSLocalizedString("Upload file", comment: "server test step") + case .downloadFile: return NSLocalizedString("Download file", comment: "server test step") + case .compareFile: return NSLocalizedString("Compare file", comment: "server test step") + case .deleteFile: return NSLocalizedString("Delete file", comment: "server test step") + } + } +} + +struct ProtocolTestFailure: Decodable, Error, Equatable { + var testStep: ProtocolTestStep + var testError: AgentErrorType + + static func == (l: ProtocolTestFailure, r: ProtocolTestFailure) -> Bool { + l.testStep == r.testStep + } + + var localizedDescription: String { + let err = String.localizedStringWithFormat(NSLocalizedString("Test failed at step %@.", comment: "server test failure"), testStep.text) + switch testError { + case .SMP(_, .AUTH): + return err + " " + NSLocalizedString("Server requires authorization to create queues, check password", comment: "server test error") + case .XFTP(.AUTH): + return err + " " + NSLocalizedString("Server requires authorization to upload, check password", comment: "server test error") + case .BROKER(_, .NETWORK): + return err + " " + NSLocalizedString("Possibly, certificate fingerprint in server address is incorrect", comment: "server test error") + default: + return err + } + } +} + +struct MigrationFileLinkData: Codable { + let networkConfig: NetworkConfig? + + struct NetworkConfig: Codable { + let socksProxy: String? + let networkProxy: NetworkProxy? + let hostMode: HostMode? + let requiredHostMode: Bool? + + func transformToPlatformSupported() -> NetworkConfig { + return if let hostMode, let requiredHostMode { + NetworkConfig( + socksProxy: nil, + networkProxy: nil, + hostMode: hostMode == .onionViaSocks ? .onionHost : hostMode, + requiredHostMode: requiredHostMode + ) + } else { self } + } + } + + func addToLink(link: String) -> String { + "\(link)&data=\(encodeJSON(self).addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)" + } + + static func readFromLink(link: String) -> MigrationFileLinkData? { +// standaloneFileInfo(link) + nil + } +} + +struct AppSettings: Codable, Equatable { + var networkConfig: NetCfg? = nil + var networkProxy: NetworkProxy? = nil + var privacyEncryptLocalFiles: Bool? = nil + var privacyAskToApproveRelays: Bool? = nil + var privacyAcceptImages: Bool? = nil + var privacyLinkPreviews: Bool? = nil + var privacyShowChatPreviews: Bool? = nil + var privacySaveLastDraft: Bool? = nil + var privacyProtectScreen: Bool? = nil + var privacyMediaBlurRadius: Int? = nil + var notificationMode: AppSettingsNotificationMode? = nil + var notificationPreviewMode: NotificationPreviewMode? = nil + var webrtcPolicyRelay: Bool? = nil + var webrtcICEServers: [String]? = nil + var confirmRemoteSessions: Bool? = nil + var connectRemoteViaMulticast: Bool? = nil + var connectRemoteViaMulticastAuto: Bool? = nil + var developerTools: Bool? = nil + var confirmDBUpgrades: Bool? = nil + var androidCallOnLockScreen: AppSettingsLockScreenCalls? = nil + var iosCallKitEnabled: Bool? = nil + var iosCallKitCallsInRecents: Bool? = nil + var uiProfileImageCornerRadius: Double? = nil + var uiChatItemRoundness: Double? = nil + var uiChatItemTail: Bool? = nil + var uiColorScheme: String? = nil + var uiDarkColorScheme: String? = nil + var uiCurrentThemeIds: [String: String]? = nil + var uiThemes: [ThemeOverrides]? = nil + var oneHandUI: Bool? = nil + var chatBottomBar: Bool? = nil + + func prepareForExport() -> AppSettings { + var empty = AppSettings() + let def = AppSettings.defaults + if networkConfig != def.networkConfig { empty.networkConfig = networkConfig } + if networkProxy != def.networkProxy { empty.networkProxy = networkProxy } + if privacyEncryptLocalFiles != def.privacyEncryptLocalFiles { empty.privacyEncryptLocalFiles = privacyEncryptLocalFiles } + if privacyAskToApproveRelays != def.privacyAskToApproveRelays { empty.privacyAskToApproveRelays = privacyAskToApproveRelays } + if privacyAcceptImages != def.privacyAcceptImages { empty.privacyAcceptImages = privacyAcceptImages } + if privacyLinkPreviews != def.privacyLinkPreviews { empty.privacyLinkPreviews = privacyLinkPreviews } + if privacyShowChatPreviews != def.privacyShowChatPreviews { empty.privacyShowChatPreviews = privacyShowChatPreviews } + if privacySaveLastDraft != def.privacySaveLastDraft { empty.privacySaveLastDraft = privacySaveLastDraft } + if privacyProtectScreen != def.privacyProtectScreen { empty.privacyProtectScreen = privacyProtectScreen } + if privacyMediaBlurRadius != def.privacyMediaBlurRadius { empty.privacyMediaBlurRadius = privacyMediaBlurRadius } + if notificationMode != def.notificationMode { empty.notificationMode = notificationMode } + if notificationPreviewMode != def.notificationPreviewMode { empty.notificationPreviewMode = notificationPreviewMode } + if webrtcPolicyRelay != def.webrtcPolicyRelay { empty.webrtcPolicyRelay = webrtcPolicyRelay } + if webrtcICEServers != def.webrtcICEServers { empty.webrtcICEServers = webrtcICEServers } + if confirmRemoteSessions != def.confirmRemoteSessions { empty.confirmRemoteSessions = confirmRemoteSessions } + if connectRemoteViaMulticast != def.connectRemoteViaMulticast {empty.connectRemoteViaMulticast = connectRemoteViaMulticast } + if connectRemoteViaMulticastAuto != def.connectRemoteViaMulticastAuto { empty.connectRemoteViaMulticastAuto = connectRemoteViaMulticastAuto } + if developerTools != def.developerTools { empty.developerTools = developerTools } + if confirmDBUpgrades != def.confirmDBUpgrades { empty.confirmDBUpgrades = confirmDBUpgrades } + if androidCallOnLockScreen != def.androidCallOnLockScreen { empty.androidCallOnLockScreen = androidCallOnLockScreen } + if iosCallKitEnabled != def.iosCallKitEnabled { empty.iosCallKitEnabled = iosCallKitEnabled } + if iosCallKitCallsInRecents != def.iosCallKitCallsInRecents { empty.iosCallKitCallsInRecents = iosCallKitCallsInRecents } + if uiProfileImageCornerRadius != def.uiProfileImageCornerRadius { empty.uiProfileImageCornerRadius = uiProfileImageCornerRadius } + if uiChatItemRoundness != def.uiChatItemRoundness { empty.uiChatItemRoundness = uiChatItemRoundness } + if uiChatItemTail != def.uiChatItemTail { empty.uiChatItemTail = uiChatItemTail } + if uiColorScheme != def.uiColorScheme { empty.uiColorScheme = uiColorScheme } + if uiDarkColorScheme != def.uiDarkColorScheme { empty.uiDarkColorScheme = uiDarkColorScheme } + if uiCurrentThemeIds != def.uiCurrentThemeIds { empty.uiCurrentThemeIds = uiCurrentThemeIds } + if uiThemes != def.uiThemes { empty.uiThemes = uiThemes } + if oneHandUI != def.oneHandUI { empty.oneHandUI = oneHandUI } + if chatBottomBar != def.chatBottomBar { empty.chatBottomBar = chatBottomBar } + return empty + } + + static var defaults: AppSettings { + AppSettings ( + networkConfig: NetCfg.defaults, + networkProxy: NetworkProxy.def, + privacyEncryptLocalFiles: true, + privacyAskToApproveRelays: true, + privacyAcceptImages: true, + privacyLinkPreviews: true, + privacyShowChatPreviews: true, + privacySaveLastDraft: true, + privacyProtectScreen: false, + privacyMediaBlurRadius: 0, + notificationMode: AppSettingsNotificationMode.instant, + notificationPreviewMode: NotificationPreviewMode.message, + webrtcPolicyRelay: true, + webrtcICEServers: [], + confirmRemoteSessions: false, + connectRemoteViaMulticast: true, + connectRemoteViaMulticastAuto: true, + developerTools: false, + confirmDBUpgrades: false, + androidCallOnLockScreen: AppSettingsLockScreenCalls.show, + iosCallKitEnabled: true, + iosCallKitCallsInRecents: false, + uiProfileImageCornerRadius: 22.5, + uiChatItemRoundness: 0.75, + uiChatItemTail: true, + uiColorScheme: DefaultTheme.SYSTEM_THEME_NAME, + uiDarkColorScheme: DefaultTheme.SIMPLEX.themeName, + uiCurrentThemeIds: nil as [String: String]?, + uiThemes: nil as [ThemeOverrides]?, + oneHandUI: true, + chatBottomBar: true + ) + } +} + +enum AppSettingsNotificationMode: String, Codable { + case off + case periodic + case instant + + func toNotificationsMode() -> NotificationsMode { + switch self { + case .instant: .instant + case .periodic: .periodic + case .off: .off + } + } + + static func from(_ mode: NotificationsMode) -> AppSettingsNotificationMode { + switch mode { + case .instant: .instant + case .periodic: .periodic + case .off: .off + } + } +} + +//enum NotificationPreviewMode: Codable { +// case hidden +// case contact +// case message +//} + +enum AppSettingsLockScreenCalls: String, Codable { + case disable + case show + case accept +} + +struct UserNetworkInfo: Codable, Equatable { + let networkType: UserNetworkType + let online: Bool +} + +enum UserNetworkType: String, Codable { + case none + case cellular + case wifi + case ethernet + case other + + var text: LocalizedStringKey { + switch self { + case .none: "No network connection" + case .cellular: "Cellular" + case .wifi: "WiFi" + case .ethernet: "Wired ethernet" + case .other: "Other" + } + } +} + +struct RcvMsgInfo: Codable { + var msgId: Int64 + var msgDeliveryId: Int64 + var msgDeliveryStatus: String + var agentMsgId: Int64 + var agentMsgMeta: String +} + +struct ServerQueueInfo: Codable { + var server: String + var rcvId: String + var sndId: String + var ntfId: String? + var status: String + var info: QueueInfo +} + +struct QueueInfo: Codable { + var qiSnd: Bool + var qiNtf: Bool + var qiSub: QSub? + var qiSize: Int + var qiMsg: MsgInfo? +} + +struct QSub: Codable { + var qSubThread: QSubThread + var qDelivered: String? +} + +enum QSubThread: String, Codable { + case noSub + case subPending + case subThread + case prohibitSub +} + +struct MsgInfo: Codable { + var msgId: String + var msgTs: Date + var msgType: MsgType +} + +enum MsgType: String, Codable { + case message + case quota +} + +struct PresentedServersSummary: Codable { + var statsStartedAt: Date + var allUsersSMP: SMPServersSummary + var allUsersXFTP: XFTPServersSummary + var currentUserSMP: SMPServersSummary + var currentUserXFTP: XFTPServersSummary +} + +struct SMPServersSummary: Codable { + var smpTotals: SMPTotals + var currentlyUsedSMPServers: [SMPServerSummary] + var previouslyUsedSMPServers: [SMPServerSummary] + var onlyProxiedSMPServers: [SMPServerSummary] +} + +struct SMPTotals: Codable { + var sessions: ServerSessions + var subs: SMPServerSubs + var stats: AgentSMPServerStatsData +} + +struct SMPServerSummary: Codable, Identifiable { + var smpServer: String + var known: Bool? + var sessions: ServerSessions? + var subs: SMPServerSubs? + var stats: AgentSMPServerStatsData? + + var id: String { smpServer } + + var hasSubs: Bool { subs != nil } + + var sessionsOrNew: ServerSessions { sessions ?? ServerSessions.newServerSessions } + + var subsOrNew: SMPServerSubs { subs ?? SMPServerSubs.newSMPServerSubs } +} + +struct ServerSessions: Codable { + var ssConnected: Int + var ssErrors: Int + var ssConnecting: Int + + static var newServerSessions = ServerSessions( + ssConnected: 0, + ssErrors: 0, + ssConnecting: 0 + ) + + var hasSess: Bool { ssConnected > 0 } +} + +struct SMPServerSubs: Codable { + var ssActive: Int + var ssPending: Int + + static var newSMPServerSubs = SMPServerSubs( + ssActive: 0, + ssPending: 0 + ) + + var total: Int { ssActive + ssPending } + + var shareOfActive: Double { + guard total != 0 else { return 0.0 } + return Double(ssActive) / Double(total) + } +} + +struct AgentSMPServerStatsData: Codable { + var _sentDirect: Int + var _sentViaProxy: Int + var _sentProxied: Int + var _sentDirectAttempts: Int + var _sentViaProxyAttempts: Int + var _sentProxiedAttempts: Int + var _sentAuthErrs: Int + var _sentQuotaErrs: Int + var _sentExpiredErrs: Int + var _sentOtherErrs: Int + var _recvMsgs: Int + var _recvDuplicates: Int + var _recvCryptoErrs: Int + var _recvErrs: Int + var _ackMsgs: Int + var _ackAttempts: Int + var _ackNoMsgErrs: Int + var _ackOtherErrs: Int + var _connCreated: Int + var _connSecured: Int + var _connCompleted: Int + var _connDeleted: Int + var _connDelAttempts: Int + var _connDelErrs: Int + var _connSubscribed: Int + var _connSubAttempts: Int + var _connSubIgnored: Int + var _connSubErrs: Int + var _ntfKey: Int + var _ntfKeyAttempts: Int + var _ntfKeyDeleted: Int + var _ntfKeyDeleteAttempts: Int +} + +struct XFTPServersSummary: Codable { + var xftpTotals: XFTPTotals + var currentlyUsedXFTPServers: [XFTPServerSummary] + var previouslyUsedXFTPServers: [XFTPServerSummary] +} + +struct XFTPTotals: Codable { + var sessions: ServerSessions + var stats: AgentXFTPServerStatsData +} + +struct XFTPServerSummary: Codable, Identifiable { + var xftpServer: String + var known: Bool? + var sessions: ServerSessions? + var stats: AgentXFTPServerStatsData? + var rcvInProgress: Bool + var sndInProgress: Bool + var delInProgress: Bool + + var id: String { xftpServer } +} + +struct AgentXFTPServerStatsData: Codable { + var _uploads: Int + var _uploadsSize: Int64 + var _uploadAttempts: Int + var _uploadErrs: Int + var _downloads: Int + var _downloadsSize: Int64 + var _downloadAttempts: Int + var _downloadAuthErrs: Int + var _downloadErrs: Int + var _deletions: Int + var _deleteAttempts: Int + var _deleteErrs: Int +} + +struct AgentNtfServerStatsData: Codable { + var _ntfCreated: Int + var _ntfCreateAttempts: Int + var _ntfChecked: Int + var _ntfCheckAttempts: Int + var _ntfDeleted: Int + var _ntfDelAttempts: Int +} diff --git a/apps/ios/Shared/Model/AudioRecPlay.swift b/apps/ios/Shared/Model/AudioRecPlay.swift index a9d0d6c1d9..99851f4be8 100644 --- a/apps/ios/Shared/Model/AudioRecPlay.swift +++ b/apps/ios/Shared/Model/AudioRecPlay.swift @@ -179,7 +179,7 @@ class AudioPlayer: NSObject, AVAudioPlayerDelegate { if playback { if AVAudioSession.sharedInstance().category != .playback { logger.log("AudioSession: playback") - try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: .duckOthers) + try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: [.duckOthers, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP]) } } else { if AVAudioSession.sharedInstance().category != .soloAmbient { diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 462699e407..9b9fda0397 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -30,9 +30,18 @@ actor TerminalItems { } } - func addCommand(_ start: Date, _ cmd: ChatCommand, _ resp: ChatResponse) async { + func addCommand(_ start: Date, _ cmd: ChatCommand, _ res: APIResult) async { await add(.cmd(start, cmd)) - await add(.resp(.now, resp)) + await addResult(res) + } + + func addResult(_ res: APIResult) async { + let item: TerminalItem = switch res { + case let .result(r): .res(.now, r) + case let .error(e): .err(.now, e) + case let .invalid(type, json): .bad(.now, type, json) + } + await add(item) } } @@ -43,11 +52,224 @@ private func addTermItem(_ items: inout [TerminalItem], _ item: TerminalItem) { items.append(item) } +class ItemsModel: ObservableObject { + static let shared = ItemsModel() + private let publisher = ObservableObjectPublisher() + private var bag = Set() + var reversedChatItems: [ChatItem] = [] { + willSet { publisher.send() } + } + var itemAdded = false { + willSet { publisher.send() } + } + + let chatState = ActiveChatState() + + // Publishes directly to `objectWillChange` publisher, + // this will cause reversedChatItems to be rendered without throttling + @Published var isLoading = false + @Published var showLoadingProgress: ChatId? = nil + + private var navigationTimeoutTask: Task? = nil + private var loadChatTask: Task? = nil + + var lastItemsLoaded: Bool { + chatState.splits.isEmpty || chatState.splits.first != reversedChatItems.first?.id + } + + init() { + publisher + .throttle(for: 0.2, scheduler: DispatchQueue.main, latest: true) + .sink { self.objectWillChange.send() } + .store(in: &bag) + } + + func loadOpenChat(_ chatId: ChatId, willNavigate: @escaping () -> Void = {}) { + navigationTimeoutTask?.cancel() + loadChatTask?.cancel() + navigationTimeoutTask = Task { + do { + try await Task.sleep(nanoseconds: 250_000000) + await MainActor.run { + ChatModel.shared.chatId = chatId + willNavigate() + } + } catch {} + } + loadChatTask = Task { + await MainActor.run { self.isLoading = true } +// try? await Task.sleep(nanoseconds: 1000_000000) + await loadChat(chatId: chatId) + if !Task.isCancelled { + await MainActor.run { + self.isLoading = false + self.showLoadingProgress = nil + } + } + } + } + + func loadOpenChatNoWait(_ chatId: ChatId, _ openAroundItemId: ChatItem.ID? = nil) { + navigationTimeoutTask?.cancel() + loadChatTask?.cancel() + loadChatTask = Task { + // try? await Task.sleep(nanoseconds: 1000_000000) + await loadChat(chatId: chatId, openAroundItemId: openAroundItemId, clearItems: openAroundItemId == nil) + if !Task.isCancelled { + await MainActor.run { + if openAroundItemId == nil { + ChatModel.shared.chatId = chatId + } + } + } + } + } +} + +class ChatTagsModel: ObservableObject { + static let shared = ChatTagsModel() + + @Published var userTags: [ChatTag] = [] + @Published var activeFilter: ActiveFilter? = nil + @Published var presetTags: [PresetTag:Int] = [:] + @Published var unreadTags: [Int64:Int] = [:] + + func updateChatTags(_ chats: [Chat]) { + let tm = ChatTagsModel.shared + var newPresetTags: [PresetTag:Int] = [:] + var newUnreadTags: [Int64:Int] = [:] + for chat in chats { + for tag in PresetTag.allCases { + if presetTagMatchesChat(tag, chat.chatInfo, chat.chatStats) { + newPresetTags[tag] = (newPresetTags[tag] ?? 0) + 1 + } + } + if chat.unreadTag, let tags = chat.chatInfo.chatTags { + for tag in tags { + newUnreadTags[tag] = (newUnreadTags[tag] ?? 0) + 1 + } + } + } + presetTags = newPresetTags + unreadTags = newUnreadTags + clearActiveChatFilterIfNeeded() + } + + func updateChatFavorite(favorite: Bool, wasFavorite: Bool) { + let count = presetTags[.favorites] + if favorite && !wasFavorite { + presetTags[.favorites] = (count ?? 0) + 1 + } else if !favorite && wasFavorite, let count { + presetTags[.favorites] = max(0, count - 1) + clearActiveChatFilterIfNeeded() + } + } + + func addPresetChatTags(_ chatInfo: ChatInfo, _ chatStats: ChatStats) { + for tag in PresetTag.allCases { + if presetTagMatchesChat(tag, chatInfo, chatStats) { + presetTags[tag] = (presetTags[tag] ?? 0) + 1 + } + } + } + + func removePresetChatTags(_ chatInfo: ChatInfo, _ chatStats: ChatStats) { + for tag in PresetTag.allCases { + if presetTagMatchesChat(tag, chatInfo, chatStats) { + if let count = presetTags[tag] { + if count > 1 { + presetTags[tag] = count - 1 + } else { + presetTags.removeValue(forKey: tag) + } + } + } + } + clearActiveChatFilterIfNeeded() + } + + func markChatTagRead(_ chat: Chat) -> Void { + if chat.unreadTag, let tags = chat.chatInfo.chatTags { + decTagsReadCount(tags) + } + } + + func updateChatTagRead(_ chat: Chat, wasUnread: Bool) -> Void { + guard let tags = chat.chatInfo.chatTags else { return } + let nowUnread = chat.unreadTag + if nowUnread && !wasUnread { + for tag in tags { + unreadTags[tag] = (unreadTags[tag] ?? 0) + 1 + } + } else if !nowUnread && wasUnread { + decTagsReadCount(tags) + } + } + + func decTagsReadCount(_ tags: [Int64]) -> Void { + for tag in tags { + if let count = unreadTags[tag] { + unreadTags[tag] = max(0, count - 1) + } + } + } + + func changeGroupReportsTag(_ by: Int = 0) { + if by == 0 { return } + presetTags[.groupReports] = max(0, (presetTags[.groupReports] ?? 0) + by) + clearActiveChatFilterIfNeeded() + } + + func clearActiveChatFilterIfNeeded() { + let clear = switch activeFilter { + case let .presetTag(tag): (presetTags[tag] ?? 0) == 0 + case let .userTag(tag): !userTags.contains(tag) + case .unread, nil: false + } + if clear { activeFilter = nil } + } +} + +class NetworkModel: ObservableObject { + // map of connections network statuses, key is agent connection id + @Published var networkStatuses: Dictionary = [:] + + static let shared = NetworkModel() + + private init() { } + + func setContactNetworkStatus(_ contact: Contact, _ status: NetworkStatus) { + if let conn = contact.activeConn { + networkStatuses[conn.agentConnId] = status + } + } + + func contactNetworkStatus(_ contact: Contact) -> NetworkStatus { + if let conn = contact.activeConn { + networkStatuses[conn.agentConnId] ?? .unknown + } else { + .unknown + } + } +} + +/// ChatItemWithMenu can depend on previous or next item for it's appearance +/// This dummy model is used to force an update of all chat items, +/// when they might have changed appearance. +class ChatItemDummyModel: ObservableObject { + static let shared = ChatItemDummyModel() + func sendUpdate() { objectWillChange.send() } +} + final class ChatModel: ObservableObject { @Published var onboardingStage: OnboardingStage? @Published var setDeliveryReceipts = false @Published var v3DBMigration: V3DBMigrationState = v3DBMigrationDefault.get() - @Published var currentUser: User? + @Published var currentUser: User? { + didSet { + ThemeManager.applyTheme(currentThemeDefault.get()) + } + } @Published var users: [UserInfo] = [] @Published var chatInitialized = false @Published var chatRunning: Bool? @@ -55,20 +277,21 @@ final class ChatModel: ObservableObject { @Published var chatDbEncrypted: Bool? @Published var chatDbStatus: DBMigrationResult? @Published var ctrlInitInProgress: Bool = false + @Published var notificationResponse: UNNotificationResponse? // local authentication @Published var contentViewAccessAuthenticated: Bool = false @Published var laRequest: LocalAuthRequest? // list of chat "previews" - @Published var chats: [Chat] = [] + @Published private(set) var chats: [Chat] = [] @Published var deletedChats: Set = [] - // map of connections network statuses, key is agent connection id - @Published var networkStatuses: Dictionary = [:] // current chat @Published var chatId: String? - @Published var reversedChatItems: [ChatItem] = [] + @Published var openAroundItemId: ChatItem.ID? = nil var chatItemStatuses: Dictionary = [:] @Published var chatToTop: String? @Published var groupMembers: [GMember] = [] + @Published var groupMembersIndexes: Dictionary = [:] // groupMemberId to index in groupMembers list + @Published var membersLoaded = false // items in the terminal view @Published var showingTerminal = false @Published var terminalItems: [TerminalItem] = [] @@ -78,6 +301,7 @@ final class ChatModel: ObservableObject { @Published var deviceToken: DeviceToken? @Published var savedToken: DeviceToken? @Published var tokenRegistered = false + @Published var reRegisterTknStatus: NtfTknStatus? = nil @Published var tokenStatus: NtfTknStatus? @Published var notificationMode = NotificationsMode.off @Published var notificationServer: String? @@ -100,9 +324,9 @@ final class ChatModel: ObservableObject { @Published var stopPreviousRecPlay: URL? = nil // coordinates currently playing source @Published var draft: ComposeState? @Published var draftChatId: String? - // tracks keyboard height via subscription in AppDelegate - @Published var keyboardHeight: CGFloat = 0 - @Published var pasteboardHasStrings: Bool = UIPasteboard.general.hasStrings + @Published var networkInfo = UserNetworkInfo(networkType: .other, online: true) + // usage conditions + @Published var conditions: ServerOperatorConditions = .empty var messageDelivery: Dictionary Void> = [:] @@ -110,6 +334,8 @@ final class ChatModel: ObservableObject { static let shared = ChatModel() + let im = ItemsModel.shared + static var ok: Bool { ChatModel.shared.chatDbStatus == .ok } let ntfEnableLocal = true @@ -175,18 +401,47 @@ final class ChatModel: ObservableObject { } } + func populateGroupMembersIndexes() { + groupMembersIndexes.removeAll() + for (i, member) in groupMembers.enumerated() { + groupMembersIndexes[member.groupMemberId] = i + } + } + func getGroupMember(_ groupMemberId: Int64) -> GMember? { - groupMembers.first { $0.groupMemberId == groupMemberId } + if let i = groupMembersIndexes[groupMemberId] { + return groupMembers[i] + } + return nil + } + + func loadGroupMembers(_ groupInfo: GroupInfo, updateView: @escaping () -> Void = {}) async { + let groupMembers = await apiListMembers(groupInfo.groupId) + await MainActor.run { + if chatId == groupInfo.id { + self.groupMembers = groupMembers.map { GMember.init($0) } + self.populateGroupMembersIndexes() + self.membersLoaded = true + updateView() + } + } } private func getChatIndex(_ id: String) -> Int? { chats.firstIndex(where: { $0.id == id }) } - func addChat(_ chat: Chat, at position: Int = 0) { - withAnimation { - chats.insert(chat, at: position) + func addChat(_ chat: Chat) { + if chatId == nil { + withAnimation { addChat_(chat, at: 0) } + } else { + addChat_(chat, at: 0) } + popChatCollector.throttlePopChat(chat.chatInfo.id, currentPosition: 0) + } + + func addChat_(_ chat: Chat, at position: Int = 0) { + chats.insert(chat, at: position) } func updateChatInfo(_ cInfo: ChatInfo) { @@ -221,6 +476,7 @@ final class ChatModel: ObservableObject { updateChatInfo(cInfo) } else if addMissing { addChat(Chat(chatInfo: cInfo, chatItems: [])) + ChatTagsModel.shared.addPresetChatTags(cInfo, ChatStats()) } } @@ -244,26 +500,10 @@ final class ChatModel: ObservableObject { } } - func updateChats(with newChats: [ChatData]) { - for i in 0.. 0 { - if chatId == nil { - withAnimation { popChat_(i) } - } else if chatId == cInfo.id { - chatToTop = cInfo.id - } else { - popChat_(i) - } + unreadCollector.changeUnreadCounter(cInfo.id, by: 1, unreadMentions: cItem.meta.userMention ? 1 : 0) } + popChatCollector.throttlePopChat(cInfo.id, currentPosition: i) } else { addChat(Chat(chatInfo: cInfo, chatItems: [cItem])) } @@ -314,7 +551,7 @@ final class ChatModel: ObservableObject { var res: Bool if let chat = getChat(cInfo.id) { if let pItem = chat.chatItems.last { - if pItem.id == cItem.id || (chatId == cInfo.id && reversedChatItems.first(where: { $0.id == cItem.id }) == nil) { + if pItem.id == cItem.id || (chatId == cInfo.id && im.reversedChatItems.first(where: { $0.id == cItem.id }) == nil) { chat.chatItems = [cItem] } } else { @@ -325,24 +562,27 @@ final class ChatModel: ObservableObject { addChat(Chat(chatInfo: cInfo, chatItems: [cItem])) res = true } + if cItem.isDeletedContent || cItem.meta.itemDeleted != nil { + VoiceItemState.stopVoiceInChatView(cInfo, cItem) + } // update current chat return chatId == cInfo.id ? _upsertChatItem(cInfo, cItem) : res } private func _upsertChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) -> Bool { if let i = getChatItemIndex(cItem) { - withAnimation { - _updateChatItem(at: i, with: cItem) - } + _updateChatItem(at: i, with: cItem) + ChatItemDummyModel.shared.sendUpdate() return false } else { - withAnimation(itemAnimation()) { - var ci = cItem - if let status = chatItemStatuses.removeValue(forKey: ci.id), case .sndNew = ci.meta.itemStatus { - ci.meta.itemStatus = status - } - reversedChatItems.insert(ci, at: hasLiveDummy ? 1 : 0) + var ci = cItem + if let status = chatItemStatuses.removeValue(forKey: ci.id), case .sndNew = ci.meta.itemStatus { + ci.meta.itemStatus = status } + im.reversedChatItems.insert(ci, at: hasLiveDummy ? 1 : 0) + im.chatState.itemAdded((ci.id, ci.isRcvNew), hasLiveDummy ? 1 : 0) + im.itemAdded = true + ChatItemDummyModel.shared.sendUpdate() return true } @@ -356,7 +596,7 @@ final class ChatModel: ObservableObject { func updateChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem, status: CIStatus? = nil) { if chatId == cInfo.id, let i = getChatItemIndex(cItem) { - withAnimation { + withConditionalAnimation { _updateChatItem(at: i, with: cItem) } } else if let status = status { @@ -365,17 +605,17 @@ final class ChatModel: ObservableObject { } private func _updateChatItem(at i: Int, with cItem: ChatItem) { - reversedChatItems[i] = cItem - reversedChatItems[i].viewTimestamp = .now + im.reversedChatItems[i] = cItem + im.reversedChatItems[i].viewTimestamp = .now } func getChatItemIndex(_ cItem: ChatItem) -> Int? { - reversedChatItems.firstIndex(where: { $0.id == cItem.id }) + im.reversedChatItems.firstIndex(where: { $0.id == cItem.id }) } func removeChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) { if cItem.isRcvNew { - decreaseUnreadCounter(cInfo) + unreadCollector.changeUnreadCounter(cInfo.id, by: -1, unreadMentions: cItem.meta.userMention ? -1 : 0) } // update previews if let chat = getChat(cInfo.id) { @@ -386,24 +626,65 @@ final class ChatModel: ObservableObject { // remove from current chat if chatId == cInfo.id { if let i = getChatItemIndex(cItem) { - _ = withAnimation { - self.reversedChatItems.remove(at: i) + withAnimation { + let item = im.reversedChatItems.remove(at: i) + im.chatState.itemsRemoved([(item.id, i, item.isRcvNew)], im.reversedChatItems.reversed()) } } } + VoiceItemState.stopVoiceInChatView(cInfo, cItem) + } + + func removeMemberItems(_ removedMember: GroupMember, byMember: GroupMember, _ groupInfo: GroupInfo) { + // this should not happen, only another member can "remove" user, user can only "leave" (another event). + if byMember.groupMemberId == groupInfo.membership.groupMemberId { + logger.debug("exiting removeMemberItems") + return + } + if chatId == groupInfo.id { + for i in 0.. 0, + let updatedItem = removedUpdatedItem(chat.chatItems[0]) { + chat.chatItems = [updatedItem] + } + + func removedUpdatedItem(_ item: ChatItem) -> ChatItem? { + let newContent: CIContent + if case .groupSnd = item.chatDir, removedMember.groupMemberId == groupInfo.membership.groupMemberId { + newContent = .sndModerated + } else if case let .groupRcv(groupMember) = item.chatDir, groupMember.groupMemberId == removedMember.groupMemberId { + newContent = .rcvModerated + } else { + return nil + } + var updatedItem = item + updatedItem.meta.itemDeleted = .moderated(deletedTs: Date.now, byGroupMember: byMember) + if groupInfo.fullGroupPreferences.fullDelete.on { + updatedItem.content = newContent + } + if item.isActiveReport { + decreaseGroupReportsCounter(groupInfo.id) + } + return updatedItem + } } func nextChatItemData(_ chatItemId: Int64, previous: Bool, map: @escaping (ChatItem) -> T?) -> T? { - guard var i = reversedChatItems.firstIndex(where: { $0.id == chatItemId }) else { return nil } + guard var i = im.reversedChatItems.firstIndex(where: { $0.id == chatItemId }) else { return nil } if previous { - while i < reversedChatItems.count - 1 { + while i < im.reversedChatItems.count - 1 { i += 1 - if let res = map(reversedChatItems[i]) { return res } + if let res = map(im.reversedChatItems[i]) { return res } } } else { while i > 0 { i -= 1 - if let res = map(reversedChatItems[i]) { return res } + if let res = map(im.reversedChatItems[i]) { return res } } } return nil @@ -421,10 +702,22 @@ final class ChatModel: ObservableObject { } } + func updateCurrentUserUiThemes(uiThemes: ThemeModeOverrides?) { + guard var current = currentUser, current.uiThemes != uiThemes else { return } + current.uiThemes = uiThemes + let i = users.firstIndex(where: { $0.user.userId == current.userId }) + if let i { + users[i].user = current + } + currentUser = current + } + func addLiveDummy(_ chatInfo: ChatInfo) -> ChatItem { let cItem = ChatItem.liveDummy(chatInfo.chatType) withAnimation { - reversedChatItems.insert(cItem, at: 0) + im.reversedChatItems.insert(cItem, at: 0) + im.chatState.itemAdded((cItem.id, cItem.isRcvNew), 0) + im.itemAdded = true } return cItem } @@ -432,130 +725,247 @@ final class ChatModel: ObservableObject { func removeLiveDummy(animated: Bool = true) { if hasLiveDummy { if animated { - withAnimation { _ = reversedChatItems.removeFirst() } + withAnimation { _ = im.reversedChatItems.removeFirst() } } else { - _ = reversedChatItems.removeFirst() + _ = im.reversedChatItems.removeFirst() } } } private var hasLiveDummy: Bool { - reversedChatItems.first?.isLiveDummy == true + im.reversedChatItems.first?.isLiveDummy == true } - func markChatItemsRead(_ cInfo: ChatInfo) { + func markAllChatItemsRead(_ cInfo: ChatInfo) { // update preview _updateChat(cInfo.id) { chat in - self.decreaseUnreadCounter(user: self.currentUser!, by: chat.chatStats.unreadCount) + self.decreaseUnreadCounter(user: self.currentUser!, chat: chat) + ChatTagsModel.shared.markChatTagRead(chat) chat.chatStats = ChatStats() } // update current chat if chatId == cInfo.id { - markCurrentChatRead() - } - } - - private func markCurrentChatRead(fromIndex i: Int = 0) { - var j = i - while j < reversedChatItems.count { - markChatItemRead_(j) - j += 1 - } - } - - func markChatItemsRead(_ cInfo: ChatInfo, aboveItem: ChatItem? = nil) { - if let cItem = aboveItem { - if chatId == cInfo.id, let i = getChatItemIndex(cItem) { - markCurrentChatRead(fromIndex: i) - _updateChat(cInfo.id) { chat in - var unreadBelow = 0 - var j = i - 1 - while j >= 0 { - if case .rcvNew = self.reversedChatItems[j].meta.itemStatus { - unreadBelow += 1 - } - j -= 1 - } - // update preview - let markedCount = chat.chatStats.unreadCount - unreadBelow - if markedCount > 0 { - chat.chatStats.unreadCount -= markedCount - self.decreaseUnreadCounter(user: self.currentUser!, by: markedCount) - } - } + var i = 0 + while i < im.reversedChatItems.count { + markChatItemRead_(i) + i += 1 } - } else { - markChatItemsRead(cInfo) + im.chatState.itemsRead(nil, im.reversedChatItems.reversed()) } } - func markChatUnread(_ cInfo: ChatInfo, unreadChat: Bool = true) { _updateChat(cInfo.id) { chat in + let wasUnread = chat.unreadTag chat.chatStats.unreadChat = unreadChat + ChatTagsModel.shared.updateChatTagRead(chat, wasUnread: wasUnread) } } func clearChat(_ cInfo: ChatInfo) { // clear preview if let chat = getChat(cInfo.id) { - self.decreaseUnreadCounter(user: self.currentUser!, by: chat.chatStats.unreadCount) + self.decreaseUnreadCounter(user: self.currentUser!, chat: chat) chat.chatItems = [] + ChatTagsModel.shared.markChatTagRead(chat) chat.chatStats = ChatStats() chat.chatInfo = cInfo } // clear current chat if chatId == cInfo.id { chatItemStatuses = [:] - reversedChatItems = [] + im.reversedChatItems = [] + im.chatState.clear() } } - func markChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) { - // update preview - decreaseUnreadCounter(cInfo) - // update current chat - if chatId == cInfo.id, let i = getChatItemIndex(cItem) { - markChatItemRead_(i) + func markChatItemsRead(_ cInfo: ChatInfo, _ itemIds: [ChatItem.ID], _ mentionsRead: Int) { + if self.chatId == cInfo.id { + var unreadItemIds: Set = [] + var i = 0 + var ids = Set(itemIds) + while i < im.reversedChatItems.count && !ids.isEmpty { + let item = im.reversedChatItems[i] + if ids.contains(item.id) && item.isRcvNew { + markChatItemRead_(i) + unreadItemIds.insert(item.id) + ids.remove(item.id) + } + i += 1 + } + im.chatState.itemsRead(unreadItemIds, im.reversedChatItems.reversed()) + } + self.unreadCollector.changeUnreadCounter(cInfo.id, by: -itemIds.count, unreadMentions: -mentionsRead) + } + + private let unreadCollector = UnreadCollector() + + class UnreadCollector { + private let subject = PassthroughSubject() + private var bag = Set() + private var unreadCounts: [ChatId: (unread: Int, mentions: Int)] = [:] + + init() { + subject + .debounce(for: 1, scheduler: DispatchQueue.main) + .sink { + let m = ChatModel.shared + for (chatId, (unread, mentions)) in self.unreadCounts { + if unread != 0 || mentions != 0, let i = m.getChatIndex(chatId) { + m.changeUnreadCounter(i, by: unread, unreadMentions: mentions) + } + } + self.unreadCounts = [:] + } + .store(in: &bag) + } + + func changeUnreadCounter(_ chatId: ChatId, by count: Int, unreadMentions: Int) { + let (unread, mentions) = self.unreadCounts[chatId] ?? (0, 0) + self.unreadCounts[chatId] = (unread + count, mentions + unreadMentions) + subject.send() + } + } + + let popChatCollector = PopChatCollector() + + class PopChatCollector { + private let subject = PassthroughSubject() + private var bag = Set() + private var chatsToPop: [ChatId: Date] = [:] + private let popTsComparator = KeyPathComparator(\.popTs, order: .reverse) + + init() { + subject + .throttle(for: 2, scheduler: DispatchQueue.main, latest: true) + .sink { self.popCollectedChats() } + .store(in: &bag) + } + + func throttlePopChat(_ chatId: ChatId, currentPosition: Int) { + let m = ChatModel.shared + if currentPosition > 0 && m.chatId == chatId { + m.chatToTop = chatId + } + if currentPosition > 0 || !chatsToPop.isEmpty { + chatsToPop[chatId] = Date.now + subject.send() + } + } + + func clear() { + chatsToPop = [:] + } + + func popCollectedChats() { + let m = ChatModel.shared + var ixs: IndexSet = [] + var chs: [Chat] = [] + // collect chats that received updates + for (chatId, popTs) in self.chatsToPop { + // Currently opened chat is excluded, removing it from the list would navigate out of it + // It will be popped to top later when user exits from the list. + if m.chatId != chatId, let i = m.getChatIndex(chatId) { + ixs.insert(i) + let ch = m.chats[i] + ch.popTs = popTs + chs.append(ch) + } + } + + let removeInsert = { + m.chats.remove(atOffsets: ixs) + // sort chats by pop timestamp in descending order + m.chats.insert(contentsOf: chs.sorted(using: self.popTsComparator), at: 0) + } + + if m.chatId == nil { + withAnimation { removeInsert() } + } else { + removeInsert() + } + + self.chatsToPop = [:] } } private func markChatItemRead_(_ i: Int) { - let meta = reversedChatItems[i].meta + let meta = im.reversedChatItems[i].meta if case .rcvNew = meta.itemStatus { - reversedChatItems[i].meta.itemStatus = .rcvRead - reversedChatItems[i].viewTimestamp = .now + im.reversedChatItems[i].meta.itemStatus = .rcvRead + im.reversedChatItems[i].viewTimestamp = .now if meta.itemLive != true, let ttl = meta.itemTimed?.ttl { - reversedChatItems[i].meta.itemTimed?.deleteAt = .now + TimeInterval(ttl) + im.reversedChatItems[i].meta.itemTimed?.deleteAt = .now + TimeInterval(ttl) } } } - func decreaseUnreadCounter(_ cInfo: ChatInfo) { - if let i = getChatIndex(cInfo.id) { - chats[i].chatStats.unreadCount = chats[i].chatStats.unreadCount - 1 - decreaseUnreadCounter(user: currentUser!) - } + func changeUnreadCounter(_ chatIndex: Int, by count: Int, unreadMentions: Int) { + let wasUnread = chats[chatIndex].unreadTag + let stats = chats[chatIndex].chatStats + chats[chatIndex].chatStats.unreadCount = stats.unreadCount + count + chats[chatIndex].chatStats.unreadMentions = stats.unreadMentions + unreadMentions + ChatTagsModel.shared.updateChatTagRead(chats[chatIndex], wasUnread: wasUnread) + changeUnreadCounter(user: currentUser!, by: count) } func increaseUnreadCounter(user: any UserLike) { changeUnreadCounter(user: user, by: 1) - NtfManager.shared.incNtfBadgeCount() + } + + func decreaseUnreadCounter(user: any UserLike, chat: Chat) { + let by = chat.chatInfo.chatSettings?.enableNtfs == .mentions + ? chat.chatStats.unreadMentions + : chat.chatStats.unreadCount + decreaseUnreadCounter(user: user, by: by) } func decreaseUnreadCounter(user: any UserLike, by: Int = 1) { changeUnreadCounter(user: user, by: -by) - NtfManager.shared.decNtfBadgeCount(by: by) } private func changeUnreadCounter(user: any UserLike, by: Int) { if let i = users.firstIndex(where: { $0.user.userId == user.userId }) { users[i].unreadCount += by } + NtfManager.shared.changeNtfBadgeCount(by: by) } func totalUnreadCountForAllUsers() -> Int { - chats.filter { $0.chatInfo.ntfsEnabled }.reduce(0, { count, chat in count + chat.chatStats.unreadCount }) + - users.filter { !$0.user.activeUser }.reduce(0, { unread, next -> Int in unread + next.unreadCount }) + var unread: Int = 0 + for chat in chats { + switch chat.chatInfo.chatSettings?.enableNtfs { + case .all: unread += chat.chatStats.unreadCount + case .mentions: unread += chat.chatStats.unreadMentions + default: () + } + } + for u in users { + if !u.user.activeUser { + unread += u.unreadCount + } + } + return unread + } + + func increaseGroupReportsCounter(_ chatId: ChatId) { + changeGroupReportsCounter(chatId, 1) + } + + func decreaseGroupReportsCounter(_ chatId: ChatId, by: Int = 1) { + changeGroupReportsCounter(chatId, -by) + } + + private func changeGroupReportsCounter(_ chatId: ChatId, _ by: Int = 0) { + if by == 0 { return } + + if let i = getChatIndex(chatId) { + let chat = chats[i] + let wasReportsCount = chat.chatStats.reportsCount + chat.chatStats.reportsCount = max(0, chat.chatStats.reportsCount + by) + let nowReportsCount = chat.chatStats.reportsCount + let by = wasReportsCount == 0 && nowReportsCount > 0 ? 1 : (wasReportsCount > 0 && nowReportsCount == 0) ? -1 : 0 + ChatTagsModel.shared.changeGroupReportsTag(by) + } } // this function analyses "connected" events and assumes that each member will be there only once @@ -564,8 +974,8 @@ final class ChatModel: ObservableObject { var ns: [String] = [] if let ciCategory = chatItem.mergeCategory, var i = getChatItemIndex(chatItem) { - while i < reversedChatItems.count { - let ci = reversedChatItems[i] + while i < im.reversedChatItems.count { + let ci = im.reversedChatItems[i] if ci.mergeCategory != ciCategory { break } if let m = ci.memberConnected { ns.append(m.displayName) @@ -580,7 +990,7 @@ final class ChatModel: ObservableObject { // returns the index of the passed item and the next item (it has smaller index) func getNextChatItem(_ ci: ChatItem) -> (Int?, ChatItem?) { if let i = getChatItemIndex(ci) { - (i, i > 0 ? reversedChatItems[i - 1] : nil) + (i, i > 0 ? im.reversedChatItems[i - 1] : nil) } else { (nil, nil) } @@ -590,10 +1000,10 @@ final class ChatModel: ObservableObject { // and the previous visible item with another merge category func getPrevShownChatItem(_ ciIndex: Int?, _ ciCategory: CIMergeCategory?) -> (Int?, ChatItem?) { guard var i = ciIndex else { return (nil, nil) } - let fst = reversedChatItems.count - 1 + let fst = im.reversedChatItems.count - 1 while i < fst { i = i + 1 - let ci = reversedChatItems[i] + let ci = im.reversedChatItems[i] if ciCategory == nil || ciCategory != ci.mergeCategory { return (i - 1, ci) } @@ -603,12 +1013,17 @@ final class ChatModel: ObservableObject { // returns the previous member in the same merge group and the count of members in this group func getPrevHiddenMember(_ member: GroupMember, _ range: ClosedRange) -> (GroupMember?, Int) { + let items = im.reversedChatItems var prevMember: GroupMember? = nil var memberIds: Set = [] for i in range { - if case let .groupRcv(m) = reversedChatItems[i].chatDir { - if prevMember == nil && m.groupMemberId != member.groupMemberId { prevMember = m } - memberIds.insert(m.groupMemberId) + if i < items.count { + if case let .groupRcv(m) = items[i].chatDir { + if prevMember == nil && m.groupMemberId != member.groupMemberId { prevMember = m } + memberIds.insert(m.groupMemberId) + } + } else { + logger.error("getPrevHiddenMember: index >= count of reversed items: \(i) vs \(items.count), range: \(String(describing: range))") } } return (prevMember, memberIds.count) @@ -616,6 +1031,7 @@ final class ChatModel: ObservableObject { func popChat(_ id: String) { if let i = getChatIndex(id) { + // no animation here, for it not to look like it just moved when leaving the chat popChat_(i) } } @@ -626,7 +1042,7 @@ final class ChatModel: ObservableObject { } func dismissConnReqView(_ id: String) { - if id == showingInvitation?.connId { + if id == showingInvitation?.pcc.id { markShowingInvitationUsed() dismissAllSheets() } @@ -638,7 +1054,11 @@ final class ChatModel: ObservableObject { func removeChat(_ id: String) { withAnimation { - chats.removeAll(where: { $0.id == id }) + if let i = getChatIndex(id) { + let removed = chats.remove(at: i) + ChatTagsModel.shared.removePresetChatTags(removed.chatInfo, removed.chatStats) + removeWallpaperFilesFromChat(removed) + } } } @@ -650,14 +1070,17 @@ final class ChatModel: ObservableObject { } // update current chat if chatId == groupInfo.id { - if let i = groupMembers.firstIndex(where: { $0.groupMemberId == member.groupMemberId }) { + if let i = groupMembersIndexes[member.groupMemberId] { withAnimation(.default) { self.groupMembers[i].wrapped = member self.groupMembers[i].created = Date.now } return false } else { - withAnimation { groupMembers.append(GMember(member)) } + withAnimation { + groupMembers.append(GMember(member)) + groupMembersIndexes[member.groupMemberId] = groupMembers.count - 1 + } return true } } else { @@ -674,46 +1097,26 @@ final class ChatModel: ObservableObject { } } - func unreadChatItemCounts(itemsInView: Set) -> UnreadChatItemCounts { - var i = 0 - var totalBelow = 0 - var unreadBelow = 0 - while i < reversedChatItems.count - 1 && !itemsInView.contains(reversedChatItems[i].viewId) { - totalBelow += 1 - if reversedChatItems[i].isRcvNew { - unreadBelow += 1 + func removeWallpaperFilesFromChat(_ chat: Chat) { + if case let .direct(contact) = chat.chatInfo { + removeWallpaperFilesFromTheme(contact.uiThemes) + } else if case let .group(groupInfo) = chat.chatInfo { + removeWallpaperFilesFromTheme(groupInfo.uiThemes) + } + } + + func removeWallpaperFilesFromAllChats(_ user: User) { + // Currently, only removing everything from currently active user is supported. Inactive users are TODO + if user.userId == currentUser?.userId { + chats.forEach { + removeWallpaperFilesFromChat($0) } - i += 1 - } - return UnreadChatItemCounts(totalBelow: totalBelow, unreadBelow: unreadBelow) - } - - func topItemInView(itemsInView: Set) -> ChatItem? { - let maxIx = reversedChatItems.count - 1 - var i = 0 - let inView = { itemsInView.contains(self.reversedChatItems[$0].viewId) } - while i < maxIx && !inView(i) { i += 1 } - while i < maxIx && inView(i) { i += 1 } - return reversedChatItems[min(i - 1, maxIx)] - } - - func setContactNetworkStatus(_ contact: Contact, _ status: NetworkStatus) { - if let conn = contact.activeConn { - networkStatuses[conn.agentConnId] = status - } - } - - func contactNetworkStatus(_ contact: Contact) -> NetworkStatus { - if let conn = contact.activeConn { - networkStatuses[conn.agentConnId] ?? .unknown - } else { - .unknown } } } struct ShowingInvitation { - var connId: String + var pcc: PendingContactConnection var connChatUsed: Bool } @@ -722,16 +1125,12 @@ struct NTFContactRequest { var chatId: String } -struct UnreadChatItemCounts { - var totalBelow: Int - var unreadBelow: Int -} - -final class Chat: ObservableObject, Identifiable { +final class Chat: ObservableObject, Identifiable, ChatLike { @Published var chatInfo: ChatInfo @Published var chatItems: [ChatItem] @Published var chatStats: ChatStats var created = Date.now + fileprivate var popTs: Date? init(_ cData: ChatData) { self.chatInfo = cData.chatInfo @@ -753,27 +1152,14 @@ final class Chat: ObservableObject, Identifiable { ) } - var userCanSend: Bool { - switch chatInfo { - case .direct: return true - case let .group(groupInfo): - let m = groupInfo.membership - return m.memberActive && m.memberRole >= .member - case .local: - return true - default: return false + var unreadTag: Bool { + switch chatInfo.chatSettings?.enableNtfs { + case .all: chatStats.unreadChat || chatStats.unreadCount > 0 + case .mentions: chatStats.unreadChat || chatStats.unreadMentions > 0 + default: chatStats.unreadChat } } - - var userIsObserver: Bool { - switch chatInfo { - case let .group(groupInfo): - let m = groupInfo.membership - return m.memberActive && m.memberRole == .observer - default: return false - } - } - + var id: ChatId { get { chatInfo.id } } var viewId: String { get { "\(chatInfo.id) \(created.timeIntervalSince1970)" } } diff --git a/apps/ios/Shared/Model/NetworkObserver.swift b/apps/ios/Shared/Model/NetworkObserver.swift new file mode 100644 index 0000000000..84c35afa07 --- /dev/null +++ b/apps/ios/Shared/Model/NetworkObserver.swift @@ -0,0 +1,73 @@ +// +// NetworkObserver.swift +// SimpleX (iOS) +// +// Created by Avently on 05.04.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import Network +import SimpleXChat + +class NetworkObserver { + static let shared = NetworkObserver() + private let queue: DispatchQueue = DispatchQueue(label: "chat.simplex.app.NetworkObserver") + private var prevInfo: UserNetworkInfo? = nil + private var monitor: NWPathMonitor? + private let monitorLock: DispatchQueue = DispatchQueue(label: "chat.simplex.app.monitorLock") + + func restartMonitor() { + monitorLock.sync { + monitor?.cancel() + let mon = NWPathMonitor() + mon.pathUpdateHandler = { [weak self] path in + self?.networkPathChanged(path: path) + } + mon.start(queue: queue) + monitor = mon + } + } + + private func networkPathChanged(path: NWPath) { + let info = UserNetworkInfo( + networkType: networkTypeFromPath(path), + online: path.status == .satisfied + ) + if (prevInfo != info) { + prevInfo = info + setNetworkInfo(info) + } + } + + private func networkTypeFromPath(_ path: NWPath) -> UserNetworkType { + if path.usesInterfaceType(.wiredEthernet) { + .ethernet + } else if path.usesInterfaceType(.wifi) { + .wifi + } else if path.usesInterfaceType(.cellular) { + .cellular + } else if path.usesInterfaceType(.other) { + .other + } else { + .none + } + } + + private static var networkObserver: NetworkObserver? = nil + + private func setNetworkInfo(_ info: UserNetworkInfo) { + logger.debug("setNetworkInfo Network changed: \(String(describing: info))") + DispatchQueue.main.sync { + ChatModel.shared.networkInfo = info + } + if !hasChatCtrl() { return } + self.monitorLock.sync { + do { + try apiSetNetworkInfo(info) + } catch let err { + logger.error("setNetworkInfo error: \(responseError(err))") + } + } + } +} diff --git a/apps/ios/Shared/Model/NtfManager.swift b/apps/ios/Shared/Model/NtfManager.swift index f1fdcc018e..da55bd90d0 100644 --- a/apps/ios/Shared/Model/NtfManager.swift +++ b/apps/ios/Shared/Model/NtfManager.swift @@ -26,20 +26,37 @@ enum NtfCallAction { class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { static let shared = NtfManager() + public var navigatingToChat = false private var granted = false private var prevNtfTime: Dictionary = [:] + override init() { + super.init() + UNUserNotificationCenter.current().delegate = self + } + // Handle notification when app is in background func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler handler: () -> Void) { logger.debug("NtfManager.userNotificationCenter: didReceive") - let content = response.notification.request.content + if appStateGroupDefault.get() == .active { + processNotificationResponse(response) + } else { + logger.debug("NtfManager.userNotificationCenter: remember response in model") + ChatModel.shared.notificationResponse = response + } + handler() + } + + func processNotificationResponse(_ ntfResponse: UNNotificationResponse) { let chatModel = ChatModel.shared - let action = response.actionIdentifier - logger.debug("NtfManager.userNotificationCenter: didReceive: action \(action), categoryIdentifier \(content.categoryIdentifier)") + let content = ntfResponse.notification.request.content + let action = ntfResponse.actionIdentifier + logger.debug("NtfManager.processNotificationResponse: didReceive: action \(action), categoryIdentifier \(content.categoryIdentifier)") if let userId = content.userInfo["userId"] as? Int64, userId != chatModel.currentUser?.userId { + logger.debug("NtfManager.processNotificationResponse changeActiveUser") changeActiveUser(userId, viewPwd: nil) } if content.categoryIdentifier == ntfCategoryContactRequest && (action == ntfActionAcceptContact || action == ntfActionAcceptContactIncognito), @@ -57,9 +74,13 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { chatModel.ntfCallInvitationAction = (chatId, ntfAction) } } else { - chatModel.chatId = content.targetContentIdentifier + if let chatId = content.targetContentIdentifier { + self.navigatingToChat = true + ItemsModel.shared.loadOpenChat(chatId) { + self.navigatingToChat = false + } + } } - handler() } private func ntfCallAction(_ content: UNNotificationContent, _ action: String) -> (ChatId, NtfCallAction)? { @@ -74,7 +95,6 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { return nil } - // Handle notification when the app is in foreground func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, @@ -183,6 +203,12 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { actions: [], intentIdentifiers: [], hiddenPreviewsBodyPlaceholder: NSLocalizedString("SimpleX encrypted message or connection event", comment: "notification") + ), + UNNotificationCategory( + identifier: ntfCategoryManyEvents, + actions: [], + intentIdentifiers: [], + hiddenPreviewsBodyPlaceholder: NSLocalizedString("New events", comment: "notification") ) ]) } @@ -208,29 +234,28 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { } } } - center.delegate = self } func notifyContactRequest(_ user: any UserLike, _ contactRequest: UserContactRequest) { logger.debug("NtfManager.notifyContactRequest") - addNotification(createContactRequestNtf(user, contactRequest)) + addNotification(createContactRequestNtf(user, contactRequest, 0)) } func notifyContactConnected(_ user: any UserLike, _ contact: Contact) { logger.debug("NtfManager.notifyContactConnected") - addNotification(createContactConnectedNtf(user, contact)) + addNotification(createContactConnectedNtf(user, contact, 0)) } func notifyMessageReceived(_ user: any UserLike, _ cInfo: ChatInfo, _ cItem: ChatItem) { logger.debug("NtfManager.notifyMessageReceived") - if cInfo.ntfsEnabled { - addNotification(createMessageReceivedNtf(user, cInfo, cItem)) + if cInfo.ntfsEnabled(chatItem: cItem) { + addNotification(createMessageReceivedNtf(user, cInfo, cItem, 0)) } } func notifyCallInvitation(_ invitation: RcvCallInvitation) { logger.debug("NtfManager.notifyCallInvitation") - addNotification(createCallInvitationNtf(invitation)) + addNotification(createCallInvitationNtf(invitation, 0)) } func setNtfBadgeCount(_ count: Int) { @@ -238,12 +263,8 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject { ntfBadgeCountGroupDefault.set(count) } - func decNtfBadgeCount(by count: Int = 1) { - setNtfBadgeCount(max(0, UIApplication.shared.applicationIconBadgeNumber - count)) - } - - func incNtfBadgeCount(by count: Int = 1) { - setNtfBadgeCount(UIApplication.shared.applicationIconBadgeNumber + count) + func changeNtfBadgeCount(by count: Int = 1) { + setNtfBadgeCount(max(0, UIApplication.shared.applicationIconBadgeNumber + count)) } private func addNotification(_ content: UNMutableNotificationContent) { diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 365f23c33e..d92411decd 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -11,44 +11,42 @@ import UIKit import Dispatch import BackgroundTasks import SwiftUI -import SimpleXChat +@preconcurrency import SimpleXChat private var chatController: chat_ctrl? -// currentChatVersion in core -public let CURRENT_CHAT_VERSION: Int = 2 - -// version range that supports establishing direct connection with a group member (xGrpDirectInvVRange in core) -public let CREATE_MEMBER_CONTACT_VRANGE = VersionRange(minVersion: 2, maxVersion: CURRENT_CHAT_VERSION) +private let networkStatusesLock = DispatchQueue(label: "chat.simplex.app.network-statuses.lock") enum TerminalItem: Identifiable { case cmd(Date, ChatCommand) - case resp(Date, ChatResponse) + case res(Date, ChatAPIResult) + case err(Date, ChatError) + case bad(Date, String, Data?) var id: Date { - get { - switch self { - case let .cmd(id, _): return id - case let .resp(id, _): return id - } + switch self { + case let .cmd(d, _): d + case let .res(d, _): d + case let .err(d, _): d + case let .bad(d, _, _): d } } var label: String { - get { - switch self { - case let .cmd(_, cmd): return "> \(cmd.cmdString.prefix(30))" - case let .resp(_, resp): return "< \(resp.responseType)" - } + switch self { + case let .cmd(_, cmd): "> \(cmd.cmdString.prefix(30))" + case let .res(_, res): "< \(res.responseType)" + case let .err(_, err): "< error \(err.errorType)" + case let .bad(_, type, _): "< * \(type)" } } var details: String { - get { - switch self { - case let .cmd(_, cmd): return cmd.cmdString - case let .resp(_, resp): return resp.details - } + switch self { + case let .cmd(_, cmd): cmd.cmdString + case let .res(_, res): res.details + case let .err(_, err): String(describing: err) + case let .bad(_, _, json): dataToString(json) } } } @@ -90,51 +88,77 @@ private func withBGTask(bgDelay: Double? = nil, f: @escaping () -> T) -> T { return r } -func chatSendCmdSync(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? = nil, _ ctrl: chat_ctrl? = nil) -> ChatResponse { - logger.debug("chatSendCmd \(cmd.cmdType)") +@inline(__always) +func chatSendCmdSync(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? = nil, ctrl: chat_ctrl? = nil, log: Bool = true) throws -> R { + let res: APIResult = chatApiSendCmdSync(cmd, bgTask: bgTask, bgDelay: bgDelay, ctrl: ctrl, log: log) + return try apiResult(res) +} + +func chatApiSendCmdSync(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? = nil, ctrl: chat_ctrl? = nil, log: Bool = true) -> APIResult { + if log { + logger.debug("chatSendCmd \(cmd.cmdType)") + } let start = Date.now - let resp = bgTask + let resp: APIResult = bgTask ? withBGTask(bgDelay: bgDelay) { sendSimpleXCmd(cmd, ctrl) } : sendSimpleXCmd(cmd, ctrl) - logger.debug("chatSendCmd \(cmd.cmdType): \(resp.responseType)") - if case let .response(_, json) = resp { - logger.debug("chatSendCmd \(cmd.cmdType) response: \(json)") - } - Task { - await TerminalItems.shared.addCommand(start, cmd.obfuscated, resp) + if log { + logger.debug("chatSendCmd \(cmd.cmdType): \(resp.responseType)") + if case let .invalid(_, json) = resp { + logger.debug("chatSendCmd \(cmd.cmdType) response: \(dataToString(json))") + } + Task { + await TerminalItems.shared.addCommand(start, cmd.obfuscated, resp) + } } return resp } -func chatSendCmd(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? = nil, _ ctrl: chat_ctrl? = nil) async -> ChatResponse { +@inline(__always) +func chatSendCmd(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? = nil, ctrl: chat_ctrl? = nil, log: Bool = true) async throws -> R { + let res: APIResult = await chatApiSendCmd(cmd, bgTask: bgTask, bgDelay: bgDelay, ctrl: ctrl, log: log) + return try apiResult(res) +} + +@inline(__always) +func chatApiSendCmd(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? = nil, ctrl: chat_ctrl? = nil, log: Bool = true) async -> APIResult { await withCheckedContinuation { cont in - cont.resume(returning: chatSendCmdSync(cmd, bgTask: bgTask, bgDelay: bgDelay, ctrl)) + cont.resume(returning: chatApiSendCmdSync(cmd, bgTask: bgTask, bgDelay: bgDelay, ctrl: ctrl, log: log)) } } -func chatRecvMsg(_ ctrl: chat_ctrl? = nil) async -> ChatResponse? { +@inline(__always) +func apiResult(_ res: APIResult) throws -> R { + switch res { + case let .result(r): return r + case let .error(e): throw e + case let .invalid(type, _): throw ChatError.unexpectedResult(type: type) + } +} + +func chatRecvMsg(_ ctrl: chat_ctrl? = nil) async -> APIResult? { await withCheckedContinuation { cont in - _ = withBGTask(bgDelay: msgDelay) { () -> ChatResponse? in - let resp = recvSimpleXMsg(ctrl) - cont.resume(returning: resp) - return resp + _ = withBGTask(bgDelay: msgDelay) { () -> APIResult? in + let evt: APIResult? = recvSimpleXMsg(ctrl) + cont.resume(returning: evt) + return evt } } } func apiGetActiveUser(ctrl: chat_ctrl? = nil) throws -> User? { - let r = chatSendCmdSync(.showActiveUser, ctrl) + let r: APIResult = chatApiSendCmdSync(.showActiveUser, ctrl: ctrl) switch r { - case let .activeUser(user): return user - case .chatCmdError(_, .error(.noActiveUser)): return nil - default: throw r + case let .result(.activeUser(user)): return user + case .error(.error(.noActiveUser)): return nil + default: throw r.unexpected } } -func apiCreateActiveUser(_ p: Profile?, sameServers: Bool = false, pastTimestamp: Bool = false, ctrl: chat_ctrl? = nil) throws -> User { - let r = chatSendCmdSync(.createActiveUser(profile: p, sameServers: sameServers, pastTimestamp: pastTimestamp), ctrl) +func apiCreateActiveUser(_ p: Profile?, pastTimestamp: Bool = false, ctrl: chat_ctrl? = nil) throws -> User { + let r: ChatResponse0 = try chatSendCmdSync(.createActiveUser(profile: p, pastTimestamp: pastTimestamp), ctrl: ctrl) if case let .activeUser(user) = r { return user } - throw r + throw r.unexpected } func listUsers() throws -> [UserInfo] { @@ -145,41 +169,35 @@ func listUsersAsync() async throws -> [UserInfo] { return try listUsersResponse(await chatSendCmd(.listUsers)) } -private func listUsersResponse(_ r: ChatResponse) throws -> [UserInfo] { +private func listUsersResponse(_ r: ChatResponse0) throws -> [UserInfo] { if case let .usersList(users) = r { return users.sorted { $0.user.chatViewName.compare($1.user.chatViewName) == .orderedAscending } } - throw r + throw r.unexpected } func apiSetActiveUser(_ userId: Int64, viewPwd: String?) throws -> User { - let r = chatSendCmdSync(.apiSetActiveUser(userId: userId, viewPwd: viewPwd)) + let r: ChatResponse0 = try chatSendCmdSync(.apiSetActiveUser(userId: userId, viewPwd: viewPwd)) if case let .activeUser(user) = r { return user } - throw r + throw r.unexpected } func apiSetActiveUserAsync(_ userId: Int64, viewPwd: String?) async throws -> User { - let r = await chatSendCmd(.apiSetActiveUser(userId: userId, viewPwd: viewPwd)) + let r: ChatResponse0 = try await chatSendCmd(.apiSetActiveUser(userId: userId, viewPwd: viewPwd)) if case let .activeUser(user) = r { return user } - throw r + throw r.unexpected } func apiSetAllContactReceipts(enable: Bool) async throws { - let r = await chatSendCmd(.setAllContactReceipts(enable: enable)) - if case .cmdOk = r { return } - throw r + try await sendCommandOkResp(.setAllContactReceipts(enable: enable)) } func apiSetUserContactReceipts(_ userId: Int64, userMsgReceiptSettings: UserMsgReceiptSettings) async throws { - let r = await chatSendCmd(.apiSetUserContactReceipts(userId: userId, userMsgReceiptSettings: userMsgReceiptSettings)) - if case .cmdOk = r { return } - throw r + try await sendCommandOkResp(.apiSetUserContactReceipts(userId: userId, userMsgReceiptSettings: userMsgReceiptSettings)) } func apiSetUserGroupReceipts(_ userId: Int64, userMsgReceiptSettings: UserMsgReceiptSettings) async throws { - let r = await chatSendCmd(.apiSetUserGroupReceipts(userId: userId, userMsgReceiptSettings: userMsgReceiptSettings)) - if case .cmdOk = r { return } - throw r + try await sendCommandOkResp(.apiSetUserGroupReceipts(userId: userId, userMsgReceiptSettings: userMsgReceiptSettings)) } func apiHideUser(_ userId: Int64, viewPwd: String) async throws -> User { @@ -199,97 +217,88 @@ func apiUnmuteUser(_ userId: Int64) async throws -> User { } func setUserPrivacy_(_ cmd: ChatCommand) async throws -> User { - let r = await chatSendCmd(cmd) + let r: ChatResponse1 = try await chatSendCmd(cmd) if case let .userPrivacy(_, updatedUser) = r { return updatedUser } - throw r + throw r.unexpected } func apiDeleteUser(_ userId: Int64, _ delSMPQueues: Bool, viewPwd: String?) async throws { - let r = await chatSendCmd(.apiDeleteUser(userId: userId, delSMPQueues: delSMPQueues, viewPwd: viewPwd)) - if case .cmdOk = r { return } - throw r + try await sendCommandOkResp(.apiDeleteUser(userId: userId, delSMPQueues: delSMPQueues, viewPwd: viewPwd)) } func apiStartChat(ctrl: chat_ctrl? = nil) throws -> Bool { - let r = chatSendCmdSync(.startChat(mainApp: true), ctrl) + let r: ChatResponse0 = try chatSendCmdSync(.startChat(mainApp: true, enableSndFiles: true), ctrl: ctrl) switch r { case .chatStarted: return true case .chatRunning: return false - default: throw r + default: throw r.unexpected + } +} + +func apiCheckChatRunning() throws -> Bool { + let r: ChatResponse0 = try chatSendCmdSync(.checkChatRunning) + switch r { + case .chatRunning: return true + case .chatStopped: return false + default: throw r.unexpected } } func apiStopChat() async throws { - let r = await chatSendCmd(.apiStopChat) + let r: ChatResponse0 = try await chatSendCmd(.apiStopChat) switch r { case .chatStopped: return - default: throw r + default: throw r.unexpected } } func apiActivateChat() { chatReopenStore() - let r = chatSendCmdSync(.apiActivateChat(restoreChat: true)) - if case .cmdOk = r { return } - logger.error("apiActivateChat error: \(String(describing: r))") + do { + try sendCommandOkRespSync(.apiActivateChat(restoreChat: true)) + } catch { + logger.error("apiActivateChat error: \(responseError(error))") + } } func apiSuspendChat(timeoutMicroseconds: Int) { - let r = chatSendCmdSync(.apiSuspendChat(timeoutMicroseconds: timeoutMicroseconds)) - if case .cmdOk = r { return } - logger.error("apiSuspendChat error: \(String(describing: r))") + do { + try sendCommandOkRespSync(.apiSuspendChat(timeoutMicroseconds: timeoutMicroseconds)) + } catch { + logger.error("apiSuspendChat error: \(responseError(error))") + } } -func apiSetTempFolder(tempFolder: String, ctrl: chat_ctrl? = nil) throws { - let r = chatSendCmdSync(.setTempFolder(tempFolder: tempFolder), ctrl) +func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String, ctrl: chat_ctrl? = nil) throws { + let r: ChatResponse2 = try chatSendCmdSync(.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder), ctrl: ctrl) if case .cmdOk = r { return } - throw r -} - -func apiSetFilesFolder(filesFolder: String, ctrl: chat_ctrl? = nil) throws { - let r = chatSendCmdSync(.setFilesFolder(filesFolder: filesFolder), ctrl) - if case .cmdOk = r { return } - throw r + throw r.unexpected } func apiSetEncryptLocalFiles(_ enable: Bool) throws { - let r = chatSendCmdSync(.apiSetEncryptLocalFiles(enable: enable)) - if case .cmdOk = r { return } - throw r + try sendCommandOkRespSync(.apiSetEncryptLocalFiles(enable: enable)) } func apiSaveAppSettings(settings: AppSettings) throws { - let r = chatSendCmdSync(.apiSaveSettings(settings: settings)) - if case .cmdOk = r { return } - throw r + try sendCommandOkRespSync(.apiSaveSettings(settings: settings)) } func apiGetAppSettings(settings: AppSettings) throws -> AppSettings { - let r = chatSendCmdSync(.apiGetSettings(settings: settings)) + let r: ChatResponse2 = try chatSendCmdSync(.apiGetSettings(settings: settings)) if case let .appSettings(settings) = r { return settings } - throw r + throw r.unexpected } -func apiSetPQEncryption(_ enable: Bool) throws { - let r = chatSendCmdSync(.apiSetPQEncryption(enable: enable)) - if case .cmdOk = r { return } - throw r -} - -func apiSetContactPQ(_ contactId: Int64, _ enable: Bool) async throws -> Contact { - let r = await chatSendCmd(.apiSetContactPQ(contactId: contactId, enable: enable)) - if case let .contactPQAllowed(_, contact, _) = r { return contact } - throw r -} - -func apiExportArchive(config: ArchiveConfig) async throws { - try await sendCommandOkResp(.apiExportArchive(config: config)) +func apiExportArchive(config: ArchiveConfig) async throws -> [ArchiveError] { + let r: ChatResponse2 = try await chatSendCmd(.apiExportArchive(config: config)) + if case let .archiveExported(archiveErrors) = r { return archiveErrors } + throw r.unexpected } func apiImportArchive(config: ArchiveConfig) async throws -> [ArchiveError] { - let r = await chatSendCmd(.apiImportArchive(config: config)) + let r: ChatResponse2 = try await chatSendCmd(.apiImportArchive(config: config)) if case let .archiveImported(archiveErrors) = r { return archiveErrors } - throw r + throw r.unexpected } func apiDeleteStorage() async throws { @@ -300,8 +309,8 @@ func apiStorageEncryption(currentKey: String = "", newKey: String = "") async th try await sendCommandOkResp(.apiStorageEncryption(config: DBEncryptionConfig(currentKey: currentKey, newKey: newKey))) } -func testStorageEncryption(key: String, _ ctrl: chat_ctrl? = nil) async throws { - try await sendCommandOkResp(.testStorageEncryption(key: key), ctrl) +func testStorageEncryption(key: String, ctrl: chat_ctrl? = nil) async throws { + try await sendCommandOkResp(.testStorageEncryption(key: key), ctrl: ctrl) } func apiGetChats() throws -> [ChatData] { @@ -314,48 +323,104 @@ func apiGetChatsAsync() async throws -> [ChatData] { return try apiChatsResponse(await chatSendCmd(.apiGetChats(userId: userId))) } -private func apiChatsResponse(_ r: ChatResponse) throws -> [ChatData] { +private func apiChatsResponse(_ r: ChatResponse0) throws -> [ChatData] { if case let .apiChats(_, chats) = r { return chats } - throw r + throw r.unexpected } -func apiGetChat(type: ChatType, id: Int64, search: String = "") throws -> Chat { - let r = chatSendCmdSync(.apiGetChat(type: type, id: id, pagination: .last(count: 50), search: search)) - if case let .apiChat(_, chat) = r { return Chat.init(chat) } - throw r +func apiGetChatTags() throws -> [ChatTag] { + let userId = try currentUserId("apiGetChatTags") + let r: ChatResponse0 = try chatSendCmdSync(.apiGetChatTags(userId: userId)) + if case let .chatTags(_, tags) = r { return tags } + throw r.unexpected } -func apiGetChatItems(type: ChatType, id: Int64, pagination: ChatPagination, search: String = "") async throws -> [ChatItem] { - let r = await chatSendCmd(.apiGetChat(type: type, id: id, pagination: pagination, search: search)) - if case let .apiChat(_, chat) = r { return chat.chatItems } - throw r +func apiGetChatTagsAsync() async throws -> [ChatTag] { + let userId = try currentUserId("apiGetChatTags") + let r: ChatResponse0 = try await chatSendCmd(.apiGetChatTags(userId: userId)) + if case let .chatTags(_, tags) = r { return tags } + throw r.unexpected } -func loadChat(chat: Chat, search: String = "") { - do { - let cInfo = chat.chatInfo - let m = ChatModel.shared +let loadItemsPerPage = 50 + +func apiGetChat(chatId: ChatId, pagination: ChatPagination, search: String = "") async throws -> (Chat, NavigationInfo) { + let r: ChatResponse0 = try await chatSendCmd(.apiGetChat(chatId: chatId, pagination: pagination, search: search)) + if case let .apiChat(_, chat, navInfo) = r { return (Chat.init(chat), navInfo ?? NavigationInfo()) } + throw r.unexpected +} + +func loadChat(chat: Chat, search: String = "", clearItems: Bool = true) async { + await loadChat(chatId: chat.chatInfo.id, search: search, clearItems: clearItems) +} + +func loadChat(chatId: ChatId, search: String = "", openAroundItemId: ChatItem.ID? = nil, clearItems: Bool = true) async { + let m = ChatModel.shared + let im = ItemsModel.shared + await MainActor.run { m.chatItemStatuses = [:] - m.reversedChatItems = [] - let chat = try apiGetChat(type: cInfo.chatType, id: cInfo.apiId, search: search) - m.updateChatInfo(chat.chatInfo) - m.reversedChatItems = chat.chatItems.reversed() - } catch let error { - logger.error("loadChat error: \(responseError(error))") + if clearItems { + im.reversedChatItems = [] + ItemsModel.shared.chatState.clear() + } } + await apiLoadMessages(chatId, openAroundItemId != nil ? .around(chatItemId: openAroundItemId!, count: loadItemsPerPage) : (search == "" ? .initial(count: loadItemsPerPage) : .last(count: loadItemsPerPage)), im.chatState, search, openAroundItemId, { 0...0 }) } func apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) async throws -> ChatItemInfo { - let r = await chatSendCmd(.apiGetChatItemInfo(type: type, id: id, itemId: itemId)) + let r: ChatResponse0 = try await chatSendCmd(.apiGetChatItemInfo(type: type, id: id, itemId: itemId)) if case let .chatItemInfo(_, _, chatItemInfo) = r { return chatItemInfo } - throw r + throw r.unexpected } -func apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool = false, ttl: Int? = nil) async -> ChatItem? { +func apiPlanForwardChatItems(type: ChatType, id: Int64, itemIds: [Int64]) async throws -> ([Int64], ForwardConfirmation?) { + let r: ChatResponse1 = try await chatSendCmd(.apiPlanForwardChatItems(toChatType: type, toChatId: id, itemIds: itemIds)) + if case let .forwardPlan(_, chatItemIds, forwardConfimation) = r { return (chatItemIds, forwardConfimation) } + throw r.unexpected +} + +func apiForwardChatItems(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemIds: [Int64], ttl: Int?) async -> [ChatItem]? { + let cmd: ChatCommand = .apiForwardChatItems(toChatType: toChatType, toChatId: toChatId, fromChatType: fromChatType, fromChatId: fromChatId, itemIds: itemIds, ttl: ttl) + return await processSendMessageCmd(toChatType: toChatType, cmd: cmd) +} + +func apiCreateChatTag(tag: ChatTagData) async throws -> [ChatTag] { + let r: ChatResponse0 = try await chatSendCmd(.apiCreateChatTag(tag: tag)) + if case let .chatTags(_, userTags) = r { + return userTags + } + throw r.unexpected +} + +func apiSetChatTags(type: ChatType, id: Int64, tagIds: [Int64]) async throws -> ([ChatTag], [Int64]) { + let r: ChatResponse0 = try await chatSendCmd(.apiSetChatTags(type: type, id: id, tagIds: tagIds)) + if case let .tagsUpdated(_, userTags, chatTags) = r { + return (userTags, chatTags) + } + throw r.unexpected +} + +func apiDeleteChatTag(tagId: Int64) async throws { + try await sendCommandOkResp(.apiDeleteChatTag(tagId: tagId)) +} + +func apiUpdateChatTag(tagId: Int64, tag: ChatTagData) async throws { + try await sendCommandOkResp(.apiUpdateChatTag(tagId: tagId, tagData: tag)) +} + +func apiReorderChatTags(tagIds: [Int64]) async throws { + try await sendCommandOkResp(.apiReorderChatTags(tagIds: tagIds)) +} + +func apiSendMessages(type: ChatType, id: Int64, live: Bool = false, ttl: Int? = nil, composedMessages: [ComposedMessage]) async -> [ChatItem]? { + let cmd: ChatCommand = .apiSendMessages(type: type, id: id, live: live, ttl: ttl, composedMessages: composedMessages) + return await processSendMessageCmd(toChatType: type, cmd: cmd) +} + +private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async -> [ChatItem]? { let chatModel = ChatModel.shared - let cmd: ChatCommand = .apiSendMessage(type: type, id: id, file: file, quotedItemId: quotedItemId, msg: msg, live: live, ttl: ttl) - let r: ChatResponse - if type == .direct { + let r: APIResult + if toChatType == .direct { var cItem: ChatItem? = nil let endTask = beginBGTask({ if let cItem = cItem { @@ -364,81 +429,118 @@ func apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: } } }) - r = await chatSendCmd(cmd, bgTask: false) - if case let .newChatItem(_, aChatItem) = r { - cItem = aChatItem.chatItem - chatModel.messageDelivery[aChatItem.chatItem.id] = endTask - return cItem + r = await chatApiSendCmd(cmd, bgTask: false) + if case let .result(.newChatItems(_, aChatItems)) = r { + let cItems = aChatItems.map { $0.chatItem } + if let cItemLast = cItems.last { + cItem = cItemLast + chatModel.messageDelivery[cItemLast.id] = endTask + } + return cItems } if let networkErrorAlert = networkErrorAlert(r) { AlertManager.shared.showAlert(networkErrorAlert) } else { - sendMessageErrorAlert(r) + sendMessageErrorAlert(r.unexpected) } endTask() return nil } else { - r = await chatSendCmd(cmd, bgDelay: msgDelay) - if case let .newChatItem(_, aChatItem) = r { - return aChatItem.chatItem + r = await chatApiSendCmd(cmd, bgDelay: msgDelay) + if case let .result(.newChatItems(_, aChatItems)) = r { + return aChatItems.map { $0.chatItem } } - sendMessageErrorAlert(r) + sendMessageErrorAlert(r.unexpected) return nil } } -func apiCreateChatItem(noteFolderId: Int64, file: CryptoFile?, msg: MsgContent) async -> ChatItem? { - let r = await chatSendCmd(.apiCreateChatItem(noteFolderId: noteFolderId, file: file, msg: msg)) - if case let .newChatItem(_, aChatItem) = r { return aChatItem.chatItem } - createChatItemErrorAlert(r) +func apiCreateChatItems(noteFolderId: Int64, composedMessages: [ComposedMessage]) async -> [ChatItem]? { + let r: APIResult = await chatApiSendCmd(.apiCreateChatItems(noteFolderId: noteFolderId, composedMessages: composedMessages)) + if case let .result(.newChatItems(_, aChatItems)) = r { return aChatItems.map { $0.chatItem } } + createChatItemsErrorAlert(r.unexpected) return nil } -private func sendMessageErrorAlert(_ r: ChatResponse) { - logger.error("apiSendMessage error: \(String(describing: r))") +func apiReportMessage(groupId: Int64, chatItemId: Int64, reportReason: ReportReason, reportText: String) async -> [ChatItem]? { + let r: APIResult = await chatApiSendCmd(.apiReportMessage(groupId: groupId, chatItemId: chatItemId, reportReason: reportReason, reportText: reportText)) + if case let .result(.newChatItems(_, aChatItems)) = r { return aChatItems.map { $0.chatItem } } + + logger.error("apiReportMessage error: \(String(describing: r))") + AlertManager.shared.showAlertMsg( + title: "Error creating report", + message: "Error: \(responseError(r.unexpected))" + ) + return nil +} + +private func sendMessageErrorAlert(_ r: ChatError) { + logger.error("send message error: \(String(describing: r))") AlertManager.shared.showAlertMsg( title: "Error sending message", - message: "Error: \(String(describing: r))" + message: "Error: \(responseError(r))" ) } -private func createChatItemErrorAlert(_ r: ChatResponse) { - logger.error("apiCreateChatItem error: \(String(describing: r))") +private func createChatItemsErrorAlert(_ r: ChatError) { + logger.error("apiCreateChatItems error: \(String(describing: r))") AlertManager.shared.showAlertMsg( title: "Error creating message", - message: "Error: \(String(describing: r))" + message: "Error: \(responseError(r))" ) } -func apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent, live: Bool = false) async throws -> ChatItem { - let r = await chatSendCmd(.apiUpdateChatItem(type: type, id: id, itemId: itemId, msg: msg, live: live), bgDelay: msgDelay) - if case let .chatItemUpdated(_, aChatItem) = r { return aChatItem.chatItem } - throw r +func apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, updatedMessage: UpdatedMessage, live: Bool = false) async throws -> ChatItem { + let r: ChatResponse1 = try await chatSendCmd(.apiUpdateChatItem(type: type, id: id, itemId: itemId, updatedMessage: updatedMessage, live: live), bgDelay: msgDelay) + switch r { + case let .chatItemUpdated(_, aChatItem): return aChatItem.chatItem + case let .chatItemNotChanged(_, aChatItem): return aChatItem.chatItem + default: throw r.unexpected + } } func apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction) async throws -> ChatItem { - let r = await chatSendCmd(.apiChatItemReaction(type: type, id: id, itemId: itemId, add: add, reaction: reaction), bgDelay: msgDelay) + let r: ChatResponse1 = try await chatSendCmd(.apiChatItemReaction(type: type, id: id, itemId: itemId, add: add, reaction: reaction), bgDelay: msgDelay) if case let .chatItemReaction(_, _, reaction) = r { return reaction.chatReaction.chatItem } - throw r + throw r.unexpected } -func apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode) async throws -> (ChatItem, ChatItem?) { - let r = await chatSendCmd(.apiDeleteChatItem(type: type, id: id, itemId: itemId, mode: mode), bgDelay: msgDelay) - if case let .chatItemDeleted(_, deletedChatItem, toChatItem, _) = r { return (deletedChatItem.chatItem, toChatItem?.chatItem) } - throw r +func apiGetReactionMembers(groupId: Int64, itemId: Int64, reaction: MsgReaction) async throws -> [MemberReaction] { + let userId = try currentUserId("apiGetReactionMemebers") + let r: ChatResponse1 = try await chatSendCmd(.apiGetReactionMembers(userId: userId, groupId: groupId, itemId: itemId, reaction: reaction )) + if case let .reactionMembers(_, memberReactions) = r { return memberReactions } + throw r.unexpected } -func apiDeleteMemberChatItem(groupId: Int64, groupMemberId: Int64, itemId: Int64) async throws -> (ChatItem, ChatItem?) { - let r = await chatSendCmd(.apiDeleteMemberChatItem(groupId: groupId, groupMemberId: groupMemberId, itemId: itemId), bgDelay: msgDelay) - if case let .chatItemDeleted(_, deletedChatItem, toChatItem, _) = r { return (deletedChatItem.chatItem, toChatItem?.chatItem) } - throw r +func apiDeleteChatItems(type: ChatType, id: Int64, itemIds: [Int64], mode: CIDeleteMode) async throws -> [ChatItemDeletion] { + let r: ChatResponse1 = try await chatSendCmd(.apiDeleteChatItem(type: type, id: id, itemIds: itemIds, mode: mode), bgDelay: msgDelay) + if case let .chatItemsDeleted(_, items, _) = r { return items } + throw r.unexpected +} + +func apiDeleteMemberChatItems(groupId: Int64, itemIds: [Int64]) async throws -> [ChatItemDeletion] { + let r: ChatResponse1 = try await chatSendCmd(.apiDeleteMemberChatItem(groupId: groupId, itemIds: itemIds), bgDelay: msgDelay) + if case let .chatItemsDeleted(_, items, _) = r { return items } + throw r.unexpected +} + +func apiArchiveReceivedReports(groupId: Int64) async throws -> ChatResponse1 { + let r: ChatResponse1 = try await chatSendCmd(.apiArchiveReceivedReports(groupId: groupId), bgDelay: msgDelay) + if case .groupChatItemsDeleted = r { return r } + throw r.unexpected +} + +func apiDeleteReceivedReports(groupId: Int64, itemIds: [Int64], mode: CIDeleteMode) async throws -> [ChatItemDeletion] { + let r: ChatResponse1 = try await chatSendCmd(.apiDeleteReceivedReports(groupId: groupId, itemIds: itemIds, mode: mode), bgDelay: msgDelay) + if case let .chatItemsDeleted(_, chatItemDeletions, _) = r { return chatItemDeletions } + throw r.unexpected } func apiGetNtfToken() -> (DeviceToken?, NtfTknStatus?, NotificationsMode, String?) { - let r = chatSendCmdSync(.apiGetNtfToken) + let r: APIResult = chatApiSendCmdSync(.apiGetNtfToken) switch r { - case let .ntfToken(token, status, ntfMode, ntfServer): return (token, status, ntfMode, ntfServer) - case .chatCmdError(_, .errorAgent(.CMD(.PROHIBITED))): return (nil, nil, .off, nil) + case let .result(.ntfToken(token, status, ntfMode, ntfServer)): return (token, status, ntfMode, ntfServer) + case .error(.errorAgent(.CMD(.PROHIBITED, _))): return (nil, nil, .off, nil) default: logger.debug("apiGetNtfToken response: \(String(describing: r))") return (nil, nil, .off, nil) @@ -446,9 +548,9 @@ func apiGetNtfToken() -> (DeviceToken?, NtfTknStatus?, NotificationsMode, String } func apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode) async throws -> NtfTknStatus { - let r = await chatSendCmd(.apiRegisterToken(token: token, notificationMode: notificationMode)) + let r: ChatResponse2 = try await chatSendCmd(.apiRegisterToken(token: token, notificationMode: notificationMode)) if case let .ntfTokenStatus(status) = r { return status } - throw r + throw r.unexpected } func registerToken(token: DeviceToken) { @@ -460,7 +562,12 @@ func registerToken(token: DeviceToken) { Task { do { let status = try await apiRegisterToken(token: token, notificationMode: mode) - await MainActor.run { m.tokenStatus = status } + await MainActor.run { + m.tokenStatus = status + if !status.workingToken { + m.reRegisterTknStatus = status + } + } } catch let error { logger.error("registerToken apiRegisterToken error: \(responseError(error))") } @@ -468,36 +575,129 @@ func registerToken(token: DeviceToken) { } } +func tokenStatusInfo(_ status: NtfTknStatus, register: Bool) -> String { + String.localizedStringWithFormat(NSLocalizedString("Token status: %@.", comment: "token status"), status.text) + + "\n" + status.info(register: register) +} + +func reRegisterToken(token: DeviceToken) { + let m = ChatModel.shared + let mode = m.notificationMode + logger.debug("reRegisterToken \(mode.rawValue)") + Task { + do { + let status = try await apiRegisterToken(token: token, notificationMode: mode) + await MainActor.run { + m.tokenStatus = status + showAlert( + status.workingToken + ? NSLocalizedString("Notifications status", comment: "alert title") + : NSLocalizedString("Notifications error", comment: "alert title"), + message: tokenStatusInfo(status, register: false) + ) + } + } catch let error { + logger.error("reRegisterToken apiRegisterToken error: \(responseError(error))") + await MainActor.run { + showAlert( + NSLocalizedString("Error registering for notifications", comment: "alert title"), + message: responseError(error) + ) + } + } + } +} + func apiVerifyToken(token: DeviceToken, nonce: String, code: String) async throws { try await sendCommandOkResp(.apiVerifyToken(token: token, nonce: nonce, code: code)) } +func apiCheckToken(token: DeviceToken) async throws -> NtfTknStatus { + let r: ChatResponse2 = try await chatSendCmd(.apiCheckToken(token: token)) + if case let .ntfTokenStatus(status) = r { return status } + throw r.unexpected +} + func apiDeleteToken(token: DeviceToken) async throws { try await sendCommandOkResp(.apiDeleteToken(token: token)) } -func getUserProtoServers(_ serverProtocol: ServerProtocol) throws -> UserProtoServers { - let userId = try currentUserId("getUserProtoServers") - let r = chatSendCmdSync(.apiGetUserProtoServers(userId: userId, serverProtocol: serverProtocol)) - if case let .userProtoServers(_, servers) = r { return servers } - throw r -} - -func setUserProtoServers(_ serverProtocol: ServerProtocol, servers: [ServerCfg]) async throws { - let userId = try currentUserId("setUserProtoServers") - try await sendCommandOkResp(.apiSetUserProtoServers(userId: userId, serverProtocol: serverProtocol, servers: servers)) -} - func testProtoServer(server: String) async throws -> Result<(), ProtocolTestFailure> { let userId = try currentUserId("testProtoServer") - let r = await chatSendCmd(.apiTestProtoServer(userId: userId, server: server)) + let r: ChatResponse0 = try await chatSendCmd(.apiTestProtoServer(userId: userId, server: server)) if case let .serverTestResult(_, _, testFailure) = r { if let t = testFailure { return .failure(t) } return .success(()) } - throw r + throw r.unexpected +} + +func getServerOperators() async throws -> ServerOperatorConditions { + let r: ChatResponse0 = try await chatSendCmd(.apiGetServerOperators) + if case let .serverOperatorConditions(conditions) = r { return conditions } + logger.error("getServerOperators error: \(String(describing: r))") + throw r.unexpected +} + +func getServerOperatorsSync() throws -> ServerOperatorConditions { + let r: ChatResponse0 = try chatSendCmdSync(.apiGetServerOperators) + if case let .serverOperatorConditions(conditions) = r { return conditions } + logger.error("getServerOperators error: \(String(describing: r))") + throw r.unexpected +} + +func setServerOperators(operators: [ServerOperator]) async throws -> ServerOperatorConditions { + let r: ChatResponse0 = try await chatSendCmd(.apiSetServerOperators(operators: operators)) + if case let .serverOperatorConditions(conditions) = r { return conditions } + logger.error("setServerOperators error: \(String(describing: r))") + throw r.unexpected +} + +func getUserServers() async throws -> [UserOperatorServers] { + let userId = try currentUserId("getUserServers") + let r: ChatResponse0 = try await chatSendCmd(.apiGetUserServers(userId: userId)) + if case let .userServers(_, userServers) = r { return userServers } + logger.error("getUserServers error: \(String(describing: r))") + throw r.unexpected +} + +func setUserServers(userServers: [UserOperatorServers]) async throws { + let userId = try currentUserId("setUserServers") + let r: ChatResponse2 = try await chatSendCmd(.apiSetUserServers(userId: userId, userServers: userServers)) + if case .cmdOk = r { return } + logger.error("setUserServers error: \(String(describing: r))") + throw r.unexpected +} + +func validateServers(userServers: [UserOperatorServers]) async throws -> [UserServersError] { + let userId = try currentUserId("validateServers") + let r: ChatResponse0 = try await chatSendCmd(.apiValidateServers(userId: userId, userServers: userServers)) + if case let .userServersValidation(_, serverErrors) = r { return serverErrors } + logger.error("validateServers error: \(String(describing: r))") + throw r.unexpected +} + +func getUsageConditions() async throws -> (UsageConditions, String?, UsageConditions?) { + let r: ChatResponse0 = try await chatSendCmd(.apiGetUsageConditions) + if case let .usageConditions(usageConditions, conditionsText, acceptedConditions) = r { return (usageConditions, conditionsText, acceptedConditions) } + logger.error("getUsageConditions error: \(String(describing: r))") + throw r.unexpected +} + +func setConditionsNotified(conditionsId: Int64) async throws { + let r: ChatResponse2 = try await chatSendCmd(.apiSetConditionsNotified(conditionsId: conditionsId)) + if case .cmdOk = r { return } + logger.error("setConditionsNotified error: \(String(describing: r))") + throw r.unexpected +} + +func acceptConditions(conditionsId: Int64, operatorIds: [Int64]) async throws -> ServerOperatorConditions { + let r: ChatResponse0 = try await chatSendCmd(.apiAcceptConditions(conditionsId: conditionsId, operatorIds: operatorIds)) + if case let .serverOperatorConditions(conditions) = r { return conditions } + logger.error("acceptConditions error: \(String(describing: r))") + throw r.unexpected } func getChatItemTTL() throws -> ChatItemTTL { @@ -510,9 +710,15 @@ func getChatItemTTLAsync() async throws -> ChatItemTTL { return try chatItemTTLResponse(await chatSendCmd(.apiGetChatItemTTL(userId: userId))) } -private func chatItemTTLResponse(_ r: ChatResponse) throws -> ChatItemTTL { - if case let .chatItemTTL(_, chatItemTTL) = r { return ChatItemTTL(chatItemTTL) } - throw r +private func chatItemTTLResponse(_ r: ChatResponse0) throws -> ChatItemTTL { + if case let .chatItemTTL(_, chatItemTTL) = r { + if let ttl = chatItemTTL { + return ChatItemTTL(ttl) + } else { + throw RuntimeError("chatItemTTLResponse: invalid ttl") + } + } + throw r.unexpected } func setChatItemTTL(_ chatItemTTL: ChatItemTTL) async throws { @@ -520,22 +726,38 @@ func setChatItemTTL(_ chatItemTTL: ChatItemTTL) async throws { try await sendCommandOkResp(.apiSetChatItemTTL(userId: userId, seconds: chatItemTTL.seconds)) } +func setChatTTL(chatType: ChatType, id: Int64, _ chatItemTTL: ChatTTL) async throws { + let userId = try currentUserId("setChatItemTTL") + try await sendCommandOkResp(.apiSetChatTTL(userId: userId, type: chatType, id: id, seconds: chatItemTTL.value)) +} + func getNetworkConfig() async throws -> NetCfg? { - let r = await chatSendCmd(.apiGetNetworkConfig) + let r: ChatResponse0 = try await chatSendCmd(.apiGetNetworkConfig) if case let .networkConfig(cfg) = r { return cfg } - throw r + throw r.unexpected } func setNetworkConfig(_ cfg: NetCfg, ctrl: chat_ctrl? = nil) throws { - let r = chatSendCmdSync(.apiSetNetworkConfig(networkConfig: cfg), ctrl) + let r: ChatResponse2 = try chatSendCmdSync(.apiSetNetworkConfig(networkConfig: cfg), ctrl: ctrl) if case .cmdOk = r { return } - throw r + throw r.unexpected +} + +func apiSetNetworkInfo(_ networkInfo: UserNetworkInfo) throws { + let r: ChatResponse2 = try chatSendCmdSync(.apiSetNetworkInfo(networkInfo: networkInfo)) + if case .cmdOk = r { return } + throw r.unexpected } func reconnectAllServers() async throws { try await sendCommandOkResp(.reconnectAllServers) } +func reconnectServer(smpServer: String) async throws { + let userId = try currentUserId("reconnectServer") + try await sendCommandOkResp(.reconnectServer(userId: userId, smpServer: smpServer)) +} + func apiSetChatSettings(type: ChatType, id: Int64, chatSettings: ChatSettings) async throws { try await sendCommandOkResp(.apiSetChatSettings(type: type, id: id, chatSettings: chatSettings)) } @@ -545,106 +767,135 @@ func apiSetMemberSettings(_ groupId: Int64, _ groupMemberId: Int64, _ memberSett } func apiContactInfo(_ contactId: Int64) async throws -> (ConnectionStats?, Profile?) { - let r = await chatSendCmd(.apiContactInfo(contactId: contactId)) + let r: ChatResponse0 = try await chatSendCmd(.apiContactInfo(contactId: contactId)) if case let .contactInfo(_, _, connStats, customUserProfile) = r { return (connStats, customUserProfile) } - throw r + throw r.unexpected } -func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) throws -> (GroupMember, ConnectionStats?) { - let r = chatSendCmdSync(.apiGroupMemberInfo(groupId: groupId, groupMemberId: groupMemberId)) +func apiGroupMemberInfoSync(_ groupId: Int64, _ groupMemberId: Int64) throws -> (GroupMember, ConnectionStats?) { + let r: ChatResponse0 = try chatSendCmdSync(.apiGroupMemberInfo(groupId: groupId, groupMemberId: groupMemberId)) if case let .groupMemberInfo(_, _, member, connStats_) = r { return (member, connStats_) } - throw r + throw r.unexpected +} + +func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (GroupMember, ConnectionStats?) { + let r: ChatResponse0 = try await chatSendCmd(.apiGroupMemberInfo(groupId: groupId, groupMemberId: groupMemberId)) + if case let .groupMemberInfo(_, _, member, connStats_) = r { return (member, connStats_) } + throw r.unexpected +} + +func apiContactQueueInfo(_ contactId: Int64) async throws -> (RcvMsgInfo?, ServerQueueInfo) { + let r: ChatResponse0 = try await chatSendCmd(.apiContactQueueInfo(contactId: contactId)) + if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) } + throw r.unexpected +} + +func apiGroupMemberQueueInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (RcvMsgInfo?, ServerQueueInfo) { + let r: ChatResponse0 = try await chatSendCmd(.apiGroupMemberQueueInfo(groupId: groupId, groupMemberId: groupMemberId)) + if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) } + throw r.unexpected } func apiSwitchContact(contactId: Int64) throws -> ConnectionStats { - let r = chatSendCmdSync(.apiSwitchContact(contactId: contactId)) + let r: ChatResponse0 = try chatSendCmdSync(.apiSwitchContact(contactId: contactId)) if case let .contactSwitchStarted(_, _, connectionStats) = r { return connectionStats } - throw r + throw r.unexpected } func apiSwitchGroupMember(_ groupId: Int64, _ groupMemberId: Int64) throws -> ConnectionStats { - let r = chatSendCmdSync(.apiSwitchGroupMember(groupId: groupId, groupMemberId: groupMemberId)) + let r: ChatResponse0 = try chatSendCmdSync(.apiSwitchGroupMember(groupId: groupId, groupMemberId: groupMemberId)) if case let .groupMemberSwitchStarted(_, _, _, connectionStats) = r { return connectionStats } - throw r + throw r.unexpected } func apiAbortSwitchContact(_ contactId: Int64) throws -> ConnectionStats { - let r = chatSendCmdSync(.apiAbortSwitchContact(contactId: contactId)) + let r: ChatResponse0 = try chatSendCmdSync(.apiAbortSwitchContact(contactId: contactId)) if case let .contactSwitchAborted(_, _, connectionStats) = r { return connectionStats } - throw r + throw r.unexpected } func apiAbortSwitchGroupMember(_ groupId: Int64, _ groupMemberId: Int64) throws -> ConnectionStats { - let r = chatSendCmdSync(.apiAbortSwitchGroupMember(groupId: groupId, groupMemberId: groupMemberId)) + let r: ChatResponse0 = try chatSendCmdSync(.apiAbortSwitchGroupMember(groupId: groupId, groupMemberId: groupMemberId)) if case let .groupMemberSwitchAborted(_, _, _, connectionStats) = r { return connectionStats } - throw r + throw r.unexpected } func apiSyncContactRatchet(_ contactId: Int64, _ force: Bool) throws -> ConnectionStats { - let r = chatSendCmdSync(.apiSyncContactRatchet(contactId: contactId, force: force)) + let r: ChatResponse0 = try chatSendCmdSync(.apiSyncContactRatchet(contactId: contactId, force: force)) if case let .contactRatchetSyncStarted(_, _, connectionStats) = r { return connectionStats } - throw r + throw r.unexpected } func apiSyncGroupMemberRatchet(_ groupId: Int64, _ groupMemberId: Int64, _ force: Bool) throws -> (GroupMember, ConnectionStats) { - let r = chatSendCmdSync(.apiSyncGroupMemberRatchet(groupId: groupId, groupMemberId: groupMemberId, force: force)) + let r: ChatResponse0 = try chatSendCmdSync(.apiSyncGroupMemberRatchet(groupId: groupId, groupMemberId: groupMemberId, force: force)) if case let .groupMemberRatchetSyncStarted(_, _, member, connectionStats) = r { return (member, connectionStats) } - throw r + throw r.unexpected } func apiGetContactCode(_ contactId: Int64) async throws -> (Contact, String) { - let r = await chatSendCmd(.apiGetContactCode(contactId: contactId)) + let r: ChatResponse0 = try await chatSendCmd(.apiGetContactCode(contactId: contactId)) if case let .contactCode(_, contact, connectionCode) = r { return (contact, connectionCode) } - throw r + throw r.unexpected } -func apiGetGroupMemberCode(_ groupId: Int64, _ groupMemberId: Int64) throws -> (GroupMember, String) { - let r = chatSendCmdSync(.apiGetGroupMemberCode(groupId: groupId, groupMemberId: groupMemberId)) +func apiGetGroupMemberCode(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (GroupMember, String) { + let r: ChatResponse0 = try await chatSendCmd(.apiGetGroupMemberCode(groupId: groupId, groupMemberId: groupMemberId)) if case let .groupMemberCode(_, _, member, connectionCode) = r { return (member, connectionCode) } - throw r + throw r.unexpected } func apiVerifyContact(_ contactId: Int64, connectionCode: String?) -> (Bool, String)? { - let r = chatSendCmdSync(.apiVerifyContact(contactId: contactId, connectionCode: connectionCode)) - if case let .connectionVerified(_, verified, expectedCode) = r { return (verified, expectedCode) } + let r: APIResult = chatApiSendCmdSync(.apiVerifyContact(contactId: contactId, connectionCode: connectionCode)) + if case let .result(.connectionVerified(_, verified, expectedCode)) = r { return (verified, expectedCode) } logger.error("apiVerifyContact error: \(String(describing: r))") return nil } func apiVerifyGroupMember(_ groupId: Int64, _ groupMemberId: Int64, connectionCode: String?) -> (Bool, String)? { - let r = chatSendCmdSync(.apiVerifyGroupMember(groupId: groupId, groupMemberId: groupMemberId, connectionCode: connectionCode)) - if case let .connectionVerified(_, verified, expectedCode) = r { return (verified, expectedCode) } + let r: APIResult = chatApiSendCmdSync(.apiVerifyGroupMember(groupId: groupId, groupMemberId: groupMemberId, connectionCode: connectionCode)) + if case let .result(.connectionVerified(_, verified, expectedCode)) = r { return (verified, expectedCode) } logger.error("apiVerifyGroupMember error: \(String(describing: r))") return nil } -func apiAddContact(incognito: Bool) async -> ((String, PendingContactConnection)?, Alert?) { +func apiAddContact(incognito: Bool) async -> ((CreatedConnLink, PendingContactConnection)?, Alert?) { guard let userId = ChatModel.shared.currentUser?.userId else { logger.error("apiAddContact: no current user") return (nil, nil) } - let r = await chatSendCmd(.apiAddContact(userId: userId, incognito: incognito), bgTask: false) - if case let .invitation(_, connReqInvitation, connection) = r { return ((connReqInvitation, connection), nil) } + let short = UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS) + let r: APIResult = await chatApiSendCmd(.apiAddContact(userId: userId, short: short, incognito: incognito), bgTask: false) + if case let .result(.invitation(_, connLinkInv, connection)) = r { return ((connLinkInv, connection), nil) } let alert = connectionErrorAlert(r) return (nil, alert) } func apiSetConnectionIncognito(connId: Int64, incognito: Bool) async throws -> PendingContactConnection? { - let r = await chatSendCmd(.apiSetConnectionIncognito(connId: connId, incognito: incognito)) + let r: ChatResponse1 = try await chatSendCmd(.apiSetConnectionIncognito(connId: connId, incognito: incognito)) if case let .connectionIncognitoUpdated(_, toConnection) = r { return toConnection } - throw r + throw r.unexpected } -func apiConnectPlan(connReq: String) async throws -> ConnectionPlan { - let userId = try currentUserId("apiConnectPlan") - let r = await chatSendCmd(.apiConnectPlan(userId: userId, connReq: connReq)) - if case let .connectionPlan(_, connectionPlan) = r { return connectionPlan } - logger.error("apiConnectPlan error: \(responseError(r))") - throw r +func apiChangeConnectionUser(connId: Int64, userId: Int64) async throws -> PendingContactConnection { + let r: ChatResponse1 = try await chatSendCmd(.apiChangeConnectionUser(connId: connId, userId: userId)) + + if case let .connectionUserChanged(_, _, toConnection, _) = r {return toConnection} + throw r.unexpected } -func apiConnect(incognito: Bool, connReq: String) async -> (ConnReqType, PendingContactConnection)? { - let (r, alert) = await apiConnect_(incognito: incognito, connReq: connReq) +func apiConnectPlan(connLink: String) async -> ((CreatedConnLink, ConnectionPlan)?, Alert?) { + guard let userId = ChatModel.shared.currentUser?.userId else { + logger.error("apiConnectPlan: no current user") + return (nil, nil) + } + let r: APIResult = await chatApiSendCmd(.apiConnectPlan(userId: userId, connLink: connLink)) + if case let .result(.connectionPlan(_, connLink, connPlan)) = r { return ((connLink, connPlan), nil) } + let alert = apiConnectResponseAlert(r.unexpected) ?? connectionErrorAlert(r) + return (nil, alert) +} + +func apiConnect(incognito: Bool, connLink: CreatedConnLink) async -> (ConnReqType, PendingContactConnection)? { + let (r, alert) = await apiConnect_(incognito: incognito, connLink: connLink) if let alert = alert { AlertManager.shared.showAlert(alert) return nil @@ -653,48 +904,74 @@ func apiConnect(incognito: Bool, connReq: String) async -> (ConnReqType, Pending } } -func apiConnect_(incognito: Bool, connReq: String) async -> ((ConnReqType, PendingContactConnection)?, Alert?) { +func apiConnect_(incognito: Bool, connLink: CreatedConnLink) async -> ((ConnReqType, PendingContactConnection)?, Alert?) { guard let userId = ChatModel.shared.currentUser?.userId else { logger.error("apiConnect: no current user") return (nil, nil) } - let r = await chatSendCmd(.apiConnect(userId: userId, incognito: incognito, connReq: connReq)) + let r: APIResult = await chatApiSendCmd(.apiConnect(userId: userId, incognito: incognito, connLink: connLink)) let m = ChatModel.shared switch r { - case let .sentConfirmation(_, connection): + case let .result(.sentConfirmation(_, connection)): return ((.invitation, connection), nil) - case let .sentInvitation(_, connection): + case let .result(.sentInvitation(_, connection)): return ((.contact, connection), nil) - case let .contactAlreadyExists(_, contact): + case let .result(.contactAlreadyExists(_, contact)): if let c = m.getContactChat(contact.contactId) { - await MainActor.run { m.chatId = c.id } + ItemsModel.shared.loadOpenChat(c.id) } let alert = contactAlreadyExistsAlert(contact) return (nil, alert) - case .chatCmdError(_, .error(.invalidConnReq)): - let alert = mkAlert( + default: () + } + let alert = apiConnectResponseAlert(r.unexpected) ?? connectionErrorAlert(r) + return (nil, alert) +} + +private func apiConnectResponseAlert(_ r: ChatError) -> Alert? { + switch r { + case .error(.invalidConnReq): + mkAlert( title: "Invalid connection link", message: "Please check that you used the correct link or ask your contact to send you another one." ) - return (nil, alert) - case .chatCmdError(_, .errorAgent(.SMP(.AUTH))): - let alert = mkAlert( + case .error(.unsupportedConnReq): + mkAlert( + title: "Unsupported connection link", + message: "This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link." + ) + case .errorAgent(.SMP(_, .AUTH)): + mkAlert( title: "Connection error (AUTH)", message: "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." ) - return (nil, alert) - case let .chatCmdError(_, .errorAgent(.INTERNAL(internalErr))): + case let .errorAgent(.SMP(_, .BLOCKED(info))): + Alert( + title: Text("Connection blocked"), + message: Text("Connection is blocked by server operator:\n\(info.reason.text)"), + primaryButton: .default(Text("Ok")), + secondaryButton: .default(Text("How it works")) { + DispatchQueue.main.async { + UIApplication.shared.open(contentModerationPostLink) + } + } + ) + case .errorAgent(.SMP(_, .QUOTA)): + mkAlert( + title: "Undelivered messages", + message: "The connection reached the limit of undelivered messages, your contact may be offline." + ) + case let .errorAgent(.INTERNAL(internalErr)): if internalErr == "SEUniqueID" { - let alert = mkAlert( + mkAlert( title: "Already connected?", message: "It seems like you are already connected via this link. If it is not the case, there was an error (\(responseError(r)))." ) - return (nil, alert) + } else { + nil } - default: () + default: nil } - let alert = connectionErrorAlert(r) - return (nil, alert) } func contactAlreadyExistsAlert(_ contact: Contact) -> Alert { @@ -704,13 +981,13 @@ func contactAlreadyExistsAlert(_ contact: Contact) -> Alert { ) } -private func connectionErrorAlert(_ r: ChatResponse) -> Alert { +private func connectionErrorAlert(_ r: APIResult) -> Alert { if let networkErrorAlert = networkErrorAlert(r) { return networkErrorAlert } else { return mkAlert( title: "Connection error", - message: "Error: \(String(describing: r))" + message: "Error: \(responseError(r.unexpected))" ) } } @@ -720,29 +997,45 @@ func apiConnectContactViaAddress(incognito: Bool, contactId: Int64) async -> (Co logger.error("apiConnectContactViaAddress: no current user") return (nil, nil) } - let r = await chatSendCmd(.apiConnectContactViaAddress(userId: userId, incognito: incognito, contactId: contactId)) - if case let .sentInvitationToContact(_, contact, _) = r { return (contact, nil) } - logger.error("apiConnectContactViaAddress error: \(responseError(r))") + let r: APIResult = await chatApiSendCmd(.apiConnectContactViaAddress(userId: userId, incognito: incognito, contactId: contactId)) + if case let .result(.sentInvitationToContact(_, contact, _)) = r { return (contact, nil) } + logger.error("apiConnectContactViaAddress error: \(responseError(r.unexpected))") let alert = connectionErrorAlert(r) return (nil, alert) } -func apiDeleteChat(type: ChatType, id: Int64, notify: Bool? = nil) async throws { +func apiDeleteChat(type: ChatType, id: Int64, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async throws { let chatId = type.rawValue + id.description DispatchQueue.main.async { ChatModel.shared.deletedChats.insert(chatId) } defer { DispatchQueue.main.async { ChatModel.shared.deletedChats.remove(chatId) } } - let r = await chatSendCmd(.apiDeleteChat(type: type, id: id, notify: notify), bgTask: false) + let r: ChatResponse1 = try await chatSendCmd(.apiDeleteChat(type: type, id: id, chatDeleteMode: chatDeleteMode), bgTask: false) if case .direct = type, case .contactDeleted = r { return } if case .contactConnection = type, case .contactConnectionDeleted = r { return } if case .group = type, case .groupDeletedUser = r { return } - throw r + throw r.unexpected } -func deleteChat(_ chat: Chat, notify: Bool? = nil) async { +func apiDeleteContact(id: Int64, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async throws -> Contact { + let type: ChatType = .direct + let chatId = type.rawValue + id.description + if case .full = chatDeleteMode { + DispatchQueue.main.async { ChatModel.shared.deletedChats.insert(chatId) } + } + defer { + if case .full = chatDeleteMode { + DispatchQueue.main.async { ChatModel.shared.deletedChats.remove(chatId) } + } + } + let r: ChatResponse1 = try await chatSendCmd(.apiDeleteChat(type: type, id: id, chatDeleteMode: chatDeleteMode), bgTask: false) + if case let .contactDeleted(_, contact) = r { return contact } + throw r.unexpected +} + +func deleteChat(_ chat: Chat, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async { do { let cInfo = chat.chatInfo - try await apiDeleteChat(type: cInfo.chatType, id: cInfo.apiId, notify: notify) - DispatchQueue.main.async { ChatModel.shared.removeChat(cInfo.id) } + try await apiDeleteChat(type: cInfo.chatType, id: cInfo.apiId, chatDeleteMode: chatDeleteMode) + await MainActor.run { ChatModel.shared.removeChat(cInfo.id) } } catch let error { logger.error("deleteChat apiDeleteChat error: \(responseError(error))") AlertManager.shared.showAlertMsg( @@ -752,10 +1045,43 @@ func deleteChat(_ chat: Chat, notify: Bool? = nil) async { } } +func deleteContactChat(_ chat: Chat, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async -> Alert? { + do { + let cInfo = chat.chatInfo + let ct = try await apiDeleteContact(id: cInfo.apiId, chatDeleteMode: chatDeleteMode) + await MainActor.run { + switch chatDeleteMode { + case .full: + ChatModel.shared.removeChat(cInfo.id) + case .entity: + ChatModel.shared.removeChat(cInfo.id) + ChatModel.shared.addChat(Chat( + chatInfo: .direct(contact: ct), + chatItems: chat.chatItems + )) + case .messages: + ChatModel.shared.removeChat(cInfo.id) + ChatModel.shared.addChat(Chat( + chatInfo: .direct(contact: ct), + chatItems: [] + )) + } + } + } catch let error { + logger.error("deleteContactChat apiDeleteContact error: \(responseError(error))") + return mkAlert( + title: "Error deleting chat!", + message: "Error: \(responseError(error))" + ) + } + return nil +} + + func apiClearChat(type: ChatType, id: Int64) async throws -> ChatInfo { - let r = await chatSendCmd(.apiClearChat(type: type, id: id), bgTask: false) + let r: ChatResponse1 = try await chatSendCmd(.apiClearChat(type: type, id: id), bgTask: false) if case let .chatCleared(_, updatedChatInfo) = r { return updatedChatInfo } - throw r + throw r.unexpected } func clearChat(_ chat: Chat) async { @@ -770,98 +1096,125 @@ func clearChat(_ chat: Chat) async { func apiListContacts() throws -> [Contact] { let userId = try currentUserId("apiListContacts") - let r = chatSendCmdSync(.apiListContacts(userId: userId)) + let r: ChatResponse1 = try chatSendCmdSync(.apiListContacts(userId: userId)) if case let .contactsList(_, contacts) = r { return contacts } - throw r + throw r.unexpected } func apiUpdateProfile(profile: Profile) async throws -> (Profile, [Contact])? { let userId = try currentUserId("apiUpdateProfile") - let r = await chatSendCmd(.apiUpdateProfile(userId: userId, profile: profile)) + let r: APIResult = await chatApiSendCmd(.apiUpdateProfile(userId: userId, profile: profile)) switch r { - case .userProfileNoChange: return (profile, []) - case let .userProfileUpdated(_, _, toProfile, updateSummary): return (toProfile, updateSummary.changedContacts) - case .chatCmdError(_, .errorStore(.duplicateName)): return nil; - default: throw r + case .result(.userProfileNoChange): return (profile, []) + case let .result(.userProfileUpdated(_, _, toProfile, updateSummary)): return (toProfile, updateSummary.changedContacts) + case .error(.errorStore(.duplicateName)): return nil; + default: throw r.unexpected } } func apiSetProfileAddress(on: Bool) async throws -> User? { let userId = try currentUserId("apiSetProfileAddress") - let r = await chatSendCmd(.apiSetProfileAddress(userId: userId, on: on)) + let r: ChatResponse1 = try await chatSendCmd(.apiSetProfileAddress(userId: userId, on: on)) switch r { case .userProfileNoChange: return nil case let .userProfileUpdated(user, _, _, _): return user - default: throw r + default: throw r.unexpected } } func apiSetContactPrefs(contactId: Int64, preferences: Preferences) async throws -> Contact? { - let r = await chatSendCmd(.apiSetContactPrefs(contactId: contactId, preferences: preferences)) + let r: ChatResponse1 = try await chatSendCmd(.apiSetContactPrefs(contactId: contactId, preferences: preferences)) if case let .contactPrefsUpdated(_, _, toContact) = r { return toContact } - throw r + throw r.unexpected } func apiSetContactAlias(contactId: Int64, localAlias: String) async throws -> Contact? { - let r = await chatSendCmd(.apiSetContactAlias(contactId: contactId, localAlias: localAlias)) + let r: ChatResponse1 = try await chatSendCmd(.apiSetContactAlias(contactId: contactId, localAlias: localAlias)) if case let .contactAliasUpdated(_, toContact) = r { return toContact } - throw r + throw r.unexpected +} + +func apiSetGroupAlias(groupId: Int64, localAlias: String) async throws -> GroupInfo? { + let r: ChatResponse1 = try await chatSendCmd(.apiSetGroupAlias(groupId: groupId, localAlias: localAlias)) + if case let .groupAliasUpdated(_, toGroup) = r { return toGroup } + throw r.unexpected } func apiSetConnectionAlias(connId: Int64, localAlias: String) async throws -> PendingContactConnection? { - let r = await chatSendCmd(.apiSetConnectionAlias(connId: connId, localAlias: localAlias)) + let r: ChatResponse1 = try await chatSendCmd(.apiSetConnectionAlias(connId: connId, localAlias: localAlias)) if case let .connectionAliasUpdated(_, toConnection) = r { return toConnection } - throw r + throw r.unexpected } -func apiCreateUserAddress() async throws -> String { +func apiSetUserUIThemes(userId: Int64, themes: ThemeModeOverrides?) async -> Bool { + do { + try await sendCommandOkResp(.apiSetUserUIThemes(userId: userId, themes: themes)) + return true + } catch { + logger.error("apiSetUserUIThemes bad response: \(responseError(error))") + return false + } +} + +func apiSetChatUIThemes(chatId: ChatId, themes: ThemeModeOverrides?) async -> Bool { + do { + try await sendCommandOkResp(.apiSetChatUIThemes(chatId: chatId, themes: themes)) + return true + } catch { + logger.error("apiSetChatUIThemes bad response: \(responseError(error))") + return false + } +} + + +func apiCreateUserAddress(short: Bool) async throws -> CreatedConnLink { let userId = try currentUserId("apiCreateUserAddress") - let r = await chatSendCmd(.apiCreateMyAddress(userId: userId)) - if case let .userContactLinkCreated(_, connReq) = r { return connReq } - throw r + let r: ChatResponse1 = try await chatSendCmd(.apiCreateMyAddress(userId: userId, short: short)) + if case let .userContactLinkCreated(_, connLink) = r { return connLink } + throw r.unexpected } func apiDeleteUserAddress() async throws -> User? { let userId = try currentUserId("apiDeleteUserAddress") - let r = await chatSendCmd(.apiDeleteMyAddress(userId: userId)) + let r: ChatResponse1 = try await chatSendCmd(.apiDeleteMyAddress(userId: userId)) if case let .userContactLinkDeleted(user) = r { return user } - throw r + throw r.unexpected } func apiGetUserAddress() throws -> UserContactLink? { let userId = try currentUserId("apiGetUserAddress") - return try userAddressResponse(chatSendCmdSync(.apiShowMyAddress(userId: userId))) + return try userAddressResponse(chatApiSendCmdSync(.apiShowMyAddress(userId: userId))) } func apiGetUserAddressAsync() async throws -> UserContactLink? { let userId = try currentUserId("apiGetUserAddressAsync") - return try userAddressResponse(await chatSendCmd(.apiShowMyAddress(userId: userId))) + return try userAddressResponse(await chatApiSendCmd(.apiShowMyAddress(userId: userId))) } -private func userAddressResponse(_ r: ChatResponse) throws -> UserContactLink? { +private func userAddressResponse(_ r: APIResult) throws -> UserContactLink? { switch r { - case let .userContactLink(_, contactLink): return contactLink - case .chatCmdError(_, chatError: .errorStore(storeError: .userContactLinkNotFound)): return nil - default: throw r + case let .result(.userContactLink(_, contactLink)): return contactLink + case .error(.errorStore(storeError: .userContactLinkNotFound)): return nil + default: throw r.unexpected } } func userAddressAutoAccept(_ autoAccept: AutoAccept?) async throws -> UserContactLink? { let userId = try currentUserId("userAddressAutoAccept") - let r = await chatSendCmd(.apiAddressAutoAccept(userId: userId, autoAccept: autoAccept)) + let r: APIResult = await chatApiSendCmd(.apiAddressAutoAccept(userId: userId, autoAccept: autoAccept)) switch r { - case let .userContactLinkUpdated(_, contactLink): return contactLink - case .chatCmdError(_, chatError: .errorStore(storeError: .userContactLinkNotFound)): return nil - default: throw r + case let .result(.userContactLinkUpdated(_, contactLink)): return contactLink + case .error(.errorStore(storeError: .userContactLinkNotFound)): return nil + default: throw r.unexpected } } func apiAcceptContactRequest(incognito: Bool, contactReqId: Int64) async -> Contact? { - let r = await chatSendCmd(.apiAcceptContact(incognito: incognito, contactReqId: contactReqId)) + let r: APIResult = await chatApiSendCmd(.apiAcceptContact(incognito: incognito, contactReqId: contactReqId)) let am = AlertManager.shared - if case let .acceptingContactRequest(_, contact) = r { return contact } - if case .chatCmdError(_, .errorAgent(.SMP(.AUTH))) = r { + if case let .result(.acceptingContactRequest(_, contact)) = r { return contact } + if case .error(.errorAgent(.SMP(_, .AUTH))) = r { am.showAlertMsg( title: "Connection error (AUTH)", message: "Sender may have deleted the connection request." @@ -872,20 +1225,24 @@ func apiAcceptContactRequest(incognito: Bool, contactReqId: Int64) async -> Cont logger.error("apiAcceptContactRequest error: \(String(describing: r))") am.showAlertMsg( title: "Error accepting contact request", - message: "Error: \(String(describing: r))" + message: "Error: \(responseError(r.unexpected))" ) } return nil } func apiRejectContactRequest(contactReqId: Int64) async throws { - let r = await chatSendCmd(.apiRejectContact(contactReqId: contactReqId)) + let r: ChatResponse1 = try await chatSendCmd(.apiRejectContact(contactReqId: contactReqId)) if case .contactRequestRejected = r { return } - throw r + throw r.unexpected } -func apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64)) async throws { - try await sendCommandOkResp(.apiChatRead(type: type, id: id, itemRange: itemRange)) +func apiChatRead(type: ChatType, id: Int64) async throws { + try await sendCommandOkResp(.apiChatRead(type: type, id: id)) +} + +func apiChatItemsRead(type: ChatType, id: Int64, itemIds: [Int64]) async throws { + try await sendCommandOkResp(.apiChatItemsRead(type: type, id: id, itemIds: itemIds)) } func apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) async throws { @@ -893,73 +1250,155 @@ func apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) async throws { } func uploadStandaloneFile(user: any UserLike, file: CryptoFile, ctrl: chat_ctrl? = nil) async -> (FileTransferMeta?, String?) { - let r = await chatSendCmd(.apiUploadStandaloneFile(userId: user.userId, file: file), ctrl) - if case let .sndStandaloneFileCreated(_, fileTransferMeta) = r { + let r: APIResult = await chatApiSendCmd(.apiUploadStandaloneFile(userId: user.userId, file: file), ctrl: ctrl) + if case let .result(.sndStandaloneFileCreated(_, fileTransferMeta)) = r { return (fileTransferMeta, nil) } else { - logger.error("uploadStandaloneFile error: \(String(describing: r))") - return (nil, String(describing: r)) + let err = responseError(r.unexpected) + logger.error("uploadStandaloneFile error: \(err)") + return (nil, err) } } func downloadStandaloneFile(user: any UserLike, url: String, file: CryptoFile, ctrl: chat_ctrl? = nil) async -> (RcvFileTransfer?, String?) { - let r = await chatSendCmd(.apiDownloadStandaloneFile(userId: user.userId, url: url, file: file), ctrl) - if case let .rcvStandaloneFileCreated(_, rcvFileTransfer) = r { + let r: APIResult = await chatApiSendCmd(.apiDownloadStandaloneFile(userId: user.userId, url: url, file: file), ctrl: ctrl) + if case let .result(.rcvStandaloneFileCreated(_, rcvFileTransfer)) = r { return (rcvFileTransfer, nil) } else { - logger.error("downloadStandaloneFile error: \(String(describing: r))") - return (nil, String(describing: r)) + let err = responseError(r.unexpected) + logger.error("downloadStandaloneFile error: \(err)") + return (nil, err) } } func standaloneFileInfo(url: String, ctrl: chat_ctrl? = nil) async -> MigrationFileLinkData? { - let r = await chatSendCmd(.apiStandaloneFileInfo(url: url), ctrl) - if case let .standaloneFileInfo(fileMeta) = r { + let r: APIResult = await chatApiSendCmd(.apiStandaloneFileInfo(url: url), ctrl: ctrl) + if case let .result(.standaloneFileInfo(fileMeta)) = r { return fileMeta } else { - logger.error("standaloneFileInfo error: \(String(describing: r))") + logger.error("standaloneFileInfo error: \(responseError(r.unexpected))") return nil } } -func receiveFile(user: any UserLike, fileId: Int64, auto: Bool = false) async { - if let chatItem = await apiReceiveFile(fileId: fileId, encrypted: privacyEncryptLocalFilesGroupDefault.get(), auto: auto) { - await chatItemSimpleUpdate(user, chatItem) - } +func receiveFile(user: any UserLike, fileId: Int64, userApprovedRelays: Bool = false, auto: Bool = false) async { + await receiveFiles( + user: user, + fileIds: [fileId], + userApprovedRelays: userApprovedRelays, + auto: auto + ) } -func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil, auto: Bool = false) async -> AChatItem? { - let r = await chatSendCmd(.receiveFile(fileId: fileId, encrypted: encrypted, inline: inline)) - let am = AlertManager.shared - if case let .rcvFileAccepted(_, chatItem) = r { return chatItem } - if case .rcvFileAcceptedSndCancelled = r { - logger.debug("apiReceiveFile error: sender cancelled file transfer") - if !auto { - am.showAlertMsg( - title: "Cannot receive file", - message: "Sender cancelled file transfer." +func receiveFiles(user: any UserLike, fileIds: [Int64], userApprovedRelays: Bool = false, auto: Bool = false) async { + var fileIdsToApprove: [Int64] = [] + var srvsToApprove: Set = [] + var otherFileErrs: [APIResult] = [] + + for fileId in fileIds { + let r: APIResult = await chatApiSendCmd( + .receiveFile( + fileId: fileId, + userApprovedRelays: userApprovedRelays || !privacyAskToApproveRelaysGroupDefault.get(), + encrypted: privacyEncryptLocalFilesGroupDefault.get(), + inline: nil ) - } - } else if let networkErrorAlert = networkErrorAlert(r) { - logger.error("apiReceiveFile network error: \(String(describing: r))") - am.showAlert(networkErrorAlert) - } else { - switch chatError(r) { - case .fileCancelled: - logger.debug("apiReceiveFile ignoring fileCancelled error") - case .fileAlreadyReceiving: - logger.debug("apiReceiveFile ignoring fileAlreadyReceiving error") + ) + switch r { + case let .result(.rcvFileAccepted(_, chatItem)): + await chatItemSimpleUpdate(user, chatItem) + // TODO when aChatItem added + // case let .rcvFileAcceptedSndCancelled(user, aChatItem, _): + // await chatItemSimpleUpdate(user, aChatItem) + // Task { cleanupFile(aChatItem) } + case let .error(.error(.fileNotApproved(fileId, unknownServers))): + fileIdsToApprove.append(fileId) + srvsToApprove.formUnion(unknownServers) default: - logger.error("apiReceiveFile error: \(String(describing: r))") - am.showAlertMsg( - title: "Error receiving file", - message: "Error: \(String(describing: r))" - ) + otherFileErrs.append(r) } } - return nil + + if !auto { + let otherErrsStr = fileErrorStrs(otherFileErrs) + // If there are not approved files, alert is shown the same way both in case of singular and plural files reception + if !fileIdsToApprove.isEmpty { + let srvs = srvsToApprove + .map { s in + if let srv = parseServerAddress(s), !srv.hostnames.isEmpty { + srv.hostnames[0] + } else { + serverHost(s) + } + } + .sorted() + .joined(separator: ", ") + let fIds = fileIdsToApprove + await MainActor.run { + showAlert( + title: NSLocalizedString("Unknown servers!", comment: "alert title"), + message: ( + String.localizedStringWithFormat(NSLocalizedString("Without Tor or VPN, your IP address will be visible to these XFTP relays: %@.", comment: "alert message"), srvs) + + (otherErrsStr != "" ? "\n\n" + String.localizedStringWithFormat(NSLocalizedString("Other file errors:\n%@", comment: "alert message"), otherErrsStr) : "") + ), + buttonTitle: NSLocalizedString("Download", comment: "alert button"), + buttonAction: { + Task { + logger.debug("apiReceiveFile fileNotApproved alert - in Task") + if let user = ChatModel.shared.currentUser { + await receiveFiles(user: user, fileIds: fIds, userApprovedRelays: true) + } + } + }, + cancelButton: true + ) + } + } else if otherFileErrs.count == 1 { // If there is a single other error, we differentiate on it + let errorResponse = otherFileErrs.first! + switch errorResponse { + case let .result(.rcvFileAcceptedSndCancelled(_, rcvFileTransfer)): + logger.debug("receiveFiles error: sender cancelled file transfer \(rcvFileTransfer.fileId)") + await MainActor.run { + showAlert( + NSLocalizedString("Cannot receive file", comment: "alert title"), + message: NSLocalizedString("Sender cancelled file transfer.", comment: "alert message") + ) + } + case .error(.error(.fileCancelled)), .error(.error(.fileAlreadyReceiving)): + logger.debug("receiveFiles ignoring FileCancelled or FileAlreadyReceiving error") + default: + await MainActor.run { + showAlert( + NSLocalizedString("Error receiving file", comment: "alert title"), + message: responseError(errorResponse.unexpected) + ) + } + } + } else if otherFileErrs.count > 1 { // If there are multiple other errors, we show general alert + await MainActor.run { + showAlert( + NSLocalizedString("Error receiving file", comment: "alert title"), + message: String.localizedStringWithFormat(NSLocalizedString("File errors:\n%@", comment: "alert message"), otherErrsStr) + ) + } + } + } + + func fileErrorStrs(_ errs: [APIResult]) -> String { + var errStr = "" + if errs.count >= 1 { + errStr = String(describing: errs[0].unexpected) + } + if errs.count >= 2 { + errStr += "\n\(String(describing: errs[1].unexpected))" + } + if errs.count > 2 { + errStr += "\nand \(errs.count - 2) other error(s)" + } + return errStr + } } - + func cancelFile(user: User, fileId: Int64) async { if let chatItem = await apiCancelFile(fileId: fileId) { await chatItemSimpleUpdate(user, chatItem) @@ -968,12 +1407,12 @@ func cancelFile(user: User, fileId: Int64) async { } func apiCancelFile(fileId: Int64, ctrl: chat_ctrl? = nil) async -> AChatItem? { - let r = await chatSendCmd(.cancelFile(fileId: fileId), ctrl) + let r: APIResult = await chatApiSendCmd(.cancelFile(fileId: fileId), ctrl: ctrl) switch r { - case let .sndFileCancelled(_, chatItem, _, _) : return chatItem - case let .rcvFileCancelled(_, chatItem, _) : return chatItem + case let .result(.sndFileCancelled(_, chatItem, _, _)) : return chatItem + case let .result(.rcvFileCancelled(_, chatItem, _)) : return chatItem default: - logger.error("apiCancelFile error: \(String(describing: r))") + logger.error("apiCancelFile error: \(responseError(r.unexpected))") return nil } } @@ -983,9 +1422,9 @@ func setLocalDeviceName(_ displayName: String) throws { } func connectRemoteCtrl(desktopAddress: String) async throws -> (RemoteCtrlInfo?, CtrlAppInfo, String) { - let r = await chatSendCmd(.connectRemoteCtrl(xrcpInvitation: desktopAddress)) + let r: ChatResponse2 = try await chatSendCmd(.connectRemoteCtrl(xrcpInvitation: desktopAddress)) if case let .remoteCtrlConnecting(rc_, ctrlAppInfo, v) = r { return (rc_, ctrlAppInfo, v) } - throw r + throw r.unexpected } func findKnownRemoteCtrl() async throws { @@ -993,21 +1432,21 @@ func findKnownRemoteCtrl() async throws { } func confirmRemoteCtrl(_ rcId: Int64) async throws -> (RemoteCtrlInfo?, CtrlAppInfo, String) { - let r = await chatSendCmd(.confirmRemoteCtrl(remoteCtrlId: rcId)) + let r: ChatResponse2 = try await chatSendCmd(.confirmRemoteCtrl(remoteCtrlId: rcId)) if case let .remoteCtrlConnecting(rc_, ctrlAppInfo, v) = r { return (rc_, ctrlAppInfo, v) } - throw r + throw r.unexpected } func verifyRemoteCtrlSession(_ sessCode: String) async throws -> RemoteCtrlInfo { - let r = await chatSendCmd(.verifyRemoteCtrlSession(sessionCode: sessCode)) + let r: ChatResponse2 = try await chatSendCmd(.verifyRemoteCtrlSession(sessionCode: sessCode)) if case let .remoteCtrlConnected(rc) = r { return rc } - throw r + throw r.unexpected } func listRemoteCtrls() throws -> [RemoteCtrlInfo] { - let r = chatSendCmdSync(.listRemoteCtrls) + let r: ChatResponse2 = try chatSendCmdSync(.listRemoteCtrls) if case let .remoteCtrlList(rcInfo) = r { return rcInfo } - throw r + throw r.unexpected } func stopRemoteCtrl() async throws { @@ -1018,19 +1457,10 @@ func deleteRemoteCtrl(_ rcId: Int64) async throws { try await sendCommandOkResp(.deleteRemoteCtrl(remoteCtrlId: rcId)) } -func networkErrorAlert(_ r: ChatResponse) -> Alert? { - switch r { - case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TIMEOUT))): - return mkAlert( - title: "Connection timeout", - message: "Please check your network connection with \(serverHostname(addr)) and try again." - ) - case let .chatCmdError(_, .errorAgent(.BROKER(addr, .NETWORK))): - return mkAlert( - title: "Connection error", - message: "Please check your network connection with \(serverHostname(addr)) and try again." - ) - default: +func networkErrorAlert(_ res: APIResult) -> Alert? { + if case let .error(e) = res, let alert = getNetworkErrorAlert(e) { + return mkAlert(title: alert.title, message: alert.message) + } else { return nil } } @@ -1038,7 +1468,17 @@ func networkErrorAlert(_ r: ChatResponse) -> Alert? { func acceptContactRequest(incognito: Bool, contactRequest: UserContactRequest) async { if let contact = await apiAcceptContactRequest(incognito: incognito, contactReqId: contactRequest.apiId) { let chat = Chat(chatInfo: ChatInfo.direct(contact: contact), chatItems: []) - DispatchQueue.main.async { ChatModel.shared.replaceChat(contactRequest.id, chat) } + await MainActor.run { + ChatModel.shared.replaceChat(contactRequest.id, chat) + NetworkModel.shared.setContactNetworkStatus(contact, .connected) + } + if contact.sndReady { + DispatchQueue.main.async { + dismissAllSheets(animated: true) { + ItemsModel.shared.loadOpenChat(chat.id) + } + } + } } } @@ -1079,10 +1519,16 @@ func apiEndCall(_ contact: Contact) async throws { try await sendCommandOkResp(.apiEndCall(contact: contact)) } -func apiGetCallInvitations() throws -> [RcvCallInvitation] { - let r = chatSendCmdSync(.apiGetCallInvitations) +func apiGetCallInvitationsSync() throws -> [RcvCallInvitation] { + let r: ChatResponse2 = try chatSendCmdSync(.apiGetCallInvitations) if case let .callInvitations(invs) = r { return invs } - throw r + throw r.unexpected +} + +func apiGetCallInvitations() async throws -> [RcvCallInvitation] { + let r: ChatResponse2 = try await chatSendCmd(.apiGetCallInvitations) + if case let .callInvitations(invs) = r { return invs } + throw r.unexpected } func apiCallStatus(_ contact: Contact, _ status: String) async throws { @@ -1094,20 +1540,18 @@ func apiCallStatus(_ contact: Contact, _ status: String) async throws { } func apiGetNetworkStatuses() throws -> [ConnNetworkStatus] { - let r = chatSendCmdSync(.apiGetNetworkStatuses) + let r: ChatResponse1 = try chatSendCmdSync(.apiGetNetworkStatuses) if case let .networkStatuses(_, statuses) = r { return statuses } - throw r + throw r.unexpected } -func markChatRead(_ chat: Chat, aboveItem: ChatItem? = nil) async { +func markChatRead(_ chat: Chat) async { do { if chat.chatStats.unreadCount > 0 { - let minItemId = chat.chatStats.minUnreadItemId - let itemRange = (minItemId, aboveItem?.id ?? chat.chatItems.last?.id ?? minItemId) let cInfo = chat.chatInfo - try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId, itemRange: itemRange) + try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId) await MainActor.run { - withAnimation { ChatModel.shared.markChatItemsRead(cInfo, aboveItem: aboveItem) } + withAnimation { ChatModel.shared.markAllChatItemsRead(cInfo) } } } if chat.chatStats.unreadChat { @@ -1130,39 +1574,40 @@ func markChatUnread(_ chat: Chat, unreadChat: Bool = true) async { } } -func apiMarkChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) async { +func apiMarkChatItemsRead(_ cInfo: ChatInfo, _ itemIds: [ChatItem.ID], mentionsRead: Int) async { do { - logger.debug("apiMarkChatItemRead: \(cItem.id)") - try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId, itemRange: (cItem.id, cItem.id)) - await MainActor.run { ChatModel.shared.markChatItemRead(cInfo, cItem) } + try await apiChatItemsRead(type: cInfo.chatType, id: cInfo.apiId, itemIds: itemIds) + DispatchQueue.main.async { + ChatModel.shared.markChatItemsRead(cInfo, itemIds, mentionsRead) + } } catch { - logger.error("apiMarkChatItemRead apiChatRead error: \(responseError(error))") + logger.error("apiChatItemsRead error: \(responseError(error))") } } -private func sendCommandOkResp(_ cmd: ChatCommand, _ ctrl: chat_ctrl? = nil) async throws { - let r = await chatSendCmd(cmd, ctrl) +private func sendCommandOkResp(_ cmd: ChatCommand, ctrl: chat_ctrl? = nil) async throws { + let r: ChatResponse2 = try await chatSendCmd(cmd, ctrl: ctrl) if case .cmdOk = r { return } - throw r + throw r.unexpected } private func sendCommandOkRespSync(_ cmd: ChatCommand) throws { - let r = chatSendCmdSync(cmd) + let r: ChatResponse2 = try chatSendCmdSync(cmd) if case .cmdOk = r { return } - throw r + throw r.unexpected } func apiNewGroup(incognito: Bool, groupProfile: GroupProfile) throws -> GroupInfo { let userId = try currentUserId("apiNewGroup") - let r = chatSendCmdSync(.apiNewGroup(userId: userId, incognito: incognito, groupProfile: groupProfile)) + let r: ChatResponse2 = try chatSendCmdSync(.apiNewGroup(userId: userId, incognito: incognito, groupProfile: groupProfile)) if case let .groupCreated(_, groupInfo) = r { return groupInfo } - throw r + throw r.unexpected } func apiAddMember(_ groupId: Int64, _ contactId: Int64, _ memberRole: GroupMemberRole) async throws -> GroupMember { - let r = await chatSendCmd(.apiAddMember(groupId: groupId, contactId: contactId, memberRole: memberRole)) + let r: ChatResponse2 = try await chatSendCmd(.apiAddMember(groupId: groupId, contactId: contactId, memberRole: memberRole)) if case let .sentGroupInvitation(_, _, _, member) = r { return member } - throw r + throw r.unexpected } enum JoinGroupResult { @@ -1172,31 +1617,31 @@ enum JoinGroupResult { } func apiJoinGroup(_ groupId: Int64) async throws -> JoinGroupResult { - let r = await chatSendCmd(.apiJoinGroup(groupId: groupId)) + let r: APIResult = await chatApiSendCmd(.apiJoinGroup(groupId: groupId)) switch r { - case let .userAcceptedGroupSent(_, groupInfo, _): return .joined(groupInfo: groupInfo) - case .chatCmdError(_, .errorAgent(.SMP(.AUTH))): return .invitationRemoved - case .chatCmdError(_, .errorStore(.groupNotFound)): return .groupNotFound - default: throw r + case let .result(.userAcceptedGroupSent(_, groupInfo, _)): return .joined(groupInfo: groupInfo) + case .error(.errorAgent(.SMP(_, .AUTH))): return .invitationRemoved + case .error(.errorStore(.groupNotFound)): return .groupNotFound + default: throw r.unexpected } } -func apiRemoveMember(_ groupId: Int64, _ memberId: Int64) async throws -> GroupMember { - let r = await chatSendCmd(.apiRemoveMember(groupId: groupId, memberId: memberId), bgTask: false) - if case let .userDeletedMember(_, _, member) = r { return member } - throw r +func apiRemoveMembers(_ groupId: Int64, _ memberIds: [Int64], _ withMessages: Bool = false) async throws -> [GroupMember] { + let r: ChatResponse2 = try await chatSendCmd(.apiRemoveMembers(groupId: groupId, memberIds: memberIds, withMessages: withMessages), bgTask: false) + if case let .userDeletedMembers(_, _, members, withMessages) = r { return members } + throw r.unexpected } -func apiMemberRole(_ groupId: Int64, _ memberId: Int64, _ memberRole: GroupMemberRole) async throws -> GroupMember { - let r = await chatSendCmd(.apiMemberRole(groupId: groupId, memberId: memberId, memberRole: memberRole), bgTask: false) - if case let .memberRoleUser(_, _, member, _, _) = r { return member } - throw r +func apiMembersRole(_ groupId: Int64, _ memberIds: [Int64], _ memberRole: GroupMemberRole) async throws -> [GroupMember] { + let r: ChatResponse2 = try await chatSendCmd(.apiMembersRole(groupId: groupId, memberIds: memberIds, memberRole: memberRole), bgTask: false) + if case let .membersRoleUser(_, _, members, _) = r { return members } + throw r.unexpected } -func apiBlockMemberForAll(_ groupId: Int64, _ memberId: Int64, _ blocked: Bool) async throws -> GroupMember { - let r = await chatSendCmd(.apiBlockMemberForAll(groupId: groupId, memberId: memberId, blocked: blocked), bgTask: false) - if case let .memberBlockedForAllUser(_, _, member, _) = r { return member } - throw r +func apiBlockMembersForAll(_ groupId: Int64, _ memberIds: [Int64], _ blocked: Bool) async throws -> [GroupMember] { + let r: ChatResponse2 = try await chatSendCmd(.apiBlockMembersForAll(groupId: groupId, memberIds: memberIds, blocked: blocked), bgTask: false) + if case let .membersBlockedForAllUser(_, _, members, _) = r { return members } + throw r.unexpected } func leaveGroup(_ groupId: Int64) async { @@ -1209,14 +1654,15 @@ func leaveGroup(_ groupId: Int64) async { } func apiLeaveGroup(_ groupId: Int64) async throws -> GroupInfo { - let r = await chatSendCmd(.apiLeaveGroup(groupId: groupId), bgTask: false) + let r: ChatResponse2 = try await chatSendCmd(.apiLeaveGroup(groupId: groupId), bgTask: false) if case let .leftMemberUser(_, groupInfo) = r { return groupInfo } - throw r + throw r.unexpected } +// use ChatModel's loadGroupMembers from views func apiListMembers(_ groupId: Int64) async -> [GroupMember] { - let r = await chatSendCmd(.apiListMembers(groupId: groupId)) - if case let .groupMembers(_, group) = r { return group.members } + let r: APIResult = await chatApiSendCmd(.apiListMembers(groupId: groupId)) + if case let .result(.groupMembers(_, group)) = r { return group.members } return [] } @@ -1224,61 +1670,82 @@ func filterMembersToAdd(_ ms: [GMember]) -> [Contact] { let memberContactIds = ms.compactMap{ m in m.wrapped.memberCurrent ? m.wrapped.memberContactId : nil } return ChatModel.shared.chats .compactMap{ $0.chatInfo.contact } - .filter{ c in c.ready && c.active && !memberContactIds.contains(c.apiId) } + .filter{ c in c.sendMsgEnabled && !c.nextSendGrpInv && !memberContactIds.contains(c.apiId) } .sorted{ $0.displayName.lowercased() < $1.displayName.lowercased() } } func apiUpdateGroup(_ groupId: Int64, _ groupProfile: GroupProfile) async throws -> GroupInfo { - let r = await chatSendCmd(.apiUpdateGroupProfile(groupId: groupId, groupProfile: groupProfile)) + let r: ChatResponse2 = try await chatSendCmd(.apiUpdateGroupProfile(groupId: groupId, groupProfile: groupProfile)) if case let .groupUpdated(_, toGroup) = r { return toGroup } - throw r + throw r.unexpected } -func apiCreateGroupLink(_ groupId: Int64, memberRole: GroupMemberRole = .member) async throws -> (String, GroupMemberRole) { - let r = await chatSendCmd(.apiCreateGroupLink(groupId: groupId, memberRole: memberRole)) - if case let .groupLinkCreated(_, _, connReq, memberRole) = r { return (connReq, memberRole) } - throw r +func apiCreateGroupLink(_ groupId: Int64, memberRole: GroupMemberRole = .member) async throws -> (CreatedConnLink, GroupMemberRole) { + let short = UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS) + let r: ChatResponse2 = try await chatSendCmd(.apiCreateGroupLink(groupId: groupId, memberRole: memberRole, short: short)) + if case let .groupLinkCreated(_, _, connLink, memberRole) = r { return (connLink, memberRole) } + throw r.unexpected } -func apiGroupLinkMemberRole(_ groupId: Int64, memberRole: GroupMemberRole = .member) async throws -> (String, GroupMemberRole) { - let r = await chatSendCmd(.apiGroupLinkMemberRole(groupId: groupId, memberRole: memberRole)) - if case let .groupLink(_, _, connReq, memberRole) = r { return (connReq, memberRole) } - throw r +func apiGroupLinkMemberRole(_ groupId: Int64, memberRole: GroupMemberRole = .member) async throws -> (CreatedConnLink, GroupMemberRole) { + let r: ChatResponse2 = try await chatSendCmd(.apiGroupLinkMemberRole(groupId: groupId, memberRole: memberRole)) + if case let .groupLink(_, _, connLink, memberRole) = r { return (connLink, memberRole) } + throw r.unexpected } func apiDeleteGroupLink(_ groupId: Int64) async throws { - let r = await chatSendCmd(.apiDeleteGroupLink(groupId: groupId)) + let r: ChatResponse2 = try await chatSendCmd(.apiDeleteGroupLink(groupId: groupId)) if case .groupLinkDeleted = r { return } - throw r + throw r.unexpected } -func apiGetGroupLink(_ groupId: Int64) throws -> (String, GroupMemberRole)? { - let r = chatSendCmdSync(.apiGetGroupLink(groupId: groupId)) +func apiGetGroupLink(_ groupId: Int64) throws -> (CreatedConnLink, GroupMemberRole)? { + let r: APIResult = chatApiSendCmdSync(.apiGetGroupLink(groupId: groupId)) switch r { - case let .groupLink(_, _, connReq, memberRole): - return (connReq, memberRole) - case .chatCmdError(_, chatError: .errorStore(storeError: .groupLinkNotFound)): + case let .result(.groupLink(_, _, connLink, memberRole)): + return (connLink, memberRole) + case .error(.errorStore(storeError: .groupLinkNotFound)): return nil - default: throw r + default: throw r.unexpected } } func apiCreateMemberContact(_ groupId: Int64, _ groupMemberId: Int64) async throws -> Contact { - let r = await chatSendCmd(.apiCreateMemberContact(groupId: groupId, groupMemberId: groupMemberId)) + let r: ChatResponse2 = try await chatSendCmd(.apiCreateMemberContact(groupId: groupId, groupMemberId: groupMemberId)) if case let .newMemberContact(_, contact, _, _) = r { return contact } - throw r + throw r.unexpected } func apiSendMemberContactInvitation(_ contactId: Int64, _ msg: MsgContent) async throws -> Contact { - let r = await chatSendCmd(.apiSendMemberContactInvitation(contactId: contactId, msg: msg), bgDelay: msgDelay) + let r: ChatResponse2 = try await chatSendCmd(.apiSendMemberContactInvitation(contactId: contactId, msg: msg), bgDelay: msgDelay) if case let .newMemberContactSentInv(_, contact, _, _) = r { return contact } - throw r + throw r.unexpected } func apiGetVersion() throws -> CoreVersionInfo { - let r = chatSendCmdSync(.showVersion) + let r: ChatResponse2 = try chatSendCmdSync(.showVersion) if case let .versionInfo(info, _, _) = r { return info } - throw r + throw r.unexpected +} + +func getAgentSubsTotal() async throws -> (SMPServerSubs, Bool) { + let userId = try currentUserId("getAgentSubsTotal") + let r: ChatResponse2 = try await chatSendCmd(.getAgentSubsTotal(userId: userId), log: false) + if case let .agentSubsTotal(_, subsTotal, hasSession) = r { return (subsTotal, hasSession) } + logger.error("getAgentSubsTotal error: \(String(describing: r))") + throw r.unexpected +} + +func getAgentServersSummary() throws -> PresentedServersSummary { + let userId = try currentUserId("getAgentServersSummary") + let r: ChatResponse2 = try chatSendCmdSync(.getAgentServersSummary(userId: userId), log: false) + if case let .agentServersSummary(_, serversSummary) = r { return serversSummary } + logger.error("getAgentServersSummary error: \(String(describing: r))") + throw r.unexpected +} + +func resetAgentServersStats() async throws { + try await sendCommandOkResp(.resetAgentServersStats) } private func currentUserId(_ funcName: String) throws -> Int64 { @@ -1295,16 +1762,25 @@ func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = ni defer { m.ctrlInitInProgress = false } (m.chatDbEncrypted, m.chatDbStatus) = chatMigrateInit(dbKey, confirmMigrations: confirmMigrations) if m.chatDbStatus != .ok { return } + NetworkObserver.shared.restartMonitor() // If we migrated successfully means previous re-encryption process on database level finished successfully too if encryptionStartedDefault.get() { encryptionStartedDefault.set(false) } - try apiSetTempFolder(tempFolder: getTempFilesDirectory().path) - try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path) + try apiSetAppFilePaths(filesFolder: getAppFilesDirectory().path, tempFolder: getTempFilesDirectory().path, assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path) try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get()) - try apiSetPQEncryption(pqExperimentalEnabledDefault.get()) m.chatInitialized = true m.currentUser = try apiGetActiveUser() + m.conditions = try getServerOperatorsSync() + if shouldImportAppSettingsDefault.get() { + do { + let appSettings = try apiGetAppSettings(settings: AppSettings.current.prepareForExport()) + appSettings.importIntoApp() + shouldImportAppSettingsDefault.set(false) + } catch { + logger.error("Error while importing app settings: \(error)") + } + } if m.currentUser == nil { onboardingStageDefault.set(.step1_SimpleXInfo) privacyDeliveryReceiptsSet.set(true) @@ -1349,31 +1825,34 @@ private func chatInitialized(start: Bool, refreshInvitations: Bool) throws { } } -func startChat(refreshInvitations: Bool = true) throws { +func startChat(refreshInvitations: Bool = true, onboarding: Bool = false) throws { logger.debug("startChat") let m = ChatModel.shared try setNetworkConfig(getNetCfg()) - let justStarted = try apiStartChat() + let chatRunning = try apiCheckChatRunning() m.users = try listUsers() - if justStarted { + if !chatRunning { try getUserChatData() NtfManager.shared.setNtfBadgeCount(m.totalUnreadCountForAllUsers()) if (refreshInvitations) { - try refreshCallInvitations() + Task { try await refreshCallInvitations() } } (m.savedToken, m.tokenStatus, m.notificationMode, m.notificationServer) = apiGetNtfToken() + _ = try apiStartChat() // deviceToken is set when AppDelegate.application(didRegisterForRemoteNotificationsWithDeviceToken:) is called, // when it is called before startChat if let token = m.deviceToken { registerToken(token: token) } - withAnimation { - let savedOnboardingStage = onboardingStageDefault.get() - m.onboardingStage = [.step1_SimpleXInfo, .step2_CreateProfile].contains(savedOnboardingStage) && m.users.count == 1 - ? .step3_CreateSimpleXAddress - : savedOnboardingStage - if m.onboardingStage == .onboardingComplete && !privacyDeliveryReceiptsSet.get() { - m.setDeliveryReceipts = true + if !onboarding { + withAnimation { + let savedOnboardingStage = onboardingStageDefault.get() + m.onboardingStage = [.step1_SimpleXInfo, .step2_CreateProfile].contains(savedOnboardingStage) && m.users.count == 1 + ? .step3_ChooseServerOperators + : savedOnboardingStage + if m.onboardingStage == .onboardingComplete && !privacyDeliveryReceiptsSet.get() { + m.setDeliveryReceipts = true + } } } } @@ -1386,8 +1865,7 @@ func startChatWithTemporaryDatabase(ctrl: chat_ctrl) throws -> User? { logger.debug("startChatWithTemporaryDatabase") let migrationActiveUser = try? apiGetActiveUser(ctrl: ctrl) ?? apiCreateActiveUser(Profile(displayName: "Temp", fullName: ""), ctrl: ctrl) try setNetworkConfig(getNetCfg(), ctrl: ctrl) - try apiSetTempFolder(tempFolder: getMigrationTempFilesDirectory().path, ctrl: ctrl) - try apiSetFilesFolder(filesFolder: getMigrationTempFilesDirectory().path, ctrl: ctrl) + try apiSetAppFilePaths(filesFolder: getMigrationTempFilesDirectory().path, tempFolder: getMigrationTempFilesDirectory().path, assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path, ctrl: ctrl) _ = try apiStartChat(ctrl: ctrl) return migrationActiveUser } @@ -1433,24 +1911,37 @@ func getUserChatData() throws { m.userAddress = try apiGetUserAddress() m.chatItemTTL = try getChatItemTTL() let chats = try apiGetChats() - m.chats = chats.map { Chat.init($0) } + let tags = try apiGetChatTags() + m.updateChats(chats) + let tm = ChatTagsModel.shared + tm.activeFilter = nil + tm.userTags = tags + tm.updateChatTags(m.chats) } private func getUserChatDataAsync() async throws { let m = ChatModel.shared + let tm = ChatTagsModel.shared if m.currentUser != nil { let userAddress = try await apiGetUserAddressAsync() let chatItemTTL = try await getChatItemTTLAsync() let chats = try await apiGetChatsAsync() + let tags = try await apiGetChatTagsAsync() await MainActor.run { m.userAddress = userAddress m.chatItemTTL = chatItemTTL - m.chats = chats.map { Chat.init($0) } + m.updateChats(chats) + tm.activeFilter = nil + tm.userTags = tags + tm.updateChatTags(m.chats) } } else { await MainActor.run { m.userAddress = nil - m.chats = [] + m.updateChats([]) + tm.activeFilter = nil + tm.userTags = [] + tm.presetTags = [:] } } } @@ -1460,6 +1951,8 @@ class ChatReceiver { private var receiveMessages = true private var _lastMsgTime = Date.now + var messagesChannel: ((APIResult) -> Void)? = nil + static let shared = ChatReceiver() var lastMsgTime: Date { get { _lastMsgTime } } @@ -1476,7 +1969,15 @@ class ChatReceiver { while self.receiveMessages { if let msg = await chatRecvMsg() { self._lastMsgTime = .now - await processReceivedMsg(msg) + Task { await TerminalItems.shared.addResult(msg) } + switch msg { + case let .result(evt): await processReceivedMsg(evt) + case let .error(err): logger.debug("chatRecvMsg error: \(responseError(err))") + case let .invalid(type, json): logger.debug("chatRecvMsg event: * \(type) \(dataToString(json))") + } + if let messagesChannel { + messagesChannel(msg) + } } _ = try? await Task.sleep(nanoseconds: 7_500_000) } @@ -1490,11 +1991,9 @@ class ChatReceiver { } } -func processReceivedMsg(_ res: ChatResponse) async { - Task { - await TerminalItems.shared.add(.resp(.now, res)) - } +func processReceivedMsg(_ res: ChatEvent) async { let m = ChatModel.shared + let n = NetworkModel.shared logger.debug("processReceivedMsg: \(res.responseType)") switch res { case let .contactDeletedByContact(user, contact): @@ -1517,7 +2016,7 @@ func processReceivedMsg(_ res: ChatResponse) async { NtfManager.shared.notifyContactConnected(user, contact) } await MainActor.run { - m.setContactNetworkStatus(contact, .connected) + n.setContactNetworkStatus(contact, .connected) } case let .contactConnecting(user, contact): if active(user) && contact.directOrUsed { @@ -1529,6 +2028,19 @@ func processReceivedMsg(_ res: ChatResponse) async { } } } + case let .contactSndReady(user, contact): + if active(user) && contact.directOrUsed { + await MainActor.run { + m.updateContact(contact) + if let conn = contact.activeConn { + m.dismissConnReqView(conn.id) + m.removeChat(conn.id) + } + } + } + await MainActor.run { + n.setContactNetworkStatus(contact, .connected) + } case let .receivedContactRequest(user, contactRequest): if active(user) { let cInfo = ChatInfo.contactRequest(contactRequest: contactRequest) @@ -1561,71 +2073,79 @@ func processReceivedMsg(_ res: ChatResponse) async { if active(user) && m.hasChat(mergedContact.id) { await MainActor.run { if m.chatId == mergedContact.id { - m.chatId = intoContact.id + ItemsModel.shared.loadOpenChat(mergedContact.id) } m.removeChat(mergedContact.id) } } - case let .contactsSubscribed(_, contactRefs): - await updateContactsStatus(contactRefs, status: .connected) - case let .contactsDisconnected(_, contactRefs): - await updateContactsStatus(contactRefs, status: .disconnected) - case let .contactSubSummary(_, contactSubscriptions): - await MainActor.run { - for sub in contactSubscriptions { -// no need to update contact here, and it is slow -// if active(user) { -// m.updateContact(sub.contact) -// } - if let err = sub.contactError { - processContactSubError(sub.contact, err) - } else { - m.setContactNetworkStatus(sub.contact, .connected) - } - } - } case let .networkStatus(status, connections): - await MainActor.run { + // dispatch queue to synchronize access + networkStatusesLock.sync { + var ns = n.networkStatuses + // slow loop is on the background thread for cId in connections { - m.networkStatuses[cId] = status + ns[cId] = status + } + // fast model update is on the main thread + DispatchQueue.main.sync { + n.networkStatuses = ns } } case let .networkStatuses(_, statuses): () - await MainActor.run { + // dispatch queue to synchronize access + networkStatusesLock.sync { + var ns = n.networkStatuses + // slow loop is on the background thread for s in statuses { - m.networkStatuses[s.agentConnId] = s.networkStatus + ns[s.agentConnId] = s.networkStatus + } + // fast model update is on the main thread + DispatchQueue.main.sync { + n.networkStatuses = ns } } - case let .newChatItem(user, aChatItem): - let cInfo = aChatItem.chatInfo - let cItem = aChatItem.chatItem - await MainActor.run { - if active(user) { - m.addChatItem(cInfo, cItem) - } else if cItem.isRcvNew && cInfo.ntfsEnabled { - m.increaseUnreadCounter(user: user) + case let .newChatItems(user, chatItems): + for chatItem in chatItems { + let cInfo = chatItem.chatInfo + let cItem = chatItem.chatItem + await MainActor.run { + if active(user) { + m.addChatItem(cInfo, cItem) + if cItem.isActiveReport { + m.increaseGroupReportsCounter(cInfo.id) + } + } else if cItem.isRcvNew && cInfo.ntfsEnabled(chatItem: cItem) { + m.increaseUnreadCounter(user: user) + } + } + if let file = cItem.autoReceiveFile() { + Task { + await receiveFile(user: user, fileId: file.fileId, auto: true) + } + } + if cItem.showNotification { + NtfManager.shared.notifyMessageReceived(user, cInfo, cItem) } } - if let file = cItem.autoReceiveFile() { - Task { - await receiveFile(user: user, fileId: file.fileId, auto: true) + case let .chatItemsStatusesUpdated(user, chatItems): + for chatItem in chatItems { + let cInfo = chatItem.chatInfo + let cItem = chatItem.chatItem + if !cItem.isDeletedContent && active(user) { + await MainActor.run { m.updateChatItem(cInfo, cItem, status: cItem.meta.itemStatus) } } - } - if cItem.showNotification { - NtfManager.shared.notifyMessageReceived(user, cInfo, cItem) - } - case let .chatItemStatusUpdated(user, aChatItem): - let cInfo = aChatItem.chatInfo - let cItem = aChatItem.chatItem - if !cItem.isDeletedContent && active(user) { - await MainActor.run { m.updateChatItem(cInfo, cItem, status: cItem.meta.itemStatus) } - } - if let endTask = m.messageDelivery[cItem.id] { - switch cItem.meta.itemStatus { - case .sndSent: endTask() - case .sndErrorAuth: endTask() - case .sndError: endTask() - default: () + if let endTask = m.messageDelivery[cItem.id] { + switch cItem.meta.itemStatus { + case .sndNew: () + case .sndSent: endTask() + case .sndRcvd: endTask() + case .sndErrorAuth: endTask() + case .sndError: endTask() + case .sndWarning: endTask() + case .rcvNew: () + case .rcvRead: () + case .invalid: () + } } } case let .chatItemUpdated(user, aChatItem): @@ -1636,23 +2156,33 @@ func processReceivedMsg(_ res: ChatResponse) async { m.updateChatItem(r.chatInfo, r.chatReaction.chatItem) } } - case let .chatItemDeleted(user, deletedChatItem, toChatItem, _): + case let .chatItemsDeleted(user, items, _): if !active(user) { - if toChatItem == nil && deletedChatItem.chatItem.isRcvNew && deletedChatItem.chatInfo.ntfsEnabled { - await MainActor.run { - m.decreaseUnreadCounter(user: user) + for item in items { + let d = item.deletedChatItem + if item.toChatItem == nil && d.chatItem.isRcvNew && d.chatInfo.ntfsEnabled(chatItem: d.chatItem) { + await MainActor.run { + m.decreaseUnreadCounter(user: user) + } } } return } await MainActor.run { - if let toChatItem = toChatItem { - _ = m.upsertChatItem(toChatItem.chatInfo, toChatItem.chatItem) - } else { - m.removeChatItem(deletedChatItem.chatInfo, deletedChatItem.chatItem) + for item in items { + if let toChatItem = item.toChatItem { + _ = m.upsertChatItem(toChatItem.chatInfo, toChatItem.chatItem) + } else { + m.removeChatItem(item.deletedChatItem.chatInfo, item.deletedChatItem.chatItem) + } + if item.deletedChatItem.chatItem.isActiveReport { + m.decreaseGroupReportsCounter(item.deletedChatItem.chatInfo.id) + } } } + case let .groupChatItemsDeleted(user, groupInfo, chatItemIDs, _, member_): + await groupChatItemsDeleted(user, groupInfo, chatItemIDs, member_) case let .receivedGroupInvitation(user, groupInfo, _, _): if active(user) { await MainActor.run { @@ -1672,7 +2202,7 @@ func processReceivedMsg(_ res: ChatResponse) async { } case let .groupLinkConnecting(user, groupInfo, hostMember): if !active(user) { return } - + await MainActor.run { m.updateGroup(groupInfo) if let hostConn = hostMember.activeConn { @@ -1680,22 +2210,40 @@ func processReceivedMsg(_ res: ChatResponse) async { m.removeChat(hostConn.id) } } + case let .businessLinkConnecting(user, groupInfo, _, fromContact): + if !active(user) { return } + + await MainActor.run { + m.updateGroup(groupInfo) + } + if m.chatId == fromContact.id { + ItemsModel.shared.loadOpenChat(groupInfo.id) + } + await MainActor.run { + m.removeChat(fromContact.id) + } case let .joinedGroupMemberConnecting(user, groupInfo, _, member): if active(user) { await MainActor.run { _ = m.upsertGroupMember(groupInfo, member) } } - case let .deletedMemberUser(user, groupInfo, _): // TODO update user member + case let .deletedMemberUser(user, groupInfo, member, withMessages): // TODO update user member if active(user) { await MainActor.run { m.updateGroup(groupInfo) + if withMessages { + m.removeMemberItems(groupInfo.membership, byMember: member, groupInfo) + } } } - case let .deletedMember(user, groupInfo, _, deletedMember): + case let .deletedMember(user, groupInfo, byMember, deletedMember, withMessages): if active(user) { await MainActor.run { _ = m.upsertGroupMember(groupInfo, deletedMember) + if withMessages { + m.removeMemberItems(deletedMember, byMember: byMember, groupInfo) + } } } case let .leftMember(user, groupInfo, member): @@ -1730,7 +2278,7 @@ func processReceivedMsg(_ res: ChatResponse) async { } if let contact = memberContact { await MainActor.run { - m.setContactNetworkStatus(contact, .connected) + n.setContactNetworkStatus(contact, .connected) } } case let .groupUpdated(user, toGroup): @@ -1761,6 +2309,10 @@ func processReceivedMsg(_ res: ChatResponse) async { } case let .rcvFileAccepted(user, aChatItem): // usually rcvFileAccepted is a response, but it's also an event for XFTP files auto-accepted from NSE await chatItemSimpleUpdate(user, aChatItem) +// TODO when aChatItem added +// case let .rcvFileAcceptedSndCancelled(user, aChatItem, _): // usually rcvFileAcceptedSndCancelled is a response, but it's also an event for XFTP files auto-accepted from NSE +// await chatItemSimpleUpdate(user, aChatItem) +// Task { cleanupFile(aChatItem) } case let .rcvFileStart(user, aChatItem): await chatItemSimpleUpdate(user, aChatItem) case let .rcvFileComplete(user, aChatItem): @@ -1772,11 +2324,15 @@ func processReceivedMsg(_ res: ChatResponse) async { if let aChatItem = aChatItem { await chatItemSimpleUpdate(user, aChatItem) } - case let .rcvFileError(user, aChatItem, _): + case let .rcvFileError(user, aChatItem, _, _): if let aChatItem = aChatItem { await chatItemSimpleUpdate(user, aChatItem) Task { cleanupFile(aChatItem) } } + case let .rcvFileWarning(user, aChatItem, _, _): + if let aChatItem = aChatItem { + await chatItemSimpleUpdate(user, aChatItem) + } case let .sndFileStart(user, aChatItem, _): await chatItemSimpleUpdate(user, aChatItem) case let .sndFileComplete(user, aChatItem, _): @@ -1793,12 +2349,15 @@ func processReceivedMsg(_ res: ChatResponse) async { } case let .sndFileCompleteXFTP(user, aChatItem, _): await chatItemSimpleUpdate(user, aChatItem) - Task { cleanupFile(aChatItem) } - case let .sndFileError(user, aChatItem, _): + case let .sndFileError(user, aChatItem, _, _): if let aChatItem = aChatItem { await chatItemSimpleUpdate(user, aChatItem) Task { cleanupFile(aChatItem) } } + case let .sndFileWarning(user, aChatItem, _, _): + if let aChatItem = aChatItem { + await chatItemSimpleUpdate(user, aChatItem) + } case let .callInvitation(invitation): await MainActor.run { m.callInvitations[invitation.contact.id] = invitation @@ -1808,7 +2367,6 @@ func processReceivedMsg(_ res: ChatResponse) async { await withCall(contact) { call in await MainActor.run { call.callState = .offerReceived - call.peerMedia = callType.media call.sharedKey = sharedKey } let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY) @@ -1844,21 +2402,35 @@ func processReceivedMsg(_ res: ChatResponse) async { } case .chatSuspended: chatSuspended() - case let .contactSwitch(_, contact, switchProgress): - await MainActor.run { - m.updateContactConnectionStats(contact, switchProgress.connectionStats) + case let .contactSwitch(user, contact, switchProgress): + if active(user) { + await MainActor.run { + m.updateContactConnectionStats(contact, switchProgress.connectionStats) + } } - case let .groupMemberSwitch(_, groupInfo, member, switchProgress): - await MainActor.run { - m.updateGroupMemberConnectionStats(groupInfo, member, switchProgress.connectionStats) + case let .groupMemberSwitch(user, groupInfo, member, switchProgress): + if active(user) { + await MainActor.run { + m.updateGroupMemberConnectionStats(groupInfo, member, switchProgress.connectionStats) + } } - case let .contactRatchetSync(_, contact, ratchetSyncProgress): - await MainActor.run { - m.updateContactConnectionStats(contact, ratchetSyncProgress.connectionStats) + case let .contactRatchetSync(user, contact, ratchetSyncProgress): + if active(user) { + await MainActor.run { + m.updateContactConnectionStats(contact, ratchetSyncProgress.connectionStats) + } } - case let .groupMemberRatchetSync(_, groupInfo, member, ratchetSyncProgress): - await MainActor.run { - m.updateGroupMemberConnectionStats(groupInfo, member, ratchetSyncProgress.connectionStats) + case let .groupMemberRatchetSync(user, groupInfo, member, ratchetSyncProgress): + if active(user) { + await MainActor.run { + m.updateGroupMemberConnectionStats(groupInfo, member, ratchetSyncProgress.connectionStats) + } + } + case let .contactDisabled(user, contact): + if active(user) { + await MainActor.run { + m.updateContact(contact) + } } case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): await MainActor.run { @@ -1882,12 +2454,30 @@ func processReceivedMsg(_ res: ChatResponse) async { let state = UIRemoteCtrlSessionState.connected(remoteCtrl: remoteCtrl, sessionCode: m.remoteCtrlSession?.sessionCode ?? "") m.remoteCtrlSession = m.remoteCtrlSession?.updateState(state) } - case .remoteCtrlStopped: + case let .remoteCtrlStopped(_, rcStopReason): // This delay is needed to cancel the session that fails on network failure, // e.g. when user did not grant permission to access local network yet. if let sess = m.remoteCtrlSession { await MainActor.run { m.remoteCtrlSession = nil + dismissAllSheets() { + switch rcStopReason { + case .disconnected: + () + case .connectionFailed(.errorAgent(.RCP(.identity))): + AlertManager.shared.showAlertMsg( + title: "Connection with desktop stopped", + message: "This link was used with another mobile device, please create a new link on the desktop." + ) + default: + AlertManager.shared.showAlert(Alert( + title: Text("Connection with desktop stopped"), + message: Text("Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers."), + primaryButton: .default(Text("Ok")), + secondaryButton: .default(Text("Copy error")) { UIPasteboard.general.string = String(describing: rcStopReason) } + )) + } + } } if case .connected = sess.sessionState { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { @@ -1916,12 +2506,13 @@ func processReceivedMsg(_ res: ChatResponse) async { func switchToLocalSession() { let m = ChatModel.shared + let n = NetworkModel.shared m.remoteCtrlSession = nil do { m.users = try listUsers() try getUserChatData() let statuses = (try apiGetNetworkStatuses()).map { s in (s.agentConnId, s.networkStatus) } - m.networkStatuses = Dictionary(uniqueKeysWithValues: statuses) + n.networkStatuses = Dictionary(uniqueKeysWithValues: statuses) } catch let error { logger.debug("error updating chat data: \(responseError(error))") } @@ -1944,52 +2535,79 @@ func chatItemSimpleUpdate(_ user: any UserLike, _ aChatItem: AChatItem) async { } } -func updateContactsStatus(_ contactRefs: [ContactRef], status: NetworkStatus) async { +func groupChatItemsDeleted(_ user: UserRef, _ groupInfo: GroupInfo, _ chatItemIDs: Set, _ member_: GroupMember?) async { let m = ChatModel.shared + if !active(user) { + do { + let users = try listUsers() + await MainActor.run { + m.users = users + } + } catch { + logger.error("Error loading users: \(error)") + } + return + } + let im = ItemsModel.shared + let cInfo = ChatInfo.group(groupInfo: groupInfo) await MainActor.run { - for c in contactRefs { - m.networkStatuses[c.agentConnId] = status + m.decreaseGroupReportsCounter(cInfo.id, by: chatItemIDs.count) + } + var notFound = chatItemIDs.count + for ci in im.reversedChatItems { + if chatItemIDs.contains(ci.id) { + let deleted = if case let .groupRcv(groupMember) = ci.chatDir, let member_, groupMember.groupMemberId != member_.groupMemberId { + CIDeleted.moderated(deletedTs: Date.now, byGroupMember: member_) + } else { + CIDeleted.deleted(deletedTs: Date.now) + } + await MainActor.run { + var newItem = ci + newItem.meta.itemDeleted = deleted + _ = m.upsertChatItem(cInfo, newItem) + } + notFound -= 1 + if notFound == 0 { break } } } } -func processContactSubError(_ contact: Contact, _ chatError: ChatError) { +func refreshCallInvitations() async throws { let m = ChatModel.shared - var err: String - switch chatError { - case .errorAgent(agentError: .BROKER(_, .NETWORK)): err = "network" - case .errorAgent(agentError: .SMP(smpErr: .AUTH)): err = "contact deleted" - default: err = String(describing: chatError) - } - m.setContactNetworkStatus(contact, .error(connectionError: err)) -} - -func refreshCallInvitations() throws { - let m = ChatModel.shared - let callInvitations = try justRefreshCallInvitations() - if let (chatId, ntfAction) = m.ntfCallInvitationAction, - let invitation = m.callInvitations.removeValue(forKey: chatId) { - m.ntfCallInvitationAction = nil - CallController.shared.callAction(invitation: invitation, action: ntfAction) - } else if let invitation = callInvitations.last(where: { $0.user.showNotifications }) { - activateCall(invitation) + let callInvitations = try await apiGetCallInvitations() + await MainActor.run { + m.callInvitations = callsByChat(callInvitations) + if let (chatId, ntfAction) = m.ntfCallInvitationAction, + let invitation = m.callInvitations.removeValue(forKey: chatId) { + m.ntfCallInvitationAction = nil + CallController.shared.callAction(invitation: invitation, action: ntfAction) + } else if let invitation = callInvitations.last(where: { $0.user.showNotifications }) { + activateCall(invitation) + } } } -func justRefreshCallInvitations() throws -> [RcvCallInvitation] { - let m = ChatModel.shared - let callInvitations = try apiGetCallInvitations() - m.callInvitations = callInvitations.reduce(into: [ChatId: RcvCallInvitation]()) { result, inv in result[inv.contact.id] = inv } - return callInvitations +func justRefreshCallInvitations() async throws { + let callInvitations = try apiGetCallInvitationsSync() + await MainActor.run { + ChatModel.shared.callInvitations = callsByChat(callInvitations) + } +} + +private func callsByChat(_ callInvitations: [RcvCallInvitation]) -> [ChatId: RcvCallInvitation] { + callInvitations.reduce(into: [ChatId: RcvCallInvitation]()) { + result, inv in result[inv.contact.id] = inv + } } func activateCall(_ callInvitation: RcvCallInvitation) { - if !callInvitation.user.showNotifications { return } let m = ChatModel.shared + logger.debug("reportNewIncomingCall activeCallUUID \(String(describing: m.activeCall?.callUUID)) invitationUUID \(String(describing: callInvitation.callUUID))") + if !callInvitation.user.showNotifications || m.activeCall?.callUUID == callInvitation.callUUID { return } CallController.shared.reportNewIncomingCall(invitation: callInvitation) { error in if let error = error { DispatchQueue.main.async { - m.callInvitations[callInvitation.contact.id]?.callkitUUID = nil + m.callInvitations[callInvitation.contact.id]?.callUUID = nil } logger.error("reportNewIncomingCall error: \(error.localizedDescription)") } else { diff --git a/apps/ios/Shared/Model/SuspendChat.swift b/apps/ios/Shared/Model/SuspendChat.swift index 4494adc0e8..92bcdcac53 100644 --- a/apps/ios/Shared/Model/SuspendChat.swift +++ b/apps/ios/Shared/Model/SuspendChat.swift @@ -36,6 +36,18 @@ private func _suspendChat(timeout: Int) { } } +let seSubscriber = seMessageSubscriber { + switch $0 { + case let .state(state): + switch state { + case .inactive: + if AppChatState.shared.value.inactive { activateChat() } + case .sendingMessage: + if AppChatState.shared.value.canSuspend { suspendChat() } + } + } +} + func suspendChat() { suspendLockQueue.sync { _suspendChat(timeout: appSuspendTimeout) diff --git a/apps/ios/Shared/SimpleXApp.swift b/apps/ios/Shared/SimpleXApp.swift index 7d69466c07..f8d69c5fc8 100644 --- a/apps/ios/Shared/SimpleXApp.swift +++ b/apps/ios/Shared/SimpleXApp.swift @@ -19,6 +19,7 @@ struct SimpleXApp: App { @Environment(\.scenePhase) var scenePhase @State private var enteredBackgroundAuthenticated: TimeInterval? = nil + @State private var appOpenUrlLater: URL? init() { DispatchQueue.global(qos: .background).sync { @@ -39,9 +40,14 @@ struct SimpleXApp: App { // so that it's computed by the time view renders, and not on event after rendering ContentView(contentAccessAuthenticationExtended: !authenticationExpired()) .environmentObject(chatModel) + .environmentObject(AppTheme.shared) .onOpenURL { url in logger.debug("ContentView.onOpenURL: \(url)") - chatModel.appOpenUrl = url + if AppChatState.shared.value == .active { + chatModel.appOpenUrl = url + } else { + appOpenUrlLater = url + } } .onAppear() { // Present screen for continue migration if it wasn't finished yet @@ -57,6 +63,7 @@ struct SimpleXApp: App { } .onChange(of: scenePhase) { phase in logger.debug("scenePhase was \(String(describing: scenePhase)), now \(String(describing: phase))") + AppSheetState.shared.scenePhaseActive = phase == .active switch (phase) { case .background: // --- authentication @@ -80,10 +87,27 @@ struct SimpleXApp: App { if appState != .stopped { startChatAndActivate { - if appState.inactive && chatModel.chatRunning == true { - updateChats() - if !chatModel.showCallView && !CallController.shared.hasActiveCalls() { - updateCallInvitations() + if chatModel.chatRunning == true { + if let ntfResponse = chatModel.notificationResponse { + chatModel.notificationResponse = nil + NtfManager.shared.processNotificationResponse(ntfResponse) + } + if appState.inactive { + Task { + await updateChats() + if !chatModel.showCallView && !CallController.shared.hasActiveCalls() { + await updateCallInvitations() + } + if let url = appOpenUrlLater { + await MainActor.run { + appOpenUrlLater = nil + chatModel.appOpenUrl = url + } + } + } + } else if let url = appOpenUrlLater { + appOpenUrlLater = nil + chatModel.appOpenUrl = url } } } @@ -128,16 +152,17 @@ struct SimpleXApp: App { } } - private func updateChats() { + private func updateChats() async { do { - let chats = try apiGetChats() - chatModel.updateChats(with: chats) + let chats = try await apiGetChatsAsync() + await MainActor.run { chatModel.updateChats(chats) } if let id = chatModel.chatId, - let chat = chatModel.getChat(id) { - loadChat(chat: chat) + let chat = chatModel.getChat(id), + !NtfManager.shared.navigatingToChat { + Task { await loadChat(chat: chat, clearItems: false) } } if let ncr = chatModel.ntfContactRequest { - chatModel.ntfContactRequest = nil + await MainActor.run { chatModel.ntfContactRequest = nil } if case let .contactRequest(contactRequest) = chatModel.getChat(ncr.chatId)?.chatInfo { Task { await acceptContactRequest(incognito: ncr.incognito, contactRequest: contactRequest) } } @@ -147,9 +172,9 @@ struct SimpleXApp: App { } } - private func updateCallInvitations() { + private func updateCallInvitations() async { do { - try refreshCallInvitations() + try await refreshCallInvitations() } catch let error { logger.error("apiGetCallInvitations: cannot update call invitations \(responseError(error))") } diff --git a/apps/ios/Shared/Theme/Theme.swift b/apps/ios/Shared/Theme/Theme.swift new file mode 100644 index 0000000000..de67390026 --- /dev/null +++ b/apps/ios/Shared/Theme/Theme.swift @@ -0,0 +1,199 @@ +// +// Theme.swift +// SimpleX (iOS) +// +// Created by Avently on 14.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI +import SimpleXChat + +var CurrentColors: ThemeManager.ActiveTheme = ThemeManager.currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + +var MenuTextColor: Color { if isInDarkTheme() { AppTheme.shared.colors.onBackground.opacity(0.8) } else { Color.black } } +var NoteFolderIconColor: Color { AppTheme.shared.appColors.primaryVariant2 } + +func isInDarkTheme() -> Bool { !CurrentColors.colors.isLight } + +class AppTheme: ObservableObject, Equatable { + static let shared = AppTheme(name: CurrentColors.name, base: CurrentColors.base, colors: CurrentColors.colors, appColors: CurrentColors.appColors, wallpaper: CurrentColors.wallpaper) + + var name: String + var base: DefaultTheme + @ObservedObject var colors: Colors + @ObservedObject var appColors: AppColors + @ObservedObject var wallpaper: AppWallpaper + + init(name: String, base: DefaultTheme, colors: Colors, appColors: AppColors, wallpaper: AppWallpaper) { + self.name = name + self.base = base + self.colors = colors + self.appColors = appColors + self.wallpaper = wallpaper + } + + static func == (lhs: AppTheme, rhs: AppTheme) -> Bool { + lhs.name == rhs.name && + lhs.colors == rhs.colors && + lhs.appColors == rhs.appColors && + lhs.wallpaper == rhs.wallpaper + } + + func updateFromCurrentColors() { + objectWillChange.send() + name = CurrentColors.name + base = CurrentColors.base + colors.updateColorsFrom(CurrentColors.colors) + appColors.updateColorsFrom(CurrentColors.appColors) + wallpaper.updateWallpaperFrom(CurrentColors.wallpaper) + } +} + +struct ThemedBackground: ViewModifier { + @EnvironmentObject var theme: AppTheme + var grouped: Bool = false + + func body(content: Content) -> some View { + content + .background( + theme.base == DefaultTheme.SIMPLEX + ? LinearGradient( + colors: [ + grouped + ? theme.colors.background.lighter(0.4).asGroupedBackground(theme.base.mode) + : theme.colors.background.lighter(0.4), + grouped + ? theme.colors.background.darker(0.4).asGroupedBackground(theme.base.mode) + : theme.colors.background.darker(0.4) + ], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + : LinearGradient( + colors: [], + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + ) + .background( + theme.base == DefaultTheme.SIMPLEX + ? Color.clear + : grouped + ? theme.colors.background.asGroupedBackground(theme.base.mode) + : theme.colors.background + ) + } +} + +var systemInDarkThemeCurrently: Bool { + return UITraitCollection.current.userInterfaceStyle == .dark +} + +func reactOnDarkThemeChanges(_ inDarkNow: Bool) { + if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME && CurrentColors.colors.isLight == inDarkNow { + // Change active colors from light to dark and back based on system theme + ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME) + } +} + +extension ThemeWallpaper { + public func importFromString() -> ThemeWallpaper { + if preset == nil, let image { + // Need to save image from string and to save its path + if let parsed = imageFromBase64(image), + let filename = saveWallpaperFile(image: parsed) { + var copy = self + copy.image = nil + copy.imageFile = filename + return copy + } else { + return ThemeWallpaper() + } + } else { + return self + } + } + + func withFilledWallpaperBase64() -> ThemeWallpaper { + let aw = toAppWallpaper() + let type = aw.type + let preset: String? = if case let WallpaperType.preset(filename, _) = type { filename } else { nil } + let scale: Float? = if case let WallpaperType.preset(_, scale) = type { scale } else { if case let WallpaperType.image(_, scale, _) = type { scale } else { 1.0 } } + let scaleType: WallpaperScaleType? = if case let WallpaperType.image(_, _, scaleType) = type { scaleType } else { nil } + let image: String? = if case WallpaperType.image = type, let image = type.uiImage { resizeImageToStrSizeSync(image, maxDataSize: 5_000_000) } else { nil } + return ThemeWallpaper ( + preset: preset, + scale: scale, + scaleType: scaleType, + background: aw.background?.toReadableHex(), + tint: aw.tint?.toReadableHex(), + image: image, + imageFile: nil + ) + } +} + +extension ThemeModeOverride { + func removeSameColors(_ base: DefaultTheme, colorsToCompare tc: ThemeColors) -> ThemeModeOverride { + let wallpaperType = WallpaperType.from(wallpaper) ?? WallpaperType.empty + let w: ThemeWallpaper + switch wallpaperType { + case let WallpaperType.preset(filename, scale): + let p = PresetWallpaper.from(filename) + w = ThemeWallpaper( + preset: filename, + scale: scale ?? wallpaper?.scale, + scaleType: nil, + background: p?.background[base]?.toReadableHex(), + tint: p?.tint[base]?.toReadableHex(), + image: nil, + imageFile: nil + ) + case WallpaperType.image: + w = ThemeWallpaper( + preset: nil, + scale: nil, + scaleType: WallpaperScaleType.fill, + background: Color.clear.toReadableHex(), + tint: Color.clear.toReadableHex(), + image: nil, + imageFile: nil + ) + default: + w = ThemeWallpaper() + } + let wallpaper: ThemeWallpaper? = if let wallpaper { + ThemeWallpaper( + preset: wallpaper.preset, + scale: wallpaper.scale != w.scale ? wallpaper.scale : nil, + scaleType: wallpaper.scaleType != w.scaleType ? wallpaper.scaleType : nil, + background: wallpaper.background != w.background ? wallpaper.background : nil, + tint: wallpaper.tint != w.tint ? wallpaper.tint : nil, + image: wallpaper.image, + imageFile: wallpaper.imageFile + ) + } else { + nil + } + return ThemeModeOverride( + mode: self.mode, + colors: ThemeColors( + primary: colors.primary != tc.primary ? colors.primary : nil, + primaryVariant: colors.primaryVariant != tc.primaryVariant ? colors.primaryVariant : nil, + secondary: colors.secondary != tc.secondary ? colors.secondary : nil, + secondaryVariant: colors.secondaryVariant != tc.secondaryVariant ? colors.secondaryVariant : nil, + background: colors.background != tc.background ? colors.background : nil, + surface: colors.surface != tc.surface ? colors.surface : nil, + title: colors.title != tc.title ? colors.title : nil, + primaryVariant2: colors.primaryVariant2 != tc.primaryVariant2 ? colors.primary : nil, + sentMessage: colors.sentMessage != tc.sentMessage ? colors.sentMessage : nil, + sentQuote: colors.sentQuote != tc.sentQuote ? colors.sentQuote : nil, + receivedMessage: colors.receivedMessage != tc.receivedMessage ? colors.receivedMessage : nil, + receivedQuote: colors.receivedQuote != tc.receivedQuote ? colors.receivedQuote : nil + ), + wallpaper: wallpaper + ) + } +} diff --git a/apps/ios/Shared/Theme/ThemeManager.swift b/apps/ios/Shared/Theme/ThemeManager.swift new file mode 100644 index 0000000000..4166619d04 --- /dev/null +++ b/apps/ios/Shared/Theme/ThemeManager.swift @@ -0,0 +1,303 @@ +// +// ThemeManager.swift +// SimpleX (iOS) +// +// Created by Avently on 03.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI +import SimpleXChat + +class ThemeManager { + struct ActiveTheme: Equatable { + let name: String + let base: DefaultTheme + let colors: Colors + let appColors: AppColors + var wallpaper: AppWallpaper = AppWallpaper(background: nil, tint: nil, type: .empty) + + func toAppTheme() -> AppTheme { + AppTheme(name: name, base: base, colors: colors, appColors: appColors, wallpaper: wallpaper) + } + } + + private static func systemDarkThemeColors() -> (Colors, DefaultTheme) { + switch systemDarkThemeDefault.get() { + case DefaultTheme.DARK.themeName: (DarkColorPalette, DefaultTheme.DARK) + case DefaultTheme.SIMPLEX.themeName: (SimplexColorPalette, DefaultTheme.SIMPLEX) + case DefaultTheme.BLACK.themeName: (BlackColorPalette, DefaultTheme.BLACK) + default: (SimplexColorPalette, DefaultTheme.SIMPLEX) + } + } + + private static func nonSystemThemeName() -> String { + let themeName = currentThemeDefault.get() + return if themeName != DefaultTheme.SYSTEM_THEME_NAME { + themeName + } else { + systemInDarkThemeCurrently ? systemDarkThemeDefault.get() : DefaultTheme.LIGHT.themeName + } + } + + static func defaultActiveTheme(_ appSettingsTheme: [ThemeOverrides]) -> ThemeOverrides? { + let nonSystemThemeName = nonSystemThemeName() + let defaultThemeId = currentThemeIdsDefault.get()[nonSystemThemeName] + return appSettingsTheme.getTheme(defaultThemeId) + } + + static func defaultActiveTheme(_ perUserTheme: ThemeModeOverrides?, _ appSettingsTheme: [ThemeOverrides]) -> ThemeModeOverride { + let perUserTheme = !CurrentColors.colors.isLight ? perUserTheme?.dark : perUserTheme?.light + if let perUserTheme { + return perUserTheme + } + let defaultTheme = defaultActiveTheme(appSettingsTheme) + return ThemeModeOverride(mode: CurrentColors.base.mode, colors: defaultTheme?.colors ?? ThemeColors(), wallpaper: defaultTheme?.wallpaper ?? ThemeWallpaper.from(PresetWallpaper.school.toType(CurrentColors.base), nil, nil)) + } + + static func currentColors(_ themeOverridesForType: WallpaperType?, _ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverrides?, _ appSettingsTheme: [ThemeOverrides]) -> ActiveTheme { + let themeName = currentThemeDefault.get() + let nonSystemThemeName = nonSystemThemeName() + let defaultTheme = defaultActiveTheme(appSettingsTheme) + + let baseTheme = switch nonSystemThemeName { + case DefaultTheme.LIGHT.themeName: ActiveTheme(name: DefaultTheme.LIGHT.themeName, base: DefaultTheme.LIGHT, colors: LightColorPalette.clone(), appColors: LightColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.LIGHT))) + case DefaultTheme.DARK.themeName: ActiveTheme(name: DefaultTheme.DARK.themeName, base: DefaultTheme.DARK, colors: DarkColorPalette.clone(), appColors: DarkColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.DARK))) + case DefaultTheme.SIMPLEX.themeName: ActiveTheme(name: DefaultTheme.SIMPLEX.themeName, base: DefaultTheme.SIMPLEX, colors: SimplexColorPalette.clone(), appColors: SimplexColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.SIMPLEX))) + case DefaultTheme.BLACK.themeName: ActiveTheme(name: DefaultTheme.BLACK.themeName, base: DefaultTheme.BLACK, colors: BlackColorPalette.clone(), appColors: BlackColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.BLACK))) + default: ActiveTheme(name: DefaultTheme.LIGHT.themeName, base: DefaultTheme.LIGHT, colors: LightColorPalette.clone(), appColors: LightColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.LIGHT))) + } + + let perUserTheme = baseTheme.colors.isLight ? perUserTheme?.light : perUserTheme?.dark + let theme = appSettingsTheme.sameTheme(themeOverridesForType ?? perChatTheme?.type ?? perUserTheme?.type ?? defaultTheme?.wallpaper?.toAppWallpaper().type, nonSystemThemeName) ?? defaultTheme + + if theme == nil && perUserTheme == nil && perChatTheme == nil && themeOverridesForType == nil { + return ActiveTheme(name: themeName, base: baseTheme.base, colors: baseTheme.colors, appColors: baseTheme.appColors, wallpaper: baseTheme.wallpaper) + } + let presetWallpaperTheme: ThemeColors? = if let themeOverridesForType, case let WallpaperType.preset(filename, _) = themeOverridesForType { + PresetWallpaper.from(filename)?.colors[baseTheme.base] + } else if let wallpaper = perChatTheme?.wallpaper { + if let preset = wallpaper.preset { PresetWallpaper.from(preset)?.colors[baseTheme.base] } else { nil } + } else if let wallpaper = perUserTheme?.wallpaper { + if let preset = wallpaper.preset { PresetWallpaper.from(preset)?.colors[baseTheme.base] } else { nil } + } else { + if let preset = theme?.wallpaper?.preset { PresetWallpaper.from(preset)?.colors[baseTheme.base] } else { nil } + } + + let themeOrEmpty = theme ?? ThemeOverrides(base: baseTheme.base) + let colors = themeOrEmpty.toColors(themeOrEmpty.base, perChatTheme?.colors, perUserTheme?.colors, presetWallpaperTheme) + return ActiveTheme( + name: themeName, + base: baseTheme.base, + colors: colors, + appColors: themeOrEmpty.toAppColors(themeOrEmpty.base, perChatTheme?.colors, perChatTheme?.type, perUserTheme?.colors, perUserTheme?.type, presetWallpaperTheme), + wallpaper: themeOrEmpty.toAppWallpaper(themeOverridesForType, perChatTheme, perUserTheme, colors.background) + ) + } + + static func currentThemeOverridesForExport(_ themeOverridesForType: WallpaperType?, _ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverrides?) -> ThemeOverrides { + let current = currentColors(themeOverridesForType, perChatTheme, perUserTheme, themeOverridesDefault.get()) + let wType = current.wallpaper.type + let wBackground = current.wallpaper.background + let wTint = current.wallpaper.tint + let w: ThemeWallpaper? = if case WallpaperType.empty = wType { + nil + } else { + ThemeWallpaper.from(wType, wBackground?.toReadableHex(), wTint?.toReadableHex()).withFilledWallpaperBase64() + } + return ThemeOverrides( + themeId: "", + base: current.base, + colors: ThemeColors.from(current.colors, current.appColors), + wallpaper: w + ) + } + + static func applyTheme(_ theme: String) { + currentThemeDefault.set(theme) + CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + AppTheme.shared.updateFromCurrentColors() + let tint = UIColor(CurrentColors.colors.primary) + if SceneDelegate.windowStatic?.tintColor != tint { + SceneDelegate.windowStatic?.tintColor = tint + } +// applyNavigationBarColors(CurrentColors.toAppTheme()) + } + + static func adjustWindowStyle() { + let style = switch currentThemeDefault.get() { + case DefaultTheme.LIGHT.themeName: UIUserInterfaceStyle.light + case DefaultTheme.SYSTEM_THEME_NAME: UIUserInterfaceStyle.unspecified + default: UIUserInterfaceStyle.dark + } + if SceneDelegate.windowStatic?.overrideUserInterfaceStyle != style { + SceneDelegate.windowStatic?.overrideUserInterfaceStyle = style + } + } + +// static func applyNavigationBarColors(_ theme: AppTheme) { +// let baseColors = switch theme.base { +// case DefaultTheme.LIGHT: LightColorPaletteApp +// case DefaultTheme.DARK: DarkColorPaletteApp +// case DefaultTheme.SIMPLEX: SimplexColorPaletteApp +// case DefaultTheme.BLACK: BlackColorPaletteApp +// } +// let isDefaultColor = baseColors.title == theme.appColors.title +// +// let title = UIColor(theme.appColors.title) +// if !isDefaultColor && UINavigationBar.appearance().titleTextAttributes?.first as? UIColor != title { +// UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: title] +// UINavigationBar.appearance().largeTitleTextAttributes = [.foregroundColor: title] +// } else { +// UINavigationBar.appearance().titleTextAttributes = nil +// UINavigationBar.appearance().largeTitleTextAttributes = nil +// } +// } + + static func changeDarkTheme(_ theme: String) { + systemDarkThemeDefault.set(theme) + CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + AppTheme.shared.updateFromCurrentColors() + } + + static func saveAndApplyThemeColor(_ baseTheme: DefaultTheme, _ name: ThemeColor, _ color: Color? = nil, _ pref: CodableDefault<[ThemeOverrides]>? = nil) { + let nonSystemThemeName = baseTheme.themeName + let pref = pref ?? themeOverridesDefault + let overrides = pref.get() + let themeId = currentThemeIdsDefault.get()[nonSystemThemeName] + let prevValue = overrides.getTheme(themeId) ?? ThemeOverrides(base: baseTheme) + pref.set(overrides.replace(prevValue.withUpdatedColor(name, color?.toReadableHex()))) + var themeIds = currentThemeIdsDefault.get() + themeIds[nonSystemThemeName] = prevValue.themeId + currentThemeIdsDefault.set(themeIds) + applyTheme(currentThemeDefault.get()) + } + + static func applyThemeColor(name: ThemeColor, color: Color? = nil, pref: Binding) { + pref.wrappedValue = pref.wrappedValue.withUpdatedColor(name, color?.toReadableHex()) + } + + static func saveAndApplyWallpaper(_ baseTheme: DefaultTheme, _ type: WallpaperType?, _ pref: CodableDefault<[ThemeOverrides]>?) { + let nonSystemThemeName = baseTheme.themeName + let pref = pref ?? themeOverridesDefault + let overrides = pref.get() + let theme = overrides.sameTheme(type, baseTheme.themeName) + var prevValue = theme ?? ThemeOverrides(base: baseTheme) + prevValue.wallpaper = if let type { + if case WallpaperType.empty = type { + nil as ThemeWallpaper? + } else { + ThemeWallpaper.from(type, prevValue.wallpaper?.background, prevValue.wallpaper?.tint) + } + } else { + nil + } + pref.set(overrides.replace(prevValue)) + var themeIds = currentThemeIdsDefault.get() + themeIds[nonSystemThemeName] = prevValue.themeId + currentThemeIdsDefault.set(themeIds) + applyTheme(currentThemeDefault.get()) + } + + static func copyFromSameThemeOverrides(_ type: WallpaperType?, _ lowerLevelOverride: ThemeModeOverride?, _ pref: Binding) -> Bool { + let overrides = themeOverridesDefault.get() + let sameWallpaper: ThemeWallpaper? = if let wallpaper = lowerLevelOverride?.wallpaper, lowerLevelOverride?.type?.sameType(type) == true { + wallpaper + } else { + overrides.sameTheme(type, CurrentColors.base.themeName)?.wallpaper + } + guard let sameWallpaper else { + if let type { + var w: ThemeWallpaper = ThemeWallpaper.from(type, nil, nil) + w.scale = nil + w.scaleType = nil + w.background = nil + w.tint = nil + pref.wrappedValue = ThemeModeOverride(mode: CurrentColors.base.mode, wallpaper: w) + } else { + // Make an empty wallpaper to override any top level ones + pref.wrappedValue = ThemeModeOverride(mode: CurrentColors.base.mode, wallpaper: ThemeWallpaper()) + } + return true + } + var type = sameWallpaper.toAppWallpaper().type + if case let WallpaperType.image(filename, scale, scaleType) = type, sameWallpaper.imageFile == filename { + // same image file. Needs to be copied first in order to be able to remove the file once it's not needed anymore without affecting main theme override + if let filename = saveWallpaperFile(url: getWallpaperFilePath(filename)) { + type = WallpaperType.image(filename, scale, scaleType) + } else { + logger.error("Error while copying wallpaper from global overrides to chat overrides") + return false + } + } + var prevValue = pref.wrappedValue + var w = ThemeWallpaper.from(type, nil, nil) + w.scale = nil + w.scaleType = nil + w.background = nil + w.tint = nil + prevValue.colors = ThemeColors() + prevValue.wallpaper = w + pref.wrappedValue = prevValue + return true + } + + static func applyWallpaper(_ type: WallpaperType?, _ pref: Binding) { + var prevValue = pref.wrappedValue + prevValue.wallpaper = if let type { + ThemeWallpaper.from(type, prevValue.wallpaper?.background, prevValue.wallpaper?.tint) + } else { + nil + } + pref.wrappedValue = prevValue + } + + static func saveAndApplyThemeOverrides(_ theme: ThemeOverrides, _ pref: CodableDefault<[ThemeOverrides]>? = nil) { + let wallpaper = theme.wallpaper?.importFromString() + let nonSystemThemeName = theme.base.themeName + let pref: CodableDefault<[ThemeOverrides]> = pref ?? themeOverridesDefault + let overrides = pref.get() + var prevValue = overrides.getTheme(nil, wallpaper?.toAppWallpaper().type, theme.base) ?? ThemeOverrides(base: theme.base) + if let imageFile = prevValue.wallpaper?.imageFile { + try? FileManager.default.removeItem(at: getWallpaperFilePath(imageFile)) + } + prevValue.base = theme.base + prevValue.colors = theme.colors + prevValue.wallpaper = wallpaper + pref.set(overrides.replace(prevValue)) + currentThemeDefault.set(nonSystemThemeName) + var currentThemeIds = currentThemeIdsDefault.get() + currentThemeIds[nonSystemThemeName] = prevValue.themeId + currentThemeIdsDefault.set(currentThemeIds) + applyTheme(nonSystemThemeName) + } + + static func resetAllThemeColors(_ pref: CodableDefault<[ThemeOverrides]>? = nil) { + let nonSystemThemeName = nonSystemThemeName() + let pref: CodableDefault<[ThemeOverrides]> = pref ?? themeOverridesDefault + let overrides = pref.get() + guard let themeId = currentThemeIdsDefault.get()[nonSystemThemeName], + var prevValue = overrides.getTheme(themeId) + else { return } + prevValue.colors = ThemeColors() + prevValue.wallpaper?.background = nil + prevValue.wallpaper?.tint = nil + pref.set(overrides.replace(prevValue)) + applyTheme(currentThemeDefault.get()) + } + + static func resetAllThemeColors(_ pref: Binding) { + var prevValue = pref.wrappedValue + prevValue.colors = ThemeColors() + prevValue.wallpaper?.background = nil + prevValue.wallpaper?.tint = nil + pref.wrappedValue = prevValue + } + + static func removeTheme(_ themeId: String?) { + var themes = themeOverridesDefault.get().map { $0 } + themes.removeAll(where: { $0.themeId == themeId }) + themeOverridesDefault.set(themes) + } +} diff --git a/apps/ios/Shared/Views/Call/ActiveCallView.swift b/apps/ios/Shared/Views/Call/ActiveCallView.swift index 9f246f63f3..ab7a47b944 100644 --- a/apps/ios/Shared/Views/Call/ActiveCallView.swift +++ b/apps/ios/Shared/Views/Call/ActiveCallView.swift @@ -9,6 +9,7 @@ import SwiftUI import WebKit import SimpleXChat +import AVFoundation struct ActiveCallView: View { @EnvironmentObject var m: ChatModel @@ -16,33 +17,49 @@ struct ActiveCallView: View { @ObservedObject var call: Call @Environment(\.scenePhase) var scenePhase @State private var client: WebRTCClient? = nil - @State private var activeCall: WebRTCClient.Call? = nil @State private var localRendererAspectRatio: CGFloat? = nil + @State var remoteContentMode: UIView.ContentMode = .scaleAspectFill @Binding var canConnectCall: Bool @State var prevColorScheme: ColorScheme = .dark @State var pipShown = false + @State var wasConnected = false var body: some View { ZStack(alignment: .topLeading) { ZStack(alignment: .bottom) { - if let client = client, [call.peerMedia, call.localMedia].contains(.video), activeCall != nil { + if let client = client, call.hasVideo { GeometryReader { g in let width = g.size.width * 0.3 ZStack(alignment: .topTrailing) { - CallViewRemote(client: client, activeCall: $activeCall, activeCallViewIsCollapsed: $m.activeCallViewIsCollapsed, pipShown: $pipShown) - CallViewLocal(client: client, activeCall: $activeCall, localRendererAspectRatio: $localRendererAspectRatio, pipShown: $pipShown) - .cornerRadius(10) - .frame(width: width, height: width / (localRendererAspectRatio ?? 1)) - .padding([.top, .trailing], 17) ZStack(alignment: .center) { // For some reason, when the view in GeometryReader and ZStack is visible, it steals clicks on a back button, so showing something on top like this with background color helps (.clear color doesn't work) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.primary.opacity(0.000001)) + + CallViewRemote(client: client, call: call, activeCallViewIsCollapsed: $m.activeCallViewIsCollapsed, contentMode: $remoteContentMode, pipShown: $pipShown) + .onTapGesture { + remoteContentMode = remoteContentMode == .scaleAspectFill ? .scaleAspectFit : .scaleAspectFill + } + + Group { + let localVideoTrack = client.activeCall?.localVideoTrack ?? client.notConnectedCall?.localCameraAndTrack?.1 + if localVideoTrack != nil { + CallViewLocal(client: client, localRendererAspectRatio: $localRendererAspectRatio, pipShown: $pipShown) + .onDisappear { + localRendererAspectRatio = nil + } + } else { + Rectangle().fill(.black) + } + } + .cornerRadius(10) + .frame(width: width, height: localRendererAspectRatio == nil ? (g.size.width < g.size.height ? width * 1.33 : width / 1.33) : width / (localRendererAspectRatio ?? 1)) + .padding([.top, .trailing], 17) } } } - if let call = m.activeCall, let client = client, (!pipShown || !call.supportsVideo) { + if let call = m.activeCall, let client = client, (!pipShown || !call.hasVideo) { ActiveCallOverlay(call: call, client: client) } } @@ -52,6 +69,9 @@ struct ActiveCallView: View { .onAppear { logger.debug("ActiveCallView: appear client is nil \(client == nil), scenePhase \(String(describing: scenePhase)), canConnectCall \(canConnectCall)") AppDelegate.keepScreenOn(true) + Task { + await askRequiredPermissions() + } createWebRTCClient() dismissAllSheets() hideKeyboard() @@ -69,6 +89,11 @@ struct ActiveCallView: View { Task { await m.callCommand.setClient(nil) } AppDelegate.keepScreenOn(false) client?.endCall() + CallSoundsPlayer.shared.stop() + try? AVAudioSession.sharedInstance().setCategory(.soloAmbient) + if (wasConnected) { + CallSoundsPlayer.shared.vibrate(long: true) + } } .background(m.activeCallViewIsCollapsed ? .clear : .black) // Quite a big delay when opening/closing the view when a scheme changes (globally) this way. It's not needed when CallKit is used since status bar is green with white text on it @@ -77,7 +102,7 @@ struct ActiveCallView: View { private func createWebRTCClient() { if client == nil && canConnectCall { - client = WebRTCClient($activeCall, { msg in await MainActor.run { processRtcMessage(msg: msg) } }, $localRendererAspectRatio) + client = WebRTCClient({ msg in await MainActor.run { processRtcMessage(msg: msg) } }, $localRendererAspectRatio) Task { await m.callCommand.setClient(client) } @@ -92,7 +117,7 @@ struct ActiveCallView: View { logger.debug("ActiveCallView: response \(msg.resp.respType)") switch msg.resp { case let .capabilities(capabilities): - let callType = CallType(media: call.localMedia, capabilities: capabilities) + let callType = CallType(media: call.initialCallType, capabilities: capabilities) Task { do { try await apiSendCallInvitation(call.contact, callType) @@ -103,12 +128,17 @@ struct ActiveCallView: View { call.callState = .invitationSent call.localCapabilities = capabilities } + if call.hasVideo && !AVAudioSession.sharedInstance().hasExternalAudioDevice() { + try? AVAudioSession.sharedInstance().setCategory(.playback, options: [.allowBluetooth, .allowAirPlay, .allowBluetoothA2DP]) + } + CallSoundsPlayer.shared.startConnectingCallSound() + activeCallWaitDeliveryReceipt() } case let .offer(offer, iceCandidates, capabilities): Task { do { try await apiSendCallOffer(call.contact, offer, iceCandidates, - media: call.localMedia, capabilities: capabilities) + media: call.initialCallType, capabilities: capabilities) } catch { logger.error("apiSendCallOffer \(responseError(error))") } @@ -126,6 +156,7 @@ struct ActiveCallView: View { } await MainActor.run { call.callState = .negotiated + CallSoundsPlayer.shared.stop() } } case let .ice(iceCandidates): @@ -144,9 +175,16 @@ struct ActiveCallView: View { : CallController.shared.reportIncomingCall(call: call, connectedAt: nil) call.callState = .connected call.connectedAt = .now + if !wasConnected { + CallSoundsPlayer.shared.vibrate(long: false) + wasConnected = true + } } if state.connectionState == "closed" { closeCallView(client) + if let callUUID = m.activeCall?.callUUID { + CallController.shared.endCall(callUUID: callUUID) + } m.activeCall = nil m.activeCallViewIsCollapsed = false } @@ -161,10 +199,22 @@ struct ActiveCallView: View { call.callState = .connected call.connectionInfo = connectionInfo call.connectedAt = .now + if !wasConnected { + CallSoundsPlayer.shared.vibrate(long: false) + wasConnected = true + } + case let .peerMedia(source, enabled): + switch source { + case .mic: call.peerMediaSources.mic = enabled + case .camera: call.peerMediaSources.camera = enabled + case .screenAudio: call.peerMediaSources.screenAudio = enabled + case .screenVideo: call.peerMediaSources.screenVideo = enabled + case .unknown: () + } case .ended: closeCallView(client) call.callState = .ended - if let uuid = call.callkitUUID { + if let uuid = call.callUUID { CallController.shared.endCall(callUUID: uuid) } case .ok: @@ -187,6 +237,44 @@ struct ActiveCallView: View { } } + private func activeCallWaitDeliveryReceipt() { + ChatReceiver.shared.messagesChannel = { msg in + guard let call = ChatModel.shared.activeCall, call.callState == .invitationSent else { + ChatReceiver.shared.messagesChannel = nil + return + } + if case let .result(.chatItemsStatusesUpdated(_, chatItems)) = msg, + chatItems.contains(where: { ci in + ci.chatInfo.id == call.contact.id && + ci.chatItem.content.isSndCall && + ci.chatItem.meta.itemStatus.isSndRcvd + }) { + CallSoundsPlayer.shared.startInCallSound() + ChatReceiver.shared.messagesChannel = nil + } + } + } + + private func askRequiredPermissions() async { + let mic = await WebRTCClient.isAuthorized(for: .audio) + await MainActor.run { + call.localMediaSources.mic = mic + } + let cameraAuthorized = AVCaptureDevice.authorizationStatus(for: .video) == .authorized + var camera = call.initialCallType == .audio || cameraAuthorized + if call.initialCallType == .video && !cameraAuthorized { + camera = await WebRTCClient.isAuthorized(for: .video) + await MainActor.run { + if camera, let client { + client.setCameraEnabled(true) + } + } + } + if !mic || !camera { + WebRTCClient.showUnauthorizedAlert(for: !mic ? .audio : .video) + } + } + private func closeCallView(_ client: WebRTCClient) { if m.activeCall != nil { m.showCallView = false @@ -198,38 +286,20 @@ struct ActiveCallOverlay: View { @EnvironmentObject var chatModel: ChatModel @ObservedObject var call: Call var client: WebRTCClient + @ObservedObject private var deviceManager = CallAudioDeviceManager.shared var body: some View { VStack { - switch call.localMedia { - case .video: + switch call.hasVideo { + case true: videoCallInfoView(call) .foregroundColor(.white) .opacity(0.8) - .padding() - - Spacer() - - HStack { - toggleAudioButton() - Spacer() - Color.clear.frame(width: 40, height: 40) - Spacer() - endCallButton() - Spacer() - if call.videoEnabled { - flipCameraButton() - } else { - Color.clear.frame(width: 40, height: 40) - } - Spacer() - toggleVideoButton() - } - .padding(.horizontal, 20) - .padding(.bottom, 16) - .frame(maxWidth: .infinity, alignment: .center) - - case .audio: + .padding(.horizontal) + // Fixed vertical padding required for preserving position of buttons row when changing audio-to-video and back in landscape orientation. + // Otherwise, bigger padding is added by SwiftUI when switching call types + .padding(.vertical, 10) + case false: ZStack(alignment: .topLeading) { Button { chatModel.activeCallViewIsCollapsed = true @@ -239,31 +309,45 @@ struct ActiveCallOverlay: View { .foregroundColor(.white.opacity(0.8)) } VStack { - ProfileImage(imageStr: call.contact.profile.image) - .scaledToFit() - .frame(width: 192, height: 192) + ProfileImage(imageStr: call.contact.profile.image, size: 192) audioCallInfoView(call) } .foregroundColor(.white) .opacity(0.8) - .padding() + .padding(.horizontal) + .padding(.vertical, 10) .frame(maxHeight: .infinity) } - - Spacer() - - ZStack(alignment: .bottom) { - toggleAudioButton() - .frame(maxWidth: .infinity, alignment: .leading) - endCallButton() - toggleSpeakerButton() - .frame(maxWidth: .infinity, alignment: .trailing) - } - .padding(.bottom, 60) - .padding(.horizontal, 48) } + + Spacer() + + HStack { + toggleMicButton() + Spacer() + audioDeviceButton() + Spacer() + endCallButton() + Spacer() + if call.localMediaSources.camera { + flipCameraButton() + } else { + Color.clear.frame(width: 60, height: 60) + } + Spacer() + toggleCameraButton() + } + .padding(.horizontal, 20) + .padding(.bottom, 16) + .frame(maxWidth: 440, alignment: .center) } .frame(maxWidth: .infinity) + .onAppear { + deviceManager.start() + } + .onDisappear { + deviceManager.stop() + } } private func audioCallInfoView(_ call: Call) -> some View { @@ -277,7 +361,7 @@ struct ActiveCallOverlay: View { HStack { Text(call.encryptionStatus) if let connInfo = call.connectionInfo { - Text("(") + Text(connInfo.text) + Text(")") + Text(verbatim: "(") + Text(connInfo.text) + Text(verbatim: ")") } } } @@ -306,7 +390,7 @@ struct ActiveCallOverlay: View { HStack { Text(call.encryptionStatus) if let connInfo = call.connectionInfo { - Text("(") + Text(connInfo.text) + Text(")") + Text(verbatim: "(") + Text(connInfo.text) + Text(verbatim: ")") } } } @@ -317,85 +401,122 @@ struct ActiveCallOverlay: View { private func endCallButton() -> some View { let cc = CallController.shared - return callButton("phone.down.fill", width: 60, height: 60) { - if let uuid = call.callkitUUID { + return callButton("phone.down.fill", .red, padding: 10) { + if let uuid = call.callUUID { cc.endCall(callUUID: uuid) } else { cc.endCall(call: call) {} } } - .foregroundColor(.red) } - private func toggleAudioButton() -> some View { - controlButton(call, call.audioEnabled ? "mic.fill" : "mic.slash") { + private func toggleMicButton() -> some View { + controlButton(call, call.localMediaSources.mic ? "mic.fill" : "mic.slash", padding: 14) { Task { - client.setAudioEnabled(!call.audioEnabled) - DispatchQueue.main.async { - call.audioEnabled = !call.audioEnabled - } + if await WebRTCClient.isAuthorized(for: .audio) { + client.setAudioEnabled(!call.localMediaSources.mic) + } else { WebRTCClient.showUnauthorizedAlert(for: .audio) } + } + } + } + + func audioDeviceButton() -> some View { + // Check if the only input is microphone. And in this case show toggle button, + // If there are more inputs, it probably means something like bluetooth headphones are available + // and in this case show iOS button for choosing different output. + // There is no way to get available outputs, only inputs + Group { + if deviceManager.availableInputs.allSatisfy({ $0.portType == .builtInMic }) { + toggleSpeakerButton() + } else { + audioDevicePickerButton() + } + } + .onChange(of: call.localMediaSources.hasVideo) { hasVideo in + let current = AVAudioSession.sharedInstance().currentRoute.outputs.first?.portType + let speakerEnabled = current == .builtInSpeaker + let receiverEnabled = current == .builtInReceiver + // react automatically only when receiver were selected, otherwise keep an external device selected + if !speakerEnabled && hasVideo && receiverEnabled { + client.setSpeakerEnabledAndConfigureSession(!speakerEnabled, skipExternalDevice: true) + call.speakerEnabled = !speakerEnabled } } } private func toggleSpeakerButton() -> some View { - controlButton(call, call.speakerEnabled ? "speaker.wave.2.fill" : "speaker.wave.1.fill") { - Task { - client.setSpeakerEnabledAndConfigureSession(!call.speakerEnabled) - DispatchQueue.main.async { - call.speakerEnabled = !call.speakerEnabled - } - } + controlButton(call, !call.peerMediaSources.mic ? "speaker.slash" : call.speakerEnabled ? "speaker.wave.2.fill" : "speaker.wave.1.fill", padding: !call.peerMediaSources.mic ? 16 : call.speakerEnabled ? 15 : 17) { + let speakerEnabled = AVAudioSession.sharedInstance().currentRoute.outputs.first?.portType == .builtInSpeaker + client.setSpeakerEnabledAndConfigureSession(!speakerEnabled) + call.speakerEnabled = !speakerEnabled + } + .onAppear { + deviceManager.call = call + //call.speakerEnabled = AVAudioSession.sharedInstance().currentRoute.outputs.first?.portType == .builtInSpeaker } } - private func toggleVideoButton() -> some View { - controlButton(call, call.videoEnabled ? "video.fill" : "video.slash") { + private func toggleCameraButton() -> some View { + controlButton(call, call.localMediaSources.camera ? "video.fill" : "video.slash", padding: call.localMediaSources.camera ? 16 : 14) { Task { - client.setVideoEnabled(!call.videoEnabled) - DispatchQueue.main.async { - call.videoEnabled = !call.videoEnabled - } + if await WebRTCClient.isAuthorized(for: .video) { + client.setCameraEnabled(!call.localMediaSources.camera) + } else { WebRTCClient.showUnauthorizedAlert(for: .video) } } } + .disabled(call.initialCallType == .audio && client.activeCall?.peerHasOldVersion == true) } - @ViewBuilder private func flipCameraButton() -> some View { - controlButton(call, "arrow.triangle.2.circlepath") { + private func flipCameraButton() -> some View { + controlButton(call, "arrow.triangle.2.circlepath", padding: 12) { Task { - client.flipCamera() + if await WebRTCClient.isAuthorized(for: .video) { + client.flipCamera() + } } } } - @ViewBuilder private func controlButton(_ call: Call, _ imageName: String, _ perform: @escaping () -> Void) -> some View { - if call.hasMedia { - callButton(imageName, width: 50, height: 38, perform) - .foregroundColor(.white) - .opacity(0.85) - } else { - Color.clear.frame(width: 50, height: 38) - } + private func controlButton(_ call: Call, _ imageName: String, padding: CGFloat, _ perform: @escaping () -> Void) -> some View { + callButton(imageName, call.peerMediaSources.hasVideo ? Color.black.opacity(0.2) : Color.white.opacity(0.2), padding: padding, perform) } - private func callButton(_ imageName: String, width: CGFloat, height: CGFloat, _ perform: @escaping () -> Void) -> some View { + private func audioDevicePickerButton() -> some View { + AudioDevicePicker() + .opacity(0.8) + .scaleEffect(2) + .padding(10) + .frame(width: 60, height: 60) + .background(call.peerMediaSources.hasVideo ? Color.black.opacity(0.2) : Color.white.opacity(0.2)) + .clipShape(.circle) + } + + private func callButton(_ imageName: String, _ background: Color, padding: CGFloat, _ perform: @escaping () -> Void) -> some View { Button { perform() } label: { Image(systemName: imageName) .resizable() .scaledToFit() - .frame(maxWidth: width, maxHeight: height) + .padding(padding) + .frame(width: 60, height: 60) + .background(background) } + .foregroundColor(whiteColorWithAlpha) + .clipShape(.circle) + } + + private var whiteColorWithAlpha: Color { + get { Color(red: 204 / 255, green: 204 / 255, blue: 204 / 255) } } } struct ActiveCallOverlay_Previews: PreviewProvider { static var previews: some View { Group{ - ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callkitUUID: UUID(), callState: .offerSent, localMedia: .video), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil))) + ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, initialCallType: .video), client: WebRTCClient({ _ in }, Binding.constant(nil))) .background(.black) - ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callkitUUID: UUID(), callState: .offerSent, localMedia: .audio), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil))) + ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, initialCallType: .audio), client: WebRTCClient({ _ in }, Binding.constant(nil))) .background(.black) } } diff --git a/apps/ios/Shared/Views/Call/AudioDevicePicker.swift b/apps/ios/Shared/Views/Call/AudioDevicePicker.swift new file mode 100644 index 0000000000..be41741ab5 --- /dev/null +++ b/apps/ios/Shared/Views/Call/AudioDevicePicker.swift @@ -0,0 +1,25 @@ +// +// MPVolumeView.swift +// SimpleX (iOS) +// +// Created by Avently on 24.04.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI +import UIKit +import AVKit + +struct AudioDevicePicker: UIViewRepresentable { + func makeUIView(context: Context) -> some UIView { + let v = AVRoutePickerView(frame: .zero) + v.activeTintColor = .white + v.tintColor = .white + return v + } + + func updateUIView(_ uiView: UIViewType, context: Context) { + + } +} diff --git a/apps/ios/Shared/Views/Call/CallAudioDeviceManager.swift b/apps/ios/Shared/Views/Call/CallAudioDeviceManager.swift new file mode 100644 index 0000000000..d56849d16a --- /dev/null +++ b/apps/ios/Shared/Views/Call/CallAudioDeviceManager.swift @@ -0,0 +1,67 @@ +// +// CallAudioDeviceManager.swift +// SimpleX (iOS) +// +// Created by Avently on 23.04.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI +import SimpleXChat +import AVKit +import WebRTC + +class CallAudioDeviceManager: ObservableObject { + static let shared = CallAudioDeviceManager() + let audioSession: AVAudioSession + let nc = NotificationCenter.default + + var call: Call? + var timer: Timer? = nil + + // Actually, only one output + @Published var outputs: [AVAudioSessionPortDescription] + @Published var currentDevice: AVAudioSessionPortDescription? = nil + // All devices that can record audio (the ones that can play audio are not included) + @Published var availableInputs: [AVAudioSessionPortDescription] = [] + + + init(_ audioSession: AVAudioSession? = nil) { + self.audioSession = audioSession ?? RTCAudioSession.sharedInstance().session + self.outputs = self.audioSession.currentRoute.outputs + self.availableInputs = self.audioSession.availableInputs ?? [] + } + + func reloadDevices() { + outputs = audioSession.currentRoute.outputs + currentDevice = audioSession.currentRoute.outputs.first + availableInputs = audioSession.availableInputs ?? [] + call?.speakerEnabled = currentDevice?.portType == .builtInSpeaker + + + // Workaround situation: + // have bluetooth device connected, choosing speaker, disconnecting bluetooth device. In this case iOS will not post notification, so do it manually + timer?.invalidate() + if availableInputs.contains(where: { $0.portType != .builtInReceiver && $0.portType != .builtInSpeaker }) { + timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { t in + self.reloadDevices() + } + } + } + + @objc func audioCallback(notification: Notification) { + reloadDevices() + + logger.debug("Changes in devices, current audio devices: \(String(describing: self.availableInputs.map({ $0.portType.rawValue }))), output: \(String(describing: self.currentDevice?.portType.rawValue))") + } + + func start() { + nc.addObserver(self, selector: #selector(audioCallback), name: AVAudioSession.routeChangeNotification, object: nil) + } + + func stop() { + nc.removeObserver(self, name: AVAudioSession.routeChangeNotification, object: nil) + timer?.invalidate() + } +} diff --git a/apps/ios/Shared/Views/Call/CallController.swift b/apps/ios/Shared/Views/Call/CallController.swift index 6da8294ef8..1f28180e87 100644 --- a/apps/ios/Shared/Views/Call/CallController.swift +++ b/apps/ios/Shared/Views/Call/CallController.swift @@ -51,7 +51,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse func provider(_ provider: CXProvider, perform action: CXStartCallAction) { logger.debug("CallController.provider CXStartCallAction") - if callManager.startOutgoingCall(callUUID: action.callUUID) { + if callManager.startOutgoingCall(callUUID: action.callUUID.uuidString.lowercased()) { action.fulfill() provider.reportOutgoingCall(with: action.callUUID, startedConnectingAt: nil) } else { @@ -61,12 +61,30 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { logger.debug("CallController.provider CXAnswerCallAction") - if callManager.answerIncomingCall(callUUID: action.callUUID) { - // WebRTC call should be in connected state to fulfill. - // Otherwise no audio and mic working on lockscreen - fulfillOnConnect = action - } else { - action.fail() + Task { + let chatIsReady = await waitUntilChatStarted(timeoutMs: 30_000, stepMs: 500) + logger.debug("CallController chat started \(chatIsReady) \(ChatModel.shared.chatInitialized) \(ChatModel.shared.chatRunning == true) \(String(describing: AppChatState.shared.value))") + if !chatIsReady { + action.fail() + return + } + if !ChatModel.shared.callInvitations.values.contains(where: { inv in inv.callUUID == action.callUUID.uuidString.lowercased() }) { + try? await justRefreshCallInvitations() + logger.debug("CallController: updated call invitations chat") + } + await MainActor.run { + logger.debug("CallController.provider will answer on call") + + if callManager.answerIncomingCall(callUUID: action.callUUID.uuidString.lowercased()) { + logger.debug("CallController.provider answered on call") + // WebRTC call should be in connected state to fulfill. + // Otherwise no audio and mic working on lockscreen + fulfillOnConnect = action + } else { + logger.debug("CallController.provider will fail the call") + action.fail() + } + } } } @@ -75,7 +93,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse // Should be nil here if connection was in connected state fulfillOnConnect?.fail() fulfillOnConnect = nil - callManager.endCall(callUUID: action.callUUID) { ok in + callManager.endCall(callUUID: action.callUUID.uuidString.lowercased()) { ok in if ok { action.fulfill() } else { @@ -86,7 +104,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse } func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) { - if callManager.enableMedia(media: .audio, enable: !action.isMuted, callUUID: action.callUUID) { + if callManager.enableMedia(source: .mic, enable: !action.isMuted, callUUID: action.callUUID.uuidString.lowercased()) { action.fulfill() } else { action.fail() @@ -103,7 +121,23 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse RTCAudioSession.sharedInstance().audioSessionDidActivate(audioSession) RTCAudioSession.sharedInstance().isAudioEnabled = true do { - try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: .mixWithOthers) + let hasVideo = ChatModel.shared.activeCall?.hasVideo == true + if hasVideo { + try audioSession.setCategory(.playAndRecord, mode: .videoChat, options: [.defaultToSpeaker, .mixWithOthers, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP]) + } else { + try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.mixWithOthers, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP]) + } + // Without any delay sound is not playing from speaker or external device in incoming call + Task { + for i in 0 ... 3 { + try? await Task.sleep(nanoseconds: UInt64(i) * 300_000000) + if let preferred = audioSession.preferredInputDevice() { + await MainActor.run { try? audioSession.setPreferredInput(preferred) } + } else if hasVideo { + await MainActor.run { try? audioSession.overrideOutputAudioPort(.speaker) } + } + } + } logger.debug("audioSession category set") try audioSession.setActive(true) logger.debug("audioSession activated") @@ -140,6 +174,19 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse } } + private func waitUntilChatStarted(timeoutMs: UInt64, stepMs: UInt64) async -> Bool { + logger.debug("CallController waiting until chat started") + var t: UInt64 = 0 + repeat { + if ChatModel.shared.chatInitialized, ChatModel.shared.chatRunning == true, case .active = AppChatState.shared.value { + return true + } + _ = try? await Task.sleep(nanoseconds: stepMs * 1000000) + t += stepMs + } while t < timeoutMs + return false + } + @objc(pushRegistry:didUpdatePushCredentials:forType:) func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) { logger.debug("CallController: didUpdate push credentials for type \(type.rawValue)") @@ -155,32 +202,19 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse self.reportExpiredCall(payload: payload, completion) return } - if (!ChatModel.shared.chatInitialized) { - logger.debug("CallController: initializing chat") - do { - try initializeChat(start: true, refreshInvitations: false) - } catch let error { - logger.error("CallController: initializing chat error: \(error)") - self.reportExpiredCall(payload: payload, completion) - return - } - } - logger.debug("CallController: initialized chat") - startChatForCall() - logger.debug("CallController: started chat") - self.shouldSuspendChat = true - // There are no invitations in the model, as it was processed by NSE - _ = try? justRefreshCallInvitations() - logger.debug("CallController: updated call invitations chat") - // logger.debug("CallController justRefreshCallInvitations: \(String(describing: m.callInvitations))") // Extract the call information from the push notification payload let m = ChatModel.shared if let contactId = payload.dictionaryPayload["contactId"] as? String, - let invitation = m.callInvitations[contactId] { - let update = self.cxCallUpdate(invitation: invitation) - if let uuid = invitation.callkitUUID { + let displayName = payload.dictionaryPayload["displayName"] as? String, + let callUUID = payload.dictionaryPayload["callUUID"] as? String, + let uuid = UUID(uuidString: callUUID), + let callTsInterval = payload.dictionaryPayload["callTs"] as? TimeInterval, + let mediaStr = payload.dictionaryPayload["media"] as? String, + let media = CallMediaType(rawValue: mediaStr) { + let update = self.cxCallUpdate(contactId, displayName, media) + let callTs = Date(timeIntervalSince1970: callTsInterval) + if callTs.timeIntervalSinceNow >= -180 { logger.debug("CallController: report pushkit call via CallKit") - let update = self.cxCallUpdate(invitation: invitation) self.provider.reportNewIncomingCall(with: uuid, update: update) { error in if error != nil { m.callInvitations.removeValue(forKey: contactId) @@ -189,11 +223,31 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse completion() } } else { + logger.debug("CallController will expire call 1") self.reportExpiredCall(update: update, completion) } } else { + logger.debug("CallController will expire call 2") self.reportExpiredCall(payload: payload, completion) } + + //DispatchQueue.main.asyncAfter(deadline: .now() + 10) { + if (!ChatModel.shared.chatInitialized) { + logger.debug("CallController: initializing chat") + do { + try initializeChat(start: true, refreshInvitations: false) + } catch let error { + logger.error("CallController: initializing chat error: \(error)") + if let call = ChatModel.shared.activeCall { + self.endCall(call: call, completed: completion) + } + return + } + } + logger.debug("CallController: initialized chat") + startChatForCall() + logger.debug("CallController: started chat") + self.shouldSuspendChat = true } // This function fulfils the requirement to always report a call when PushKit notification is received, @@ -223,8 +277,8 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse } func reportNewIncomingCall(invitation: RcvCallInvitation, completion: @escaping (Error?) -> Void) { - logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callkitUUID))") - if CallController.useCallKit(), let uuid = invitation.callkitUUID { + logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callUUID))") + if CallController.useCallKit(), let callUUID = invitation.callUUID, let uuid = UUID(uuidString: callUUID) { if invitation.callTs.timeIntervalSinceNow >= -180 { let update = cxCallUpdate(invitation: invitation) provider.reportNewIncomingCall(with: uuid, update: update, completion: completion) @@ -245,6 +299,14 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse return update } + private func cxCallUpdate(_ contactId: String, _ displayName: String, _ media: CallMediaType) -> CXCallUpdate { + let update = CXCallUpdate() + update.remoteHandle = CXHandle(type: .generic, value: contactId) + update.hasVideo = media == .video + update.localizedCallerName = displayName + return update + } + func reportIncomingCall(call: Call, connectedAt dateConnected: Date?) { logger.debug("CallController: reporting incoming call connected") if CallController.useCallKit() { @@ -256,14 +318,14 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse func reportOutgoingCall(call: Call, connectedAt dateConnected: Date?) { logger.debug("CallController: reporting outgoing call connected") - if CallController.useCallKit(), let uuid = call.callkitUUID { + if CallController.useCallKit(), let callUUID = call.callUUID, let uuid = UUID(uuidString: callUUID) { provider.reportOutgoingCall(with: uuid, connectedAt: dateConnected) } } func reportCallRemoteEnded(invitation: RcvCallInvitation) { logger.debug("CallController: reporting remote ended") - if CallController.useCallKit(), let uuid = invitation.callkitUUID { + if CallController.useCallKit(), let callUUID = invitation.callUUID, let uuid = UUID(uuidString: callUUID) { provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded) } else if invitation.contact.id == activeCallInvitation?.contact.id { activeCallInvitation = nil @@ -272,14 +334,17 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse func reportCallRemoteEnded(call: Call) { logger.debug("CallController: reporting remote ended") - if CallController.useCallKit(), let uuid = call.callkitUUID { + if CallController.useCallKit(), let callUUID = call.callUUID, let uuid = UUID(uuidString: callUUID) { provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded) } } func startCall(_ contact: Contact, _ media: CallMediaType) { logger.debug("CallController.startCall") - let uuid = callManager.newOutgoingCall(contact, media) + let callUUID = callManager.newOutgoingCall(contact, media) + guard let uuid = UUID(uuidString: callUUID) else { + return + } if CallController.useCallKit() { let handle = CXHandle(type: .generic, value: contact.id) let action = CXStartCallAction(call: uuid, handle: handle) @@ -291,19 +356,17 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse update.localizedCallerName = contact.displayName self.provider.reportCall(with: uuid, updated: update) } - } else if callManager.startOutgoingCall(callUUID: uuid) { - if callManager.startOutgoingCall(callUUID: uuid) { - logger.debug("CallController.startCall: call started") - } else { - logger.error("CallController.startCall: no active call") - } + } else if callManager.startOutgoingCall(callUUID: callUUID) { + logger.debug("CallController.startCall: call started") + } else { + logger.error("CallController.startCall: no active call") } } func answerCall(invitation: RcvCallInvitation) { logger.debug("CallController: answering a call") - if CallController.useCallKit(), let callUUID = invitation.callkitUUID { - requestTransaction(with: CXAnswerCallAction(call: callUUID)) + if CallController.useCallKit(), let callUUID = invitation.callUUID, let uuid = UUID(uuidString: callUUID) { + requestTransaction(with: CXAnswerCallAction(call: uuid)) } else { callManager.answerIncomingCall(invitation: invitation) } @@ -312,10 +375,13 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse } } - func endCall(callUUID: UUID) { - logger.debug("CallController: ending the call with UUID \(callUUID.uuidString)") + func endCall(callUUID: String) { + let uuid = UUID(uuidString: callUUID) + logger.debug("CallController: ending the call with UUID \(callUUID)") if CallController.useCallKit() { - requestTransaction(with: CXEndCallAction(call: callUUID)) + if let uuid { + requestTransaction(with: CXEndCallAction(call: uuid)) + } } else { callManager.endCall(callUUID: callUUID) { ok in if ok { diff --git a/apps/ios/Shared/Views/Call/CallManager.swift b/apps/ios/Shared/Views/Call/CallManager.swift index a6d5ea17c4..a3e1df2301 100644 --- a/apps/ios/Shared/Views/Call/CallManager.swift +++ b/apps/ios/Shared/Views/Call/CallManager.swift @@ -10,25 +10,25 @@ import Foundation import SimpleXChat class CallManager { - func newOutgoingCall(_ contact: Contact, _ media: CallMediaType) -> UUID { - let uuid = UUID() - let call = Call(direction: .outgoing, contact: contact, callkitUUID: uuid, callState: .waitCapabilities, localMedia: media) + func newOutgoingCall(_ contact: Contact, _ media: CallMediaType) -> String { + let uuid = UUID().uuidString.lowercased() + let call = Call(direction: .outgoing, contact: contact, callUUID: uuid, callState: .waitCapabilities, initialCallType: media) call.speakerEnabled = media == .video ChatModel.shared.activeCall = call return uuid } - func startOutgoingCall(callUUID: UUID) -> Bool { + func startOutgoingCall(callUUID: String) -> Bool { let m = ChatModel.shared - if let call = m.activeCall, call.callkitUUID == callUUID { + if let call = m.activeCall, call.callUUID == callUUID { m.showCallView = true - Task { await m.callCommand.processCommand(.capabilities(media: call.localMedia)) } + Task { await m.callCommand.processCommand(.capabilities(media: call.initialCallType)) } return true } return false } - func answerIncomingCall(callUUID: UUID) -> Bool { + func answerIncomingCall(callUUID: String) -> Bool { if let invitation = getCallInvitation(callUUID) { answerIncomingCall(invitation: invitation) return true @@ -42,9 +42,9 @@ class CallManager { let call = Call( direction: .incoming, contact: invitation.contact, - callkitUUID: invitation.callkitUUID, + callUUID: invitation.callUUID, callState: .invitationAccepted, - localMedia: invitation.callType.media, + initialCallType: invitation.callType.media, sharedKey: invitation.sharedKey ) call.speakerEnabled = invitation.callType.media == .video @@ -68,17 +68,17 @@ class CallManager { } } - func enableMedia(media: CallMediaType, enable: Bool, callUUID: UUID) -> Bool { - if let call = ChatModel.shared.activeCall, call.callkitUUID == callUUID { + func enableMedia(source: CallMediaSource, enable: Bool, callUUID: String) -> Bool { + if let call = ChatModel.shared.activeCall, call.callUUID == callUUID { let m = ChatModel.shared - Task { await m.callCommand.processCommand(.media(media: media, enable: enable)) } + Task { await m.callCommand.processCommand(.media(source: source, enable: enable)) } return true } return false } - func endCall(callUUID: UUID, completed: @escaping (Bool) -> Void) { - if let call = ChatModel.shared.activeCall, call.callkitUUID == callUUID { + func endCall(callUUID: String, completed: @escaping (Bool) -> Void) { + if let call = ChatModel.shared.activeCall, call.callUUID == callUUID { endCall(call: call) { completed(true) } } else if let invitation = getCallInvitation(callUUID) { endCall(invitation: invitation) { completed(true) } @@ -126,8 +126,8 @@ class CallManager { } } - private func getCallInvitation(_ callUUID: UUID) -> RcvCallInvitation? { - if let (_, invitation) = ChatModel.shared.callInvitations.first(where: { (_, inv) in inv.callkitUUID == callUUID }) { + private func getCallInvitation(_ callUUID: String) -> RcvCallInvitation? { + if let (_, invitation) = ChatModel.shared.callInvitations.first(where: { (_, inv) in inv.callUUID == callUUID }) { return invitation } return nil diff --git a/apps/ios/Shared/Views/Call/CallViewRenderers.swift b/apps/ios/Shared/Views/Call/CallViewRenderers.swift index a3201d9351..e779093a24 100644 --- a/apps/ios/Shared/Views/Call/CallViewRenderers.swift +++ b/apps/ios/Shared/Views/Call/CallViewRenderers.swift @@ -10,40 +10,49 @@ import AVKit struct CallViewRemote: UIViewRepresentable { var client: WebRTCClient - var activeCall: Binding + @ObservedObject var call: Call @State var enablePip: (Bool) -> Void = {_ in } @Binding var activeCallViewIsCollapsed: Bool + @Binding var contentMode: UIView.ContentMode @Binding var pipShown: Bool - init(client: WebRTCClient, activeCall: Binding, activeCallViewIsCollapsed: Binding, pipShown: Binding) { - self.client = client - self.activeCall = activeCall - self._activeCallViewIsCollapsed = activeCallViewIsCollapsed - self._pipShown = pipShown - } - func makeUIView(context: Context) -> UIView { let view = UIView() - if let call = activeCall.wrappedValue { - let remoteRenderer = RTCMTLVideoView(frame: view.frame) - remoteRenderer.videoContentMode = .scaleAspectFill - client.addRemoteRenderer(call, remoteRenderer) - addSubviewAndResize(remoteRenderer, into: view) + let remoteCameraRenderer = RTCMTLVideoView(frame: view.frame) + remoteCameraRenderer.videoContentMode = contentMode + remoteCameraRenderer.tag = 0 - if AVPictureInPictureController.isPictureInPictureSupported() { - makeViewWithRTCRenderer(call, remoteRenderer, view, context) - } + let screenVideo = call.peerMediaSources.screenVideo + let remoteScreenRenderer = RTCMTLVideoView(frame: view.frame) + remoteScreenRenderer.videoContentMode = contentMode + remoteScreenRenderer.tag = 1 + remoteScreenRenderer.alpha = screenVideo ? 1 : 0 + + context.coordinator.cameraRenderer = remoteCameraRenderer + context.coordinator.screenRenderer = remoteScreenRenderer + client.addRemoteCameraRenderer(remoteCameraRenderer) + client.addRemoteScreenRenderer(remoteScreenRenderer) + if screenVideo { + addSubviewAndResize(remoteScreenRenderer, remoteCameraRenderer, into: view) + } else { + addSubviewAndResize(remoteCameraRenderer, remoteScreenRenderer, into: view) + } + + if AVPictureInPictureController.isPictureInPictureSupported() { + makeViewWithRTCRenderer(remoteCameraRenderer, remoteScreenRenderer, view, context) } return view } - func makeViewWithRTCRenderer(_ call: WebRTCClient.Call, _ remoteRenderer: RTCMTLVideoView, _ view: UIView, _ context: Context) { - let pipRemoteRenderer = RTCMTLVideoView(frame: view.frame) - pipRemoteRenderer.videoContentMode = .scaleAspectFill - + func makeViewWithRTCRenderer(_ remoteCameraRenderer: RTCMTLVideoView, _ remoteScreenRenderer: RTCMTLVideoView, _ view: UIView, _ context: Context) { + let pipRemoteCameraRenderer = RTCMTLVideoView(frame: view.frame) + pipRemoteCameraRenderer.videoContentMode = .scaleAspectFill + + let pipRemoteScreenRenderer = RTCMTLVideoView(frame: view.frame) + pipRemoteScreenRenderer.videoContentMode = .scaleAspectFill + let pipVideoCallViewController = AVPictureInPictureVideoCallViewController() pipVideoCallViewController.preferredContentSize = CGSize(width: 1080, height: 1920) - addSubviewAndResize(pipRemoteRenderer, into: pipVideoCallViewController.view) let pipContentSource = AVPictureInPictureController.ContentSource( activeVideoCallSourceView: view, contentViewController: pipVideoCallViewController @@ -55,7 +64,9 @@ struct CallViewRemote: UIViewRepresentable { context.coordinator.pipController = pipController context.coordinator.willShowHide = { show in if show { - client.addRemoteRenderer(call, pipRemoteRenderer) + client.addRemoteCameraRenderer(pipRemoteCameraRenderer) + client.addRemoteScreenRenderer(pipRemoteScreenRenderer) + context.coordinator.relayout() DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { activeCallViewIsCollapsed = true } @@ -67,13 +78,29 @@ struct CallViewRemote: UIViewRepresentable { } context.coordinator.didShowHide = { show in if show { - remoteRenderer.isHidden = true + remoteCameraRenderer.isHidden = true + remoteScreenRenderer.isHidden = true } else { - client.removeRemoteRenderer(call, pipRemoteRenderer) - remoteRenderer.isHidden = false + client.removeRemoteCameraRenderer(pipRemoteCameraRenderer) + client.removeRemoteScreenRenderer(pipRemoteScreenRenderer) + remoteCameraRenderer.isHidden = false + remoteScreenRenderer.isHidden = false } pipShown = show } + context.coordinator.relayout = { + let camera = call.peerMediaSources.camera + let screenVideo = call.peerMediaSources.screenVideo + pipRemoteCameraRenderer.alpha = camera ? 1 : 0 + pipRemoteScreenRenderer.alpha = screenVideo ? 1 : 0 + if screenVideo { + addSubviewAndResize(pipRemoteScreenRenderer, pipRemoteCameraRenderer, pip: true, into: pipVideoCallViewController.view) + } else { + addSubviewAndResize(pipRemoteCameraRenderer, pipRemoteScreenRenderer, pip: true, into: pipVideoCallViewController.view) + } + (pipVideoCallViewController.view.subviews[0] as! RTCMTLVideoView).videoContentMode = contentMode + (pipVideoCallViewController.view.subviews[1] as! RTCMTLVideoView).videoContentMode = .scaleAspectFill + } DispatchQueue.main.async { enablePip = { enable in if enable != pipShown /* pipController.isPictureInPictureActive */ { @@ -88,24 +115,50 @@ struct CallViewRemote: UIViewRepresentable { } func makeCoordinator() -> Coordinator { - Coordinator() + Coordinator(client) } func updateUIView(_ view: UIView, context: Context) { logger.debug("CallView.updateUIView remote") + let camera = view.subviews.first(where: { $0.tag == 0 })! + let screen = view.subviews.first(where: { $0.tag == 1 })! + let screenVideo = call.peerMediaSources.screenVideo + if screenVideo && screen.alpha == 0 { + screen.alpha = 1 + addSubviewAndResize(screen, camera, into: view) + } else if !screenVideo && screen.alpha == 1 { + screen.alpha = 0 + addSubviewAndResize(camera, screen, into: view) + } + (view.subviews[0] as! RTCMTLVideoView).videoContentMode = contentMode + (view.subviews[1] as! RTCMTLVideoView).videoContentMode = .scaleAspectFill + + camera.alpha = call.peerMediaSources.camera ? 1 : 0 + screen.alpha = call.peerMediaSources.screenVideo ? 1 : 0 + DispatchQueue.main.async { if activeCallViewIsCollapsed != pipShown { enablePip(activeCallViewIsCollapsed) + } else if pipShown { + context.coordinator.relayout() } } } // MARK: - Coordinator class Coordinator: NSObject, AVPictureInPictureControllerDelegate { + var cameraRenderer: RTCMTLVideoView? + var screenRenderer: RTCMTLVideoView? + var client: WebRTCClient var pipController: AVPictureInPictureController? = nil var willShowHide: (Bool) -> Void = { _ in } var didShowHide: (Bool) -> Void = { _ in } - + var relayout: () -> Void = {} + + required init(_ client: WebRTCClient) { + self.client = client + } + func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { willShowHide(true) } @@ -127,11 +180,20 @@ struct CallViewRemote: UIViewRepresentable { } deinit { + // TODO: deinit is not called when changing call type from audio to video and back, + // which causes many renderers can be created and added to stream (if enabling/disabling + // video while not yet connected in outgoing call) pipController?.stopPictureInPicture() pipController?.canStartPictureInPictureAutomaticallyFromInline = false pipController?.contentSource = nil pipController?.delegate = nil pipController = nil + if let cameraRenderer { + client.removeRemoteCameraRenderer(cameraRenderer) + } + if let screenRenderer { + client.removeRemoteScreenRenderer(screenRenderer) + } } } @@ -148,51 +210,109 @@ struct CallViewRemote: UIViewRepresentable { struct CallViewLocal: UIViewRepresentable { var client: WebRTCClient - var activeCall: Binding var localRendererAspectRatio: Binding @State var pipStateChanged: (Bool) -> Void = {_ in } @Binding var pipShown: Bool - init(client: WebRTCClient, activeCall: Binding, localRendererAspectRatio: Binding, pipShown: Binding) { + init(client: WebRTCClient, localRendererAspectRatio: Binding, pipShown: Binding) { self.client = client - self.activeCall = activeCall self.localRendererAspectRatio = localRendererAspectRatio self._pipShown = pipShown } func makeUIView(context: Context) -> UIView { let view = UIView() - if let call = activeCall.wrappedValue { - let localRenderer = RTCEAGLVideoView(frame: .zero) - client.addLocalRenderer(call, localRenderer) - client.startCaptureLocalVideo(call) - addSubviewAndResize(localRenderer, into: view) - DispatchQueue.main.async { - pipStateChanged = { shown in - localRenderer.isHidden = shown - } + let localRenderer = RTCEAGLVideoView(frame: .zero) + context.coordinator.renderer = localRenderer + client.addLocalRenderer(localRenderer) + addSubviewAndResize(localRenderer, nil, into: view) + DispatchQueue.main.async { + pipStateChanged = { shown in + localRenderer.isHidden = shown } } return view } + func makeCoordinator() -> Coordinator { + Coordinator(client) + } + func updateUIView(_ view: UIView, context: Context) { logger.debug("CallView.updateUIView local") pipStateChanged(pipShown) } + + // MARK: - Coordinator + class Coordinator: NSObject, AVPictureInPictureControllerDelegate { + var renderer: RTCEAGLVideoView? + var client: WebRTCClient + + required init(_ client: WebRTCClient) { + self.client = client + } + + deinit { + if let renderer { + client.removeLocalRenderer(renderer) + } + } + } } -private func addSubviewAndResize(_ view: UIView, into containerView: UIView) { - containerView.addSubview(view) - view.translatesAutoresizingMaskIntoConstraints = false - containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|", - options: [], - metrics: nil, - views: ["view": view])) +private func addSubviewAndResize(_ fullscreen: UIView, _ end: UIView?, pip: Bool = false, into containerView: UIView) { + if containerView.subviews.firstIndex(of: fullscreen) == 0 && ((end == nil && containerView.subviews.count == 1) || (end != nil && containerView.subviews.firstIndex(of: end!) == 1)) { + // Nothing to do, elements on their places + return + } + containerView.removeConstraints(containerView.constraints) + containerView.subviews.forEach { sub in sub.removeFromSuperview()} - containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|", + containerView.addSubview(fullscreen) + fullscreen.translatesAutoresizingMaskIntoConstraints = false + fullscreen.layer.cornerRadius = 0 + fullscreen.layer.masksToBounds = false + + if let end { + containerView.addSubview(end) + end.translatesAutoresizingMaskIntoConstraints = false + end.layer.cornerRadius = pip ? 8 : 10 + end.layer.masksToBounds = true + } + + let constraintFullscreenV = NSLayoutConstraint.constraints( + withVisualFormat: "V:|[fullscreen]|", options: [], metrics: nil, - views: ["view": view])) + views: ["fullscreen": fullscreen] + ) + let constraintFullscreenH = NSLayoutConstraint.constraints( + withVisualFormat: "H:|[fullscreen]|", + options: [], + metrics: nil, + views: ["fullscreen": fullscreen] + ) + + containerView.addConstraints(constraintFullscreenV) + containerView.addConstraints(constraintFullscreenH) + + if let end { + let constraintEndWidth = NSLayoutConstraint( + item: end, attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: pip ? 0.5 : 0.3, constant: 0 + ) + let constraintEndHeight = NSLayoutConstraint( + item: end, attribute: .height, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: pip ? 0.5 * 1.33 : 0.3 * 1.33, constant: 0 + ) + let constraintEndX = NSLayoutConstraint( + item: end, attribute: .leading, relatedBy: .equal, toItem: containerView, attribute: .trailing, multiplier: pip ? 0.5 : 0.7, constant: pip ? -8 : -17 + ) + let constraintEndY = NSLayoutConstraint( + item: end, attribute: .bottom, relatedBy: .equal, toItem: containerView, attribute: .bottom, multiplier: 1, constant: pip ? -8 : -92 + ) + containerView.addConstraint(constraintEndWidth) + containerView.addConstraint(constraintEndHeight) + containerView.addConstraint(constraintEndX) + containerView.addConstraint(constraintEndY) + } containerView.layoutIfNeeded() } diff --git a/apps/ios/Shared/Views/Call/IncomingCallView.swift b/apps/ios/Shared/Views/Call/IncomingCallView.swift index c2d5dabd48..5479a9fada 100644 --- a/apps/ios/Shared/Views/Call/IncomingCallView.swift +++ b/apps/ios/Shared/Views/Call/IncomingCallView.swift @@ -11,6 +11,7 @@ import SimpleXChat struct IncomingCallView: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @ObservedObject var cc = CallController.shared var body: some View { @@ -30,21 +31,21 @@ struct IncomingCallView: View { VStack(alignment: .leading, spacing: 6) { HStack { if m.users.count > 1 { - ProfileImage(imageStr: invitation.user.image, color: .white) - .frame(width: 24, height: 24) + ProfileImage(imageStr: invitation.user.image, size: 24, color: .white) } Image(systemName: invitation.callType.media == .video ? "video.fill" : "phone.fill").foregroundColor(.green) Text(invitation.callTypeText) } HStack { ProfilePreview(profileOf: invitation.contact, color: .white) + .padding(.vertical, 6) Spacer() callButton("Reject", "phone.down.fill", .red) { cc.endCall(invitation: invitation) } - callButton("Ignore", "multiply", .accentColor) { + callButton("Ignore", "multiply", .primary) { cc.activeCallInvitation = nil } @@ -64,7 +65,7 @@ struct IncomingCallView: View { .padding(.horizontal, 16) .padding(.vertical, 12) .frame(maxWidth: .infinity) - .background(Color(uiColor: .tertiarySystemGroupedBackground)) + .modifier(ThemedBackground()) .onAppear { dismissAllSheets() } } @@ -77,7 +78,7 @@ struct IncomingCallView: View { .frame(width: 24, height: 24) Text(text) .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } .frame(minWidth: 44) }) diff --git a/apps/ios/Shared/Views/Call/SoundPlayer.swift b/apps/ios/Shared/Views/Call/SoundPlayer.swift index 17c13ab403..c7803a0cb8 100644 --- a/apps/ios/Shared/Views/Call/SoundPlayer.swift +++ b/apps/ios/Shared/Views/Call/SoundPlayer.swift @@ -8,6 +8,7 @@ import Foundation import AVFoundation +import UIKit class SoundPlayer { static let shared = SoundPlayer() @@ -43,3 +44,63 @@ class SoundPlayer { audioPlayer = nil } } + +class CallSoundsPlayer { + static let shared = CallSoundsPlayer() + private var audioPlayer: AVAudioPlayer? + private var playerTask: Task = Task {} + + private func start(_ soundName: String, delayMs: Double) { + audioPlayer?.stop() + playerTask.cancel() + logger.debug("start \(soundName)") + guard let path = Bundle.main.path(forResource: soundName, ofType: "mp3", inDirectory: "sounds") else { + logger.debug("start: file not found") + return + } + do { + let player = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path)) + if player.prepareToPlay() { + audioPlayer = player + } + } catch { + logger.debug("start: AVAudioPlayer error \(error.localizedDescription)") + } + + playerTask = Task { + while let player = audioPlayer { + player.play() + do { + try await Task.sleep(nanoseconds: UInt64((player.duration * 1_000_000_000) + delayMs * 1_000_000)) + } catch { + break + } + } + } + } + + func startConnectingCallSound() { + start("connecting_call", delayMs: 0) + } + + func startInCallSound() { + // Taken from https://github.com/TelegramOrg/Telegram-Android + // https://github.com/TelegramOrg/Telegram-Android/blob/master/LICENSE + start("in_call", delayMs: 1000) + } + + func stop() { + playerTask.cancel() + audioPlayer?.stop() + audioPlayer = nil + } + + func vibrate(long: Bool) { + // iOS just don't want to vibrate more than once after a short period of time, and all 'styles' feel the same + if long { + AudioServicesPlayAlertSound(kSystemSoundID_Vibrate) + } else { + UIImpactFeedbackGenerator(style: .heavy).impactOccurred() + } + } +} diff --git a/apps/ios/Shared/Views/Call/WebRTC.swift b/apps/ios/Shared/Views/Call/WebRTC.swift index 919b1e14e7..ef9135761c 100644 --- a/apps/ios/Shared/Views/Call/WebRTC.swift +++ b/apps/ios/Shared/Views/Call/WebRTC.swift @@ -18,49 +18,49 @@ class Call: ObservableObject, Equatable { var direction: CallDirection var contact: Contact - var callkitUUID: UUID? - var localMedia: CallMediaType + var callUUID: String? + var initialCallType: CallMediaType + @Published var localMediaSources: CallMediaSources @Published var callState: CallState @Published var localCapabilities: CallCapabilities? - @Published var peerMedia: CallMediaType? + @Published var peerMediaSources: CallMediaSources = CallMediaSources() @Published var sharedKey: String? - @Published var audioEnabled = true @Published var speakerEnabled = false - @Published var videoEnabled: Bool @Published var connectionInfo: ConnectionInfo? @Published var connectedAt: Date? = nil init( direction: CallDirection, contact: Contact, - callkitUUID: UUID?, + callUUID: String?, callState: CallState, - localMedia: CallMediaType, + initialCallType: CallMediaType, sharedKey: String? = nil ) { self.direction = direction self.contact = contact - self.callkitUUID = callkitUUID + self.callUUID = callUUID self.callState = callState - self.localMedia = localMedia + self.initialCallType = initialCallType self.sharedKey = sharedKey - self.videoEnabled = localMedia == .video + self.localMediaSources = CallMediaSources( + mic: AVCaptureDevice.authorizationStatus(for: .audio) == .authorized, + camera: initialCallType == .video && AVCaptureDevice.authorizationStatus(for: .video) == .authorized) } var encrypted: Bool { get { localEncrypted && sharedKey != nil } } - var localEncrypted: Bool { get { localCapabilities?.encryption ?? false } } + private var localEncrypted: Bool { get { localCapabilities?.encryption ?? false } } var encryptionStatus: LocalizedStringKey { get { switch callState { case .waitCapabilities: return "" case .invitationSent: return localEncrypted ? "e2e encrypted" : "no e2e encryption" case .invitationAccepted: return sharedKey == nil ? "contact has no e2e encryption" : "contact has e2e encryption" - default: return !localEncrypted ? "no e2e encryption" : sharedKey == nil ? "contact has no e2e encryption" : "e2e encrypted" + default: return !localEncrypted ? "no e2e encryption" : sharedKey == nil ? "contact has no e2e encryption" : "e2e encrypted" } } } - var hasMedia: Bool { get { callState == .offerSent || callState == .negotiated || callState == .connected } } - var supportsVideo: Bool { get { peerMedia == .video || localMedia == .video } } + var hasVideo: Bool { get { localMediaSources.hasVideo || peerMediaSources.hasVideo } } } enum CallDirection { @@ -105,18 +105,28 @@ struct WVAPIMessage: Equatable, Decodable, Encodable { var command: WCallCommand? } +struct CallMediaSources: Equatable, Codable { + var mic: Bool = false + var camera: Bool = false + var screenAudio: Bool = false + var screenVideo: Bool = false + + var hasVideo: Bool { get { camera || screenVideo } } +} + enum WCallCommand: Equatable, Encodable, Decodable { case capabilities(media: CallMediaType) case start(media: CallMediaType, aesKey: String? = nil, iceServers: [RTCIceServer]? = nil, relay: Bool? = nil) case offer(offer: String, iceCandidates: String, media: CallMediaType, aesKey: String? = nil, iceServers: [RTCIceServer]? = nil, relay: Bool? = nil) case answer(answer: String, iceCandidates: String) case ice(iceCandidates: String) - case media(media: CallMediaType, enable: Bool) + case media(source: CallMediaSource, enable: Bool) case end enum CodingKeys: String, CodingKey { case type case media + case source case aesKey case offer case answer @@ -167,9 +177,9 @@ enum WCallCommand: Equatable, Encodable, Decodable { case let .ice(iceCandidates): try container.encode("ice", forKey: .type) try container.encode(iceCandidates, forKey: .iceCandidates) - case let .media(media, enable): + case let .media(source, enable): try container.encode("media", forKey: .type) - try container.encode(media, forKey: .media) + try container.encode(source, forKey: .media) try container.encode(enable, forKey: .enable) case .end: try container.encode("end", forKey: .type) @@ -205,9 +215,9 @@ enum WCallCommand: Equatable, Encodable, Decodable { let iceCandidates = try container.decode(String.self, forKey: CodingKeys.iceCandidates) self = .ice(iceCandidates: iceCandidates) case "media": - let media = try container.decode(CallMediaType.self, forKey: CodingKeys.media) + let source = try container.decode(CallMediaSource.self, forKey: CodingKeys.source) let enable = try container.decode(Bool.self, forKey: CodingKeys.enable) - self = .media(media: media, enable: enable) + self = .media(source: source, enable: enable) case "end": self = .end default: @@ -224,6 +234,7 @@ enum WCallResponse: Equatable, Decodable { case ice(iceCandidates: String) case connection(state: ConnectionState) case connected(connectionInfo: ConnectionInfo) + case peerMedia(source: CallMediaSource, enabled: Bool) case ended case ok case error(message: String) @@ -238,6 +249,8 @@ enum WCallResponse: Equatable, Decodable { case state case connectionInfo case message + case source + case enabled } var respType: String { @@ -249,6 +262,7 @@ enum WCallResponse: Equatable, Decodable { case .ice: return "ice" case .connection: return "connection" case .connected: return "connected" + case .peerMedia: return "peerMedia" case .ended: return "ended" case .ok: return "ok" case .error: return "error" @@ -283,6 +297,10 @@ enum WCallResponse: Equatable, Decodable { case "connected": let connectionInfo = try container.decode(ConnectionInfo.self, forKey: CodingKeys.connectionInfo) self = .connected(connectionInfo: connectionInfo) + case "peerMedia": + let source = try container.decode(CallMediaSource.self, forKey: CodingKeys.source) + let enabled = try container.decode(Bool.self, forKey: CodingKeys.enabled) + self = .peerMedia(source: source, enabled: enabled) case "ended": self = .ended case "ok": @@ -324,6 +342,10 @@ extension WCallResponse: Encodable { case let .connected(connectionInfo): try container.encode("connected", forKey: .type) try container.encode(connectionInfo, forKey: .connectionInfo) + case let .peerMedia(source, enabled): + try container.encode("peerMedia", forKey: .type) + try container.encode(source, forKey: .source) + try container.encode(enabled, forKey: .enabled) case .ended: try container.encode("ended", forKey: .type) case .ok: @@ -376,7 +398,7 @@ actor WebRTCCommandProcessor { func shouldRunCommand(_ client: WebRTCClient, _ c: WCallCommand) -> Bool { switch c { case .capabilities, .start, .offer, .end: true - default: client.activeCall.wrappedValue != nil + default: client.activeCall != nil } } } @@ -431,17 +453,18 @@ struct RTCIceServer: Codable, Equatable { } // the servers are expected in this format: -// stun:stun.simplex.im:443?transport=tcp -// turn:private:yleob6AVkiNI87hpR94Z@turn.simplex.im:443?transport=tcp +// stuns:stun.simplex.im:443?transport=tcp +// turns:private2:Hxuq2QxUjnhj96Zq2r4HjqHRj@turn.simplex.im:443?transport=tcp func parseRTCIceServer(_ str: String) -> RTCIceServer? { var s = replaceScheme(str, "stun:") + s = replaceScheme(s, "stuns:") s = replaceScheme(s, "turn:") s = replaceScheme(s, "turns:") if let u: URL = URL(string: s), let scheme = u.scheme, let host = u.host, let port = u.port, - u.path == "" && (scheme == "stun" || scheme == "turn" || scheme == "turns") { + u.path == "" && (scheme == "stun" || scheme == "stuns" || scheme == "turn" || scheme == "turns") { let query = u.query == nil || u.query == "" ? "" : "?" + (u.query ?? "") return RTCIceServer( urls: ["\(scheme):\(host):\(port)\(query)"], diff --git a/apps/ios/Shared/Views/Call/WebRTCClient.swift b/apps/ios/Shared/Views/Call/WebRTCClient.swift index 1806984d64..db7910836e 100644 --- a/apps/ios/Shared/Views/Call/WebRTCClient.swift +++ b/apps/ios/Shared/Views/Call/WebRTCClient.swift @@ -23,15 +23,24 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg struct Call { var connection: RTCPeerConnection var iceCandidates: IceCandidates - var localMedia: CallMediaType var localCamera: RTCVideoCapturer? - var localVideoSource: RTCVideoSource? - var localStream: RTCVideoTrack? - var remoteStream: RTCVideoTrack? - var device: AVCaptureDevice.Position = .front + var localAudioTrack: RTCAudioTrack? + var localVideoTrack: RTCVideoTrack? + var remoteAudioTrack: RTCAudioTrack? + var remoteVideoTrack: RTCVideoTrack? + var remoteScreenAudioTrack: RTCAudioTrack? + var remoteScreenVideoTrack: RTCVideoTrack? + var device: AVCaptureDevice.Position var aesKey: String? var frameEncryptor: RTCFrameEncryptor? var frameDecryptor: RTCFrameDecryptor? + var peerHasOldVersion: Bool + } + + struct NotConnectedCall { + var audioTrack: RTCAudioTrack? + var localCameraAndTrack: (RTCVideoCapturer, RTCVideoTrack)? + var device: AVCaptureDevice.Position = .front } actor IceCandidates { @@ -49,68 +58,77 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg } private let rtcAudioSession = RTCAudioSession.sharedInstance() - private let audioQueue = DispatchQueue(label: "audio") + private let audioQueue = DispatchQueue(label: "chat.simplex.app.audio") private var sendCallResponse: (WVAPIMessage) async -> Void - var activeCall: Binding + var activeCall: Call? + var notConnectedCall: NotConnectedCall? private var localRendererAspectRatio: Binding + var cameraRenderers: [RTCVideoRenderer] = [] + var screenRenderers: [RTCVideoRenderer] = [] + @available(*, unavailable) override init() { fatalError("Unimplemented") } - required init(_ activeCall: Binding, _ sendCallResponse: @escaping (WVAPIMessage) async -> Void, _ localRendererAspectRatio: Binding) { + required init(_ sendCallResponse: @escaping (WVAPIMessage) async -> Void, _ localRendererAspectRatio: Binding) { self.sendCallResponse = sendCallResponse - self.activeCall = activeCall self.localRendererAspectRatio = localRendererAspectRatio rtcAudioSession.useManualAudio = CallController.useCallKit() rtcAudioSession.isAudioEnabled = !CallController.useCallKit() - logger.debug("WebRTCClient: rtcAudioSession has manual audio \(self.rtcAudioSession.useManualAudio) and audio enabled \(self.rtcAudioSession.isAudioEnabled)}") + logger.debug("WebRTCClient: rtcAudioSession has manual audio \(self.rtcAudioSession.useManualAudio) and audio enabled \(self.rtcAudioSession.isAudioEnabled)") super.init() } let defaultIceServers: [WebRTC.RTCIceServer] = [ - WebRTC.RTCIceServer(urlStrings: ["stun:stun.simplex.im:443"]), - WebRTC.RTCIceServer(urlStrings: ["turn:turn.simplex.im:443?transport=udp"], username: "private", credential: "yleob6AVkiNI87hpR94Z"), - WebRTC.RTCIceServer(urlStrings: ["turn:turn.simplex.im:443?transport=tcp"], username: "private", credential: "yleob6AVkiNI87hpR94Z"), + WebRTC.RTCIceServer(urlStrings: ["stuns:stun.simplex.im:443"]), + //WebRTC.RTCIceServer(urlStrings: ["turns:turn.simplex.im:443?transport=udp"], username: "private2", credential: "Hxuq2QxUjnhj96Zq2r4HjqHRj"), + WebRTC.RTCIceServer(urlStrings: ["turns:turn.simplex.im:443?transport=tcp"], username: "private2", credential: "Hxuq2QxUjnhj96Zq2r4HjqHRj"), ] func initializeCall(_ iceServers: [WebRTC.RTCIceServer]?, _ mediaType: CallMediaType, _ aesKey: String?, _ relay: Bool?) -> Call { let connection = createPeerConnection(iceServers ?? getWebRTCIceServers() ?? defaultIceServers, relay) connection.delegate = self - createAudioSender(connection) - var localStream: RTCVideoTrack? = nil - var remoteStream: RTCVideoTrack? = nil + let device = notConnectedCall?.device ?? .front var localCamera: RTCVideoCapturer? = nil - var localVideoSource: RTCVideoSource? = nil - if mediaType == .video { - (localStream, remoteStream, localCamera, localVideoSource) = createVideoSender(connection) + var localAudioTrack: RTCAudioTrack? = nil + var localVideoTrack: RTCVideoTrack? = nil + if let localCameraAndTrack = notConnectedCall?.localCameraAndTrack { + (localCamera, localVideoTrack) = localCameraAndTrack + } else if notConnectedCall == nil && mediaType == .video { + (localCamera, localVideoTrack) = createVideoTrackAndStartCapture(device) } + if let audioTrack = notConnectedCall?.audioTrack { + localAudioTrack = audioTrack + } else if notConnectedCall == nil { + localAudioTrack = createAudioTrack() + } + notConnectedCall?.localCameraAndTrack = nil + notConnectedCall?.audioTrack = nil + var frameEncryptor: RTCFrameEncryptor? = nil var frameDecryptor: RTCFrameDecryptor? = nil if aesKey != nil { let encryptor = RTCFrameEncryptor.init(sizeChange: Int32(WebRTCClient.ivTagBytes)) encryptor.delegate = self frameEncryptor = encryptor - connection.senders.forEach { $0.setRtcFrameEncryptor(encryptor) } let decryptor = RTCFrameDecryptor.init(sizeChange: -Int32(WebRTCClient.ivTagBytes)) decryptor.delegate = self frameDecryptor = decryptor - // Has no video receiver in outgoing call if applied here, see [peerConnection(_ connection: RTCPeerConnection, didChange newState] - // connection.receivers.forEach { $0.setRtcFrameDecryptor(decryptor) } } return Call( connection: connection, iceCandidates: IceCandidates(), - localMedia: mediaType, localCamera: localCamera, - localVideoSource: localVideoSource, - localStream: localStream, - remoteStream: remoteStream, + localAudioTrack: localAudioTrack, + localVideoTrack: localVideoTrack, + device: device, aesKey: aesKey, frameEncryptor: frameEncryptor, - frameDecryptor: frameDecryptor + frameDecryptor: frameDecryptor, + peerHasOldVersion: false ) } @@ -151,18 +169,24 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg func sendCallCommand(command: WCallCommand) async { var resp: WCallResponse? = nil - let pc = activeCall.wrappedValue?.connection + let pc = activeCall?.connection switch command { - case .capabilities: + case let .capabilities(media): // outgoing + let localCameraAndTrack: (RTCVideoCapturer, RTCVideoTrack)? = media == .video + ? createVideoTrackAndStartCapture(.front) + : nil + notConnectedCall = NotConnectedCall(audioTrack: createAudioTrack(), localCameraAndTrack: localCameraAndTrack, device: .front) resp = .capabilities(capabilities: CallCapabilities(encryption: WebRTCClient.enableEncryption)) - case let .start(media: media, aesKey, iceServers, relay): + case let .start(media: media, aesKey, iceServers, relay): // incoming logger.debug("starting incoming call - create webrtc session") - if activeCall.wrappedValue != nil { endCall() } + if activeCall != nil { endCall() } let encryption = WebRTCClient.enableEncryption let call = initializeCall(iceServers?.toWebRTCIceServers(), media, encryption ? aesKey : nil, relay) - activeCall.wrappedValue = call + activeCall = call + setupLocalTracks(true, call) let (offer, error) = await call.connection.offer() if let offer = offer { + setupEncryptionForLocalTracks(call) resp = .offer( offer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: offer.type.toSdpType(), sdp: offer.sdp))), iceCandidates: compressToBase64(input: encodeJSON(await self.getInitialIceCandidates())), @@ -172,18 +196,24 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg } else { resp = .error(message: "offer error: \(error?.localizedDescription ?? "unknown error")") } - case let .offer(offer, iceCandidates, media, aesKey, iceServers, relay): - if activeCall.wrappedValue != nil { + case let .offer(offer, iceCandidates, media, aesKey, iceServers, relay): // outgoing + if activeCall != nil { resp = .error(message: "accept: call already started") } else if !WebRTCClient.enableEncryption && aesKey != nil { resp = .error(message: "accept: encryption is not supported") } else if let offer: CustomRTCSessionDescription = decodeJSON(decompressFromBase64(input: offer)), let remoteIceCandidates: [RTCIceCandidate] = decodeJSON(decompressFromBase64(input: iceCandidates)) { let call = initializeCall(iceServers?.toWebRTCIceServers(), media, WebRTCClient.enableEncryption ? aesKey : nil, relay) - activeCall.wrappedValue = call + activeCall = call let pc = call.connection if let type = offer.type, let sdp = offer.sdp { if (try? await pc.setRemoteDescription(RTCSessionDescription(type: type.toWebRTCSdpType(), sdp: sdp))) != nil { + setupLocalTracks(false, call) + setupEncryptionForLocalTracks(call) + pc.transceivers.forEach { transceiver in + transceiver.setDirection(.sendRecv, error: nil) + } + await adaptToOldVersion(pc.transceivers.count <= 2) let (answer, error) = await pc.answer() if let answer = answer { self.addIceCandidates(pc, remoteIceCandidates) @@ -200,7 +230,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg } } } - case let .answer(answer, iceCandidates): + case let .answer(answer, iceCandidates): // incoming if pc == nil { resp = .error(message: "answer: call not started") } else if pc?.localDescription == nil { @@ -212,6 +242,9 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg let type = answer.type, let sdp = answer.sdp, let pc = pc { if (try? await pc.setRemoteDescription(RTCSessionDescription(type: type.toWebRTCSdpType(), sdp: sdp))) != nil { + var currentDirection: RTCRtpTransceiverDirection = .sendOnly + pc.transceivers[2].currentDirection(¤tDirection) + await adaptToOldVersion(currentDirection == .sendOnly) addIceCandidates(pc, remoteIceCandidates) resp = .ok } else { @@ -226,13 +259,11 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg } else { resp = .error(message: "ice: call not started") } - case let .media(media, enable): - if activeCall.wrappedValue == nil { + case let .media(source, enable): + if activeCall == nil { resp = .error(message: "media: call not started") - } else if activeCall.wrappedValue?.localMedia == .audio && media == .video { - resp = .error(message: "media: no video") } else { - enableMedia(media, enable) + await enableMedia(source, enable) resp = .ok } case .end: @@ -247,7 +278,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg func getInitialIceCandidates() async -> [RTCIceCandidate] { await untilIceComplete(timeoutMs: 750, stepMs: 150) {} - let candidates = await activeCall.wrappedValue?.iceCandidates.getAndClear() ?? [] + let candidates = await activeCall?.iceCandidates.getAndClear() ?? [] logger.debug("WebRTCClient: sending initial ice candidates: \(candidates.count)") return candidates } @@ -255,7 +286,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg func waitForMoreIceCandidates() { Task { await untilIceComplete(timeoutMs: 12000, stepMs: 1500) { - let candidates = await self.activeCall.wrappedValue?.iceCandidates.getAndClear() ?? [] + let candidates = await self.activeCall?.iceCandidates.getAndClear() ?? [] if candidates.count > 0 { logger.debug("WebRTCClient: sending more ice candidates: \(candidates.count)") await self.sendIceCandidates(candidates) @@ -272,25 +303,202 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg ) } - func enableMedia(_ media: CallMediaType, _ enable: Bool) { - logger.debug("WebRTCClient: enabling media \(media.rawValue) \(enable)") - media == .video ? setVideoEnabled(enable) : setAudioEnabled(enable) + func setupMuteUnmuteListener(_ transceiver: RTCRtpTransceiver, _ track: RTCMediaStreamTrack) { + // logger.log("Setting up mute/unmute listener in the call without encryption for mid = \(transceiver.mid)") + Task { + var lastBytesReceived: Int64 = 0 + // muted initially + var mutedSeconds = 4 + while let call = self.activeCall, transceiver.receiver.track?.readyState == .live { + let stats: RTCStatisticsReport = await call.connection.statistics(for: transceiver.receiver) + let stat = stats.statistics.values.first(where: { stat in stat.type == "inbound-rtp"}) + if let stat { + //logger.debug("Stat \(stat.debugDescription)") + let bytes = stat.values["bytesReceived"] as! Int64 + if bytes <= lastBytesReceived { + mutedSeconds += 1 + if mutedSeconds == 3 { + await MainActor.run { + self.onMediaMuteUnmute(transceiver.mid, true) + } + } + } else { + if mutedSeconds >= 3 { + await MainActor.run { + self.onMediaMuteUnmute(transceiver.mid, false) + } + } + lastBytesReceived = bytes + mutedSeconds = 0 + } + } + try? await Task.sleep(nanoseconds: 1000_000000) + } + } } - func addLocalRenderer(_ activeCall: Call, _ renderer: RTCEAGLVideoView) { - activeCall.localStream?.add(renderer) + @MainActor + func onMediaMuteUnmute(_ transceiverMid: String?, _ mute: Bool) { + guard let activeCall = ChatModel.shared.activeCall else { return } + let source = mediaSourceFromTransceiverMid(transceiverMid) + logger.log("Mute/unmute \(source.rawValue) track = \(mute) with mid = \(transceiverMid ?? "nil")") + if source == .mic && activeCall.peerMediaSources.mic == mute { + activeCall.peerMediaSources.mic = !mute + } else if (source == .camera && activeCall.peerMediaSources.camera == mute) { + activeCall.peerMediaSources.camera = !mute + } else if (source == .screenAudio && activeCall.peerMediaSources.screenAudio == mute) { + activeCall.peerMediaSources.screenAudio = !mute + } else if (source == .screenVideo && activeCall.peerMediaSources.screenVideo == mute) { + activeCall.peerMediaSources.screenVideo = !mute + } + } + + @MainActor + func enableMedia(_ source: CallMediaSource, _ enable: Bool) { + logger.debug("WebRTCClient: enabling media \(source.rawValue) \(enable)") + source == .camera ? setCameraEnabled(enable) : setAudioEnabled(enable) + } + + @MainActor + func adaptToOldVersion(_ peerHasOldVersion: Bool) { + activeCall?.peerHasOldVersion = peerHasOldVersion + if peerHasOldVersion { + logger.debug("The peer has an old version. Remote audio track is nil = \(self.activeCall?.remoteAudioTrack == nil), video = \(self.activeCall?.remoteVideoTrack == nil)") + onMediaMuteUnmute("0", false) + if activeCall?.remoteVideoTrack != nil { + onMediaMuteUnmute("1", false) + } + if ChatModel.shared.activeCall?.localMediaSources.camera == true && ChatModel.shared.activeCall?.peerMediaSources.camera == false { + logger.debug("Stopping video track for the old version") + activeCall?.connection.senders[1].track = nil + ChatModel.shared.activeCall?.localMediaSources.camera = false + (activeCall?.localCamera as? RTCCameraVideoCapturer)?.stopCapture() + activeCall?.localCamera = nil + activeCall?.localVideoTrack = nil + } + } + } + + func addLocalRenderer(_ renderer: RTCEAGLVideoView) { + if let activeCall { + if let track = activeCall.localVideoTrack { + track.add(renderer) + } + } else if let notConnectedCall { + if let track = notConnectedCall.localCameraAndTrack?.1 { + track.add(renderer) + } + } // To get width and height of a frame, see videoView(videoView:, didChangeVideoSize) renderer.delegate = self } + func removeLocalRenderer(_ renderer: RTCEAGLVideoView) { + if let activeCall { + if let track = activeCall.localVideoTrack { + track.remove(renderer) + } + } else if let notConnectedCall { + if let track = notConnectedCall.localCameraAndTrack?.1 { + track.remove(renderer) + } + } + renderer.delegate = nil + } + func videoView(_ videoView: RTCVideoRenderer, didChangeVideoSize size: CGSize) { guard size.height > 0 else { return } localRendererAspectRatio.wrappedValue = size.width / size.height } + func setupLocalTracks(_ incomingCall: Bool, _ call: Call) { + let pc = call.connection + let transceivers = call.connection.transceivers + let audioTrack = call.localAudioTrack + let videoTrack = call.localVideoTrack + + if incomingCall { + let micCameraInit = RTCRtpTransceiverInit() + // streamIds required for old versions which adds tracks from stream, not from track property + micCameraInit.streamIds = ["micCamera"] + + let screenAudioVideoInit = RTCRtpTransceiverInit() + screenAudioVideoInit.streamIds = ["screenAudioVideo"] + + // incoming call, no transceivers yet. But they should be added in order: mic, camera, screen audio, screen video + // mid = 0, mic + if let audioTrack { + pc.addTransceiver(with: audioTrack, init: micCameraInit) + } else { + pc.addTransceiver(of: .audio, init: micCameraInit) + } + // mid = 1, camera + if let videoTrack { + pc.addTransceiver(with: videoTrack, init: micCameraInit) + } else { + pc.addTransceiver(of: .video, init: micCameraInit) + } + // mid = 2, screenAudio + pc.addTransceiver(of: .audio, init: screenAudioVideoInit) + // mid = 3, screenVideo + pc.addTransceiver(of: .video, init: screenAudioVideoInit) + } else { + // new version + if transceivers.count > 2 { + // Outgoing call. All transceivers are ready. Don't addTrack() because it will create new transceivers, replace existing (nil) tracks + transceivers + .first(where: { elem in mediaSourceFromTransceiverMid(elem.mid) == .mic })? + .sender.track = audioTrack + transceivers + .first(where: { elem in mediaSourceFromTransceiverMid(elem.mid) == .camera })? + .sender.track = videoTrack + } else { + // old version, only two transceivers + if let audioTrack { + pc.add(audioTrack, streamIds: ["micCamera"]) + } else { + // it's important to have any track in order to be able to turn it on again (currently it's off) + let sender = pc.add(createAudioTrack(), streamIds: ["micCamera"]) + sender?.track = nil + } + if let videoTrack { + pc.add(videoTrack, streamIds: ["micCamera"]) + } else { + // it's important to have any track in order to be able to turn it on again (currently it's off) + let localVideoSource = WebRTCClient.factory.videoSource() + let localVideoTrack = WebRTCClient.factory.videoTrack(with: localVideoSource, trackId: "video0") + let sender = pc.add(localVideoTrack, streamIds: ["micCamera"]) + sender?.track = nil + } + } + } + } + + func mediaSourceFromTransceiverMid(_ mid: String?) -> CallMediaSource { + switch mid { + case "0": + return .mic + case "1": + return .camera + case "2": + return .screenAudio + case "3": + return .screenVideo + default: + return .unknown + } + } + + // Should be called after local description set + func setupEncryptionForLocalTracks(_ call: Call) { + if let encryptor = call.frameEncryptor { + call.connection.senders.forEach { $0.setRtcFrameEncryptor(encryptor) } + } + } + func frameDecryptor(_ decryptor: RTCFrameDecryptor, mediaType: RTCRtpMediaType, withFrame encrypted: Data) -> Data? { guard encrypted.count > 0 else { return nil } - if var key: [CChar] = activeCall.wrappedValue?.aesKey?.cString(using: .utf8), + if var key: [CChar] = activeCall?.aesKey?.cString(using: .utf8), let pointer: UnsafeMutableRawPointer = malloc(encrypted.count) { memcpy(pointer, (encrypted as NSData).bytes, encrypted.count) let isKeyFrame = encrypted[0] & 1 == 0 @@ -304,7 +512,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg func frameEncryptor(_ encryptor: RTCFrameEncryptor, mediaType: RTCRtpMediaType, withFrame unencrypted: Data) -> Data? { guard unencrypted.count > 0 else { return nil } - if var key: [CChar] = activeCall.wrappedValue?.aesKey?.cString(using: .utf8), + if var key: [CChar] = activeCall?.aesKey?.cString(using: .utf8), let pointer: UnsafeMutableRawPointer = malloc(unencrypted.count + WebRTCClient.ivTagBytes) { memcpy(pointer, (unencrypted as NSData).bytes, unencrypted.count) let isKeyFrame = unencrypted[0] & 1 == 0 @@ -327,18 +535,42 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg } } - func addRemoteRenderer(_ activeCall: Call, _ renderer: RTCVideoRenderer) { - activeCall.remoteStream?.add(renderer) + func addRemoteCameraRenderer(_ renderer: RTCVideoRenderer) { + if activeCall?.remoteVideoTrack != nil { + activeCall?.remoteVideoTrack?.add(renderer) + } else { + cameraRenderers.append(renderer) + } } - func removeRemoteRenderer(_ activeCall: Call, _ renderer: RTCVideoRenderer) { - activeCall.remoteStream?.remove(renderer) + func removeRemoteCameraRenderer(_ renderer: RTCVideoRenderer) { + if activeCall?.remoteVideoTrack != nil { + activeCall?.remoteVideoTrack?.remove(renderer) + } else { + cameraRenderers.removeAll(where: { $0.isEqual(renderer) }) + } } - func startCaptureLocalVideo(_ activeCall: Call) { + func addRemoteScreenRenderer(_ renderer: RTCVideoRenderer) { + if activeCall?.remoteScreenVideoTrack != nil { + activeCall?.remoteScreenVideoTrack?.add(renderer) + } else { + screenRenderers.append(renderer) + } + } + + func removeRemoteScreenRenderer(_ renderer: RTCVideoRenderer) { + if activeCall?.remoteScreenVideoTrack != nil { + activeCall?.remoteScreenVideoTrack?.remove(renderer) + } else { + screenRenderers.removeAll(where: { $0.isEqual(renderer) }) + } + } + + func startCaptureLocalVideo(_ device: AVCaptureDevice.Position?, _ capturer: RTCVideoCapturer?) { #if targetEnvironment(simulator) guard - let capturer = activeCall.localCamera as? RTCFileVideoCapturer + let capturer = (activeCall?.localCamera ?? notConnectedCall?.localCameraAndTrack?.0) as? RTCFileVideoCapturer else { logger.error("Unable to work with a file capturer") return @@ -348,10 +580,10 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg capturer.startCapturing(fromFileNamed: "sounds/video.mp4") #else guard - let capturer = activeCall.localCamera as? RTCCameraVideoCapturer, - let camera = (RTCCameraVideoCapturer.captureDevices().first { $0.position == activeCall.device }) + let capturer = capturer as? RTCCameraVideoCapturer, + let camera = (RTCCameraVideoCapturer.captureDevices().first { $0.position == device }) else { - logger.error("Unable to find a camera") + logger.error("Unable to find a camera or local track") return } @@ -377,19 +609,6 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg #endif } - private func createAudioSender(_ connection: RTCPeerConnection) { - let streamId = "stream" - let audioTrack = createAudioTrack() - connection.add(audioTrack, streamIds: [streamId]) - } - - private func createVideoSender(_ connection: RTCPeerConnection) -> (RTCVideoTrack?, RTCVideoTrack?, RTCVideoCapturer?, RTCVideoSource?) { - let streamId = "stream" - let (localVideoTrack, localCamera, localVideoSource) = createVideoTrack() - connection.add(localVideoTrack, streamIds: [streamId]) - return (localVideoTrack, connection.transceivers.first { $0.mediaType == .video }?.receiver.track as? RTCVideoTrack, localCamera, localVideoSource) - } - private func createAudioTrack() -> RTCAudioTrack { let audioConstrains = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil) let audioSource = WebRTCClient.factory.audioSource(with: audioConstrains) @@ -397,7 +616,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg return audioTrack } - private func createVideoTrack() -> (RTCVideoTrack, RTCVideoCapturer, RTCVideoSource) { + private func createVideoTrackAndStartCapture(_ device: AVCaptureDevice.Position) -> (RTCVideoCapturer, RTCVideoTrack) { let localVideoSource = WebRTCClient.factory.videoSource() #if targetEnvironment(simulator) @@ -407,19 +626,30 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg #endif let localVideoTrack = WebRTCClient.factory.videoTrack(with: localVideoSource, trackId: "video0") - return (localVideoTrack, localCamera, localVideoSource) + startCaptureLocalVideo(device, localCamera) + return (localCamera, localVideoTrack) } func endCall() { - guard let call = activeCall.wrappedValue else { return } + if #available(iOS 16.0, *) { + _endCall() + } else { + // Fixes `connection.close()` getting locked up in iOS15 + DispatchQueue.global(qos: .utility).async { self._endCall() } + } + } + + private func _endCall() { + (notConnectedCall?.localCameraAndTrack?.0 as? RTCCameraVideoCapturer)?.stopCapture() + guard let call = activeCall else { return } logger.debug("WebRTCClient: ending the call") - activeCall.wrappedValue = nil - (call.localCamera as? RTCCameraVideoCapturer)?.stopCapture() call.connection.close() call.connection.delegate = nil call.frameEncryptor?.delegate = nil call.frameDecryptor?.delegate = nil + (call.localCamera as? RTCCameraVideoCapturer)?.stopCapture() audioSessionToDefaults() + activeCall = nil } func untilIceComplete(timeoutMs: UInt64, stepMs: UInt64, action: @escaping () async -> Void) async { @@ -428,7 +658,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg _ = try? await Task.sleep(nanoseconds: stepMs * 1000000) t += stepMs await action() - } while t < timeoutMs && activeCall.wrappedValue?.connection.iceGatheringState != .complete + } while t < timeoutMs && activeCall?.connection.iceGatheringState != .complete } } @@ -489,11 +719,40 @@ extension WebRTCClient: RTCPeerConnectionDelegate { logger.debug("Connection should negotiate") } + func peerConnection(_ peerConnection: RTCPeerConnection, didStartReceivingOn transceiver: RTCRtpTransceiver) { + if let track = transceiver.receiver.track { + DispatchQueue.main.async { + // Doesn't work for outgoing video call (audio in video call works ok still, same as incoming call) +// if let decryptor = self.activeCall?.frameDecryptor { +// transceiver.receiver.setRtcFrameDecryptor(decryptor) +// } + let source = self.mediaSourceFromTransceiverMid(transceiver.mid) + switch source { + case .mic: self.activeCall?.remoteAudioTrack = track as? RTCAudioTrack + case .camera: + self.activeCall?.remoteVideoTrack = track as? RTCVideoTrack + self.cameraRenderers.forEach({ renderer in + self.activeCall?.remoteVideoTrack?.add(renderer) + }) + self.cameraRenderers.removeAll() + case .screenAudio: self.activeCall?.remoteScreenAudioTrack = track as? RTCAudioTrack + case .screenVideo: + self.activeCall?.remoteScreenVideoTrack = track as? RTCVideoTrack + self.screenRenderers.forEach({ renderer in + self.activeCall?.remoteScreenVideoTrack?.add(renderer) + }) + self.screenRenderers.removeAll() + case .unknown: () + } + } + self.setupMuteUnmuteListener(transceiver, track) + } + } + func peerConnection(_ connection: RTCPeerConnection, didChange newState: RTCIceConnectionState) { debugPrint("Connection new connection state: \(newState.toString() ?? "" + newState.rawValue.description) \(connection.receivers)") - guard let call = activeCall.wrappedValue, - let connectionStateString = newState.toString(), + guard let connectionStateString = newState.toString(), let iceConnectionStateString = connection.iceConnectionState.toString(), let iceGatheringStateString = connection.iceGatheringState.toString(), let signalingStateString = connection.signalingState.toString() @@ -514,18 +773,14 @@ extension WebRTCClient: RTCPeerConnectionDelegate { switch newState { case .checking: - if let frameDecryptor = activeCall.wrappedValue?.frameDecryptor { + if let frameDecryptor = activeCall?.frameDecryptor { connection.receivers.forEach { $0.setRtcFrameDecryptor(frameDecryptor) } } - let enableSpeaker: Bool - switch call.localMedia { - case .video: enableSpeaker = true - default: enableSpeaker = false - } + let enableSpeaker: Bool = ChatModel.shared.activeCall?.localMediaSources.hasVideo == true setSpeakerEnabledAndConfigureSession(enableSpeaker) case .connected: sendConnectedEvent(connection) case .disconnected, .failed: endCall() - default: do {} + default: () } } } @@ -537,7 +792,7 @@ extension WebRTCClient: RTCPeerConnectionDelegate { func peerConnection(_ connection: RTCPeerConnection, didGenerate candidate: WebRTC.RTCIceCandidate) { // logger.debug("Connection generated candidate \(candidate.debugDescription)") Task { - await self.activeCall.wrappedValue?.iceCandidates.append(candidate.toCandidate(nil, nil)) + await self.activeCall?.iceCandidates.append(candidate.toCandidate(nil, nil)) } } @@ -592,11 +847,42 @@ extension WebRTCClient: RTCPeerConnectionDelegate { } extension WebRTCClient { - func setAudioEnabled(_ enabled: Bool) { - setTrackEnabled(RTCAudioTrack.self, enabled) + static func isAuthorized(for type: AVMediaType) async -> Bool { + let status = AVCaptureDevice.authorizationStatus(for: type) + var isAuthorized = status == .authorized + if status == .notDetermined { + isAuthorized = await AVCaptureDevice.requestAccess(for: type) + } + return isAuthorized } - func setSpeakerEnabledAndConfigureSession( _ enabled: Bool) { + static func showUnauthorizedAlert(for type: AVMediaType) { + if type == .audio { + AlertManager.shared.showAlert(Alert( + title: Text("No permission to record speech"), + message: Text("To record speech please grant permission to use Microphone."), + primaryButton: .default(Text("Open Settings")) { + DispatchQueue.main.async { + UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil) + } + }, + secondaryButton: .cancel() + )) + } else if type == .video { + AlertManager.shared.showAlert(Alert( + title: Text("No permission to record video"), + message: Text("To record video please grant permission to use Camera."), + primaryButton: .default(Text("Open Settings")) { + DispatchQueue.main.async { + UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil) + } + }, + secondaryButton: .cancel() + )) + } + } + + func setSpeakerEnabledAndConfigureSession( _ enabled: Bool, skipExternalDevice: Bool = false) { logger.debug("WebRTCClient: configuring session with speaker enabled \(enabled)") audioQueue.async { [weak self] in guard let self = self else { return } @@ -605,9 +891,23 @@ extension WebRTCClient { self.rtcAudioSession.unlockForConfiguration() } do { - try self.rtcAudioSession.setCategory(AVAudioSession.Category.playAndRecord.rawValue) - try self.rtcAudioSession.setMode(AVAudioSession.Mode.voiceChat.rawValue) - try self.rtcAudioSession.overrideOutputAudioPort(enabled ? .speaker : .none) + let hasExternalAudioDevice = self.rtcAudioSession.session.hasExternalAudioDevice() + if enabled { + try self.rtcAudioSession.setCategory(AVAudioSession.Category.playAndRecord.rawValue, with: [.defaultToSpeaker, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP]) + try self.rtcAudioSession.setMode(AVAudioSession.Mode.videoChat.rawValue) + if hasExternalAudioDevice && !skipExternalDevice, let preferred = self.rtcAudioSession.session.preferredInputDevice() { + try self.rtcAudioSession.setPreferredInput(preferred) + } else { + try self.rtcAudioSession.overrideOutputAudioPort(.speaker) + } + } else { + try self.rtcAudioSession.setCategory(AVAudioSession.Category.playAndRecord.rawValue, with: [.allowBluetooth, .allowAirPlay, .allowBluetoothA2DP]) + try self.rtcAudioSession.setMode(AVAudioSession.Mode.voiceChat.rawValue) + try self.rtcAudioSession.overrideOutputAudioPort(.none) + } + if hasExternalAudioDevice && !skipExternalDevice { + logger.debug("WebRTCClient: configuring session with external device available, skip configuring speaker") + } try self.rtcAudioSession.setActive(true) logger.debug("WebRTCClient: configuring session with speaker enabled \(enabled) success") } catch let error { @@ -636,25 +936,70 @@ extension WebRTCClient { } } - func setVideoEnabled(_ enabled: Bool) { - setTrackEnabled(RTCVideoTrack.self, enabled) + @MainActor + func setAudioEnabled(_ enabled: Bool) { + if activeCall != nil { + activeCall?.localAudioTrack = enabled ? createAudioTrack() : nil + activeCall?.connection.transceivers.first(where: { t in mediaSourceFromTransceiverMid(t.mid) == .mic })?.sender.track = activeCall?.localAudioTrack + } else if notConnectedCall != nil { + notConnectedCall?.audioTrack = enabled ? createAudioTrack() : nil + } + ChatModel.shared.activeCall?.localMediaSources.mic = enabled + } + + @MainActor + func setCameraEnabled(_ enabled: Bool) { + if let call = activeCall { + if enabled { + if call.localVideoTrack == nil { + let device = activeCall?.device ?? notConnectedCall?.device ?? .front + let (camera, track) = createVideoTrackAndStartCapture(device) + activeCall?.localCamera = camera + activeCall?.localVideoTrack = track + } + } else { + (call.localCamera as? RTCCameraVideoCapturer)?.stopCapture() + activeCall?.localCamera = nil + activeCall?.localVideoTrack = nil + } + call.connection.transceivers + .first(where: { t in mediaSourceFromTransceiverMid(t.mid) == .camera })? + .sender.track = activeCall?.localVideoTrack + ChatModel.shared.activeCall?.localMediaSources.camera = activeCall?.localVideoTrack != nil + } else if let call = notConnectedCall { + if enabled { + let device = activeCall?.device ?? notConnectedCall?.device ?? .front + notConnectedCall?.localCameraAndTrack = createVideoTrackAndStartCapture(device) + } else { + (call.localCameraAndTrack?.0 as? RTCCameraVideoCapturer)?.stopCapture() + notConnectedCall?.localCameraAndTrack = nil + } + ChatModel.shared.activeCall?.localMediaSources.camera = notConnectedCall?.localCameraAndTrack != nil + } } func flipCamera() { - switch activeCall.wrappedValue?.device { - case .front: activeCall.wrappedValue?.device = .back - case .back: activeCall.wrappedValue?.device = .front - default: () - } - if let call = activeCall.wrappedValue { - startCaptureLocalVideo(call) + let device = activeCall?.device ?? notConnectedCall?.device + if activeCall != nil { + activeCall?.device = device == .front ? .back : .front + } else { + notConnectedCall?.device = device == .front ? .back : .front } + startCaptureLocalVideo( + activeCall?.device ?? notConnectedCall?.device, + (activeCall?.localCamera ?? notConnectedCall?.localCameraAndTrack?.0) as? RTCCameraVideoCapturer + ) + } +} + +extension AVAudioSession { + func hasExternalAudioDevice() -> Bool { + availableInputs?.allSatisfy({ $0.portType == .builtInMic }) != true } - private func setTrackEnabled(_ type: T.Type, _ enabled: Bool) { - activeCall.wrappedValue?.connection.transceivers - .compactMap { $0.sender.track as? T } - .forEach { $0.isEnabled = enabled } + func preferredInputDevice() -> AVAudioSessionPortDescription? { +// logger.debug("Preferred input device: \(String(describing: self.availableInputs?.filter({ $0.portType != .builtInMic })))") + return availableInputs?.filter({ $0.portType != .builtInMic }).last } } diff --git a/apps/ios/Shared/Views/Chat/ChatInfoToolbar.swift b/apps/ios/Shared/Views/Chat/ChatInfoToolbar.swift index d0f4b6e55a..62a41c504a 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoToolbar.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoToolbar.swift @@ -9,10 +9,9 @@ import SwiftUI import SimpleXChat -let chatImageColorLight = Color(red: 0.9, green: 0.9, blue: 0.9) -let chatImageColorDark = Color(red: 0.2, green: 0.2, blue: 0.2) struct ChatInfoToolbar: View { @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme @ObservedObject var chat: Chat var imageSize: CGFloat = 32 @@ -25,30 +24,30 @@ struct ChatInfoToolbar: View { } ChatInfoImage( chat: chat, - color: colorScheme == .dark - ? chatImageColorDark - : chatImageColorLight + size: imageSize, + color: Color(uiColor: .tertiaryLabel) ) - .frame(width: imageSize, height: imageSize) .padding(.trailing, 4) - VStack { - let t = Text(cInfo.displayName).font(.headline) - (cInfo.contact?.verified == true ? contactVerifiedShield + t : t) - .lineLimit(1) - if cInfo.fullName != "" && cInfo.displayName != cInfo.fullName { - Text(cInfo.fullName).font(.subheadline) - .lineLimit(1) + let t = Text(cInfo.displayName).font(.headline) + (cInfo.contact?.verified == true ? contactVerifiedShield + t : t) + .lineLimit(1) + .if (cInfo.fullName != "" && cInfo.displayName != cInfo.fullName) { v in + VStack(spacing: 0) { + v + Text(cInfo.fullName).font(.subheadline) + .lineLimit(1) + .padding(.top, -2) + } } - } } - .foregroundColor(.primary) + .foregroundColor(theme.colors.onBackground) .frame(width: 220) } private var contactVerifiedShield: Text { - (Text(Image(systemName: "checkmark.shield")) + Text(" ")) + (Text(Image(systemName: "checkmark.shield")) + textSpace) .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .baselineOffset(1) .kerning(-2) } @@ -57,5 +56,6 @@ struct ChatInfoToolbar: View { struct ChatInfoToolbar_Previews: PreviewProvider { static var previews: some View { ChatInfoToolbar(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])) + .environmentObject(CurrentColors.toAppTheme()) } } diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift index 86532605db..8194c8fe6f 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift @@ -7,7 +7,7 @@ // import SwiftUI -import SimpleXChat +@preconcurrency import SimpleXChat func infoRow(_ title: LocalizedStringKey, _ value: String) -> some View { HStack { @@ -36,20 +36,20 @@ func localizedInfoRow(_ title: LocalizedStringKey, _ value: LocalizedStringKey) } } -@ViewBuilder func smpServers(_ title: LocalizedStringKey, _ servers: [String]) -> some View { +@ViewBuilder func smpServers(_ title: LocalizedStringKey, _ servers: [String], _ secondaryColor: Color) -> some View { if servers.count > 0 { HStack { Text(title).frame(width: 120, alignment: .leading) Button(serverHost(servers[0])) { UIPasteboard.general.string = servers.joined(separator: ";") } - .foregroundColor(.secondary) + .foregroundColor(secondaryColor) .lineLimit(1) } } } -private func serverHost(_ s: String) -> String { +func serverHost(_ s: String) -> String { if let i = s.range(of: "@")?.lowerBound { return String(s[i...].dropFirst()) } else { @@ -90,29 +90,37 @@ enum SendReceipts: Identifiable, Hashable { struct ChatInfoView: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme @Environment(\.dismiss) var dismiss: DismissAction + @ObservedObject var networkModel = NetworkModel.shared @ObservedObject var chat: Chat @State var contact: Contact - @Binding var connectionStats: ConnectionStats? - @Binding var customUserProfile: Profile? @State var localAlias: String - @Binding var connectionCode: String? + @State var featuresAllowed: ContactFeaturesAllowed + @State var currentFeaturesAllowed: ContactFeaturesAllowed + var onSearch: () -> Void + @State private var connectionStats: ConnectionStats? = nil + @State private var customUserProfile: Profile? = nil + @State private var connectionCode: String? = nil @FocusState private var aliasTextFieldFocused: Bool @State private var alert: ChatInfoViewAlert? = nil - @State private var showDeleteContactActionSheet = false + @State private var actionSheet: SomeActionSheet? = nil + @State private var sheet: SomeSheet? = nil + @State private var showConnectContactViaAddressDialog = false @State private var sendReceipts = SendReceipts.userDefault(true) @State private var sendReceiptsUserDefault = true + @State private var progressIndicator = false @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false - @AppStorage(GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED, store: groupDefaults) private var pqExperimentalEnabled = false - + enum ChatInfoViewAlert: Identifiable { case clearChatAlert case networkStatusAlert case switchAddressAlert case abortSwitchAddressAlert case syncConnectionForceAlert - case allowContactPQEncryptionAlert - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case queueInfo(info: String) + case someAlert(alert: SomeAlert) + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -121,126 +129,172 @@ struct ChatInfoView: View { case .switchAddressAlert: return "switchAddressAlert" case .abortSwitchAddressAlert: return "abortSwitchAddressAlert" case .syncConnectionForceAlert: return "syncConnectionForceAlert" - case .allowContactPQEncryptionAlert: return "allowContactPQEncryptionAlert" + case let .queueInfo(info): return "queueInfo \(info)" + case let .someAlert(alert): return "chatInfoSomeAlert \(alert.id)" case let .error(title, _): return "error \(title)" } } } - + var body: some View { NavigationView { - List { - contactInfoHeader() - .listRowBackground(Color.clear) - .contentShape(Rectangle()) - .onTapGesture { - aliasTextFieldFocused = false - } - - Group { + ZStack { + List { + contactInfoHeader() + .listRowBackground(Color.clear) + .contentShape(Rectangle()) + .onTapGesture { + aliasTextFieldFocused = false + } + localAliasTextEdit() - } - .listRowBackground(Color.clear) - .listRowSeparator(.hidden) - - if let customUserProfile = customUserProfile { - Section("Incognito") { - HStack { - Text("Your random profile") - Spacer() - Text(customUserProfile.chatViewName) - .foregroundStyle(.indigo) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .padding(.bottom, 18) + + GeometryReader { g in + HStack(alignment: .center, spacing: 8) { + let buttonWidth = g.size.width / 4 + searchButton(width: buttonWidth) + AudioCallButton(chat: chat, contact: contact, connectionStats: $connectionStats, width: buttonWidth) { alert = .someAlert(alert: $0) } + VideoButton(chat: chat, contact: contact, connectionStats: $connectionStats, width: buttonWidth) { alert = .someAlert(alert: $0) } + if let nextNtfMode = chat.chatInfo.nextNtfMode { + muteButton(width: buttonWidth, nextNtfMode: nextNtfMode) + } } } - } - - Section { - if let code = connectionCode { verifyCodeButton(code) } - contactPreferencesButton() - sendReceiptsOption() - if let connStats = connectionStats, - connStats.ratchetSyncAllowed { - synchronizeConnectionButton() + .padding(.trailing) + .frame(maxWidth: .infinity) + .frame(height: infoViewActionButtonHeight) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 8)) + + if let customUserProfile = customUserProfile { + Section(header: Text("Incognito").foregroundColor(theme.colors.secondary)) { + HStack { + Text("Your random profile") + Spacer() + Text(customUserProfile.chatViewName) + .foregroundStyle(.indigo) + } + } } -// } else if developerTools { -// synchronizeConnectionButtonForce() -// } - } - .disabled(!contact.ready || !contact.active) - - if pqExperimentalEnabled, - let conn = contact.activeConn { + Section { - infoRow(Text(String("E2E encryption")), conn.connPQEnabled ? "Quantum resistant" : "Standard") - if !conn.pqEncryption { - allowPQButton() + if let code = connectionCode { verifyCodeButton(code) } + contactPreferencesButton() + sendReceiptsOption() + if let connStats = connectionStats, + connStats.ratchetSyncAllowed { + synchronizeConnectionButton() } - } header: { - Text(String("Quantum resistant E2E encryption")) - } footer: { - if !conn.pqEncryption { - Text(String("After allowing quantum resistant encryption, it will be enabled after several messages if your contact also allows it.")) - } - } - } + // } else if developerTools { + // synchronizeConnectionButtonForce() + // } - if let contactLink = contact.contactLink { - Section { - SimpleXLinkQRCode(uri: contactLink) - Button { - showShareSheet(items: [simplexChatLink(contactLink)]) + NavigationLink { + ChatWallpaperEditorSheet(chat: chat) } label: { - Label("Share address", systemImage: "square.and.arrow.up") + Label("Chat theme", systemImage: "photo") } - } header: { - Text("Address") - } footer: { - Text("You can share this address with your contacts to let them connect with **\(contact.displayName)**.") + // } else if developerTools { + // synchronizeConnectionButtonForce() + // } } - } - - if contact.ready && contact.active { - Section("Servers") { - networkStatusRow() - .onTapGesture { - alert = .networkStatusAlert + .disabled(!contact.ready || !contact.active) + + Section { + ChatTTLOption(chat: chat, progressIndicator: $progressIndicator) + } footer: { + Text("Delete chat messages from your device.") + } + + if let conn = contact.activeConn { + Section { + infoRow(Text(String("E2E encryption")), conn.connPQEnabled ? "Quantum resistant" : "Standard") + } + } + + if let contactLink = contact.contactLink { + Section { + SimpleXLinkQRCode(uri: contactLink) + Button { + showShareSheet(items: [simplexChatLink(contactLink)]) + } label: { + Label("Share address", systemImage: "square.and.arrow.up") } - if let connStats = connectionStats { - Button("Change receiving address") { - alert = .switchAddressAlert - } - .disabled( - connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil } - || connStats.ratchetSyncSendProhibited - ) - if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) { - Button("Abort changing address") { - alert = .abortSwitchAddressAlert + } header: { + Text("Address") + .foregroundColor(theme.colors.secondary) + } footer: { + Text("You can share this address with your contacts to let them connect with **\(contact.displayName)**.") + .foregroundColor(theme.colors.secondary) + } + } + + if contact.ready && contact.active { + Section(header: Text("Servers").foregroundColor(theme.colors.secondary)) { + networkStatusRow() + .onTapGesture { + alert = .networkStatusAlert + } + if let connStats = connectionStats { + Button("Change receiving address") { + alert = .switchAddressAlert } .disabled( - connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch } + connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil } || connStats.ratchetSyncSendProhibited ) + if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) { + Button("Abort changing address") { + alert = .abortSwitchAddressAlert + } + .disabled( + connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch } + || connStats.ratchetSyncSendProhibited + ) + } + smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }, theme.colors.secondary) + smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }, theme.colors.secondary) + } + } + } + + Section { + clearChatButton() + deleteContactButton() + } + + if developerTools { + Section(header: Text("For console").foregroundColor(theme.colors.secondary)) { + infoRow("Local name", chat.chatInfo.localDisplayName) + infoRow("Database ID", "\(chat.chatInfo.apiId)") + Button ("Debug delivery") { + Task { + do { + let info = queueInfoText(try await apiContactQueueInfo(chat.chatInfo.apiId)) + await MainActor.run { alert = .queueInfo(info: info) } + } catch let e { + logger.error("apiContactQueueInfo error: \(responseError(e))") + let a = getErrorAlert(e, "Error") + await MainActor.run { alert = .error(title: a.title, error: a.message) } + } + } } - smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }) - smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }) } } } - - Section { - clearChatButton() - deleteContactButton() - } - - if developerTools { - Section(header: Text("For console")) { - infoRow("Local name", chat.chatInfo.localDisplayName) - infoRow("Database ID", "\(chat.chatInfo.apiId)") - } + .modifier(ThemedBackground(grouped: true)) + .navigationBarHidden(true) + .disabled(progressIndicator) + .opacity(progressIndicator ? 0.6 : 1) + + if progressIndicator { + ProgressView().scaleEffect(2) } } - .navigationBarHidden(true) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) .onAppear { @@ -248,6 +302,23 @@ struct ChatInfoView: View { sendReceiptsUserDefault = currentUser.sendRcptsContacts } sendReceipts = SendReceipts.fromBool(contact.chatSettings.sendRcpts, userDefault: sendReceiptsUserDefault) + + Task { + do { + let (stats, profile) = try await apiContactInfo(chat.chatInfo.apiId) + let (ct, code) = try await apiGetContactCode(chat.chatInfo.apiId) + await MainActor.run { + connectionStats = stats + customUserProfile = profile + connectionCode = code + if contact.activeConn?.connectionCode != ct.activeConn?.connectionCode { + chat.chatInfo = .direct(contact: ct) + } + } + } catch let error { + logger.error("apiContactInfo or apiGetContactCode error: \(responseError(error))") + } + } } .alert(item: $alert) { alertItem in switch(alertItem) { @@ -255,46 +326,52 @@ struct ChatInfoView: View { case .networkStatusAlert: return networkStatusAlert() case .switchAddressAlert: return switchAddressAlert(switchContactAddress) case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchContactAddress) - case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncContactConnection(force: true) }) - case .allowContactPQEncryptionAlert: return allowContactPQEncryptionAlert() + case .syncConnectionForceAlert: + return syncConnectionForceAlert({ + Task { + if let stats = await syncContactConnection(contact, force: true, showAlert: { alert = .someAlert(alert: $0) }) { + connectionStats = stats + dismiss() + } + } + }) + case let .queueInfo(info): return queueInfoAlert(info) + case let .someAlert(a): return a.alert case let .error(title, error): return mkAlert(title: title, message: error) } } - .actionSheet(isPresented: $showDeleteContactActionSheet) { - if contact.ready && contact.active { - return ActionSheet( - title: Text("Delete contact?\nThis cannot be undone!"), - buttons: [ - .destructive(Text("Delete and notify contact")) { deleteContact(notify: true) }, - .destructive(Text("Delete")) { deleteContact(notify: false) }, - .cancel() - ] - ) + .actionSheet(item: $actionSheet) { $0.actionSheet } + .sheet(item: $sheet) { + if #available(iOS 16.0, *) { + $0.content + .presentationDetents([.fraction($0.fraction)]) } else { - return ActionSheet( - title: Text("Delete contact?\nThis cannot be undone!"), - buttons: [ - .destructive(Text("Delete")) { deleteContact() }, - .cancel() - ] + $0.content + } + } + .onDisappear { + if currentFeaturesAllowed != featuresAllowed { + showAlert( + title: NSLocalizedString("Save preferences?", comment: "alert title"), + buttonTitle: NSLocalizedString("Save and notify contact", comment: "alert button"), + buttonAction: { savePreferences() }, + cancelButton: true ) } } } - + private func contactInfoHeader() -> some View { - VStack { + VStack(spacing: 8) { let cInfo = chat.chatInfo - ChatInfoImage(chat: chat, color: Color(uiColor: .tertiarySystemFill)) - .frame(width: 192, height: 192) - .padding(.top, 12) - .padding() + ChatInfoImage(chat: chat, size: 192, color: Color(uiColor: .tertiarySystemFill)) + .padding(.vertical, 12) if contact.verified { ( Text(Image(systemName: "checkmark.shield")) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .font(.title2) - + Text(" ") + + textSpace + Text(contact.profile.displayName) .font(.largeTitle) ) @@ -317,7 +394,7 @@ struct ChatInfoView: View { } .frame(maxWidth: .infinity, alignment: .center) } - + private func localAliasTextEdit() -> some View { TextField("Set contact name…", text: $localAlias) .disableAutocorrection(true) @@ -332,9 +409,9 @@ struct ChatInfoView: View { setContactAlias() } .multilineTextAlignment(.center) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } - + private func setContactAlias() { Task { do { @@ -349,6 +426,25 @@ struct ChatInfoView: View { } } + private func searchButton(width: CGFloat) -> some View { + InfoViewButton(image: "magnifyingglass", title: "search", width: width) { + dismiss() + onSearch() + } + .disabled(!contact.ready || chat.chatItems.isEmpty) + } + + private func muteButton(width: CGFloat, nextNtfMode: MsgFilter) -> some View { + return InfoViewButton( + image: nextNtfMode.iconFilled, + title: "\(nextNtfMode.text(mentions: false))", + width: width + ) { + toggleNotifications(chat, enableNtfs: nextNtfMode) + } + .disabled(!contact.ready || !contact.active) + } + private func verifyCodeButton(_ code: String) -> some View { NavigationLink { VerifyCodeView( @@ -370,6 +466,7 @@ struct ChatInfoView: View { ) .navigationBarTitleDisplayMode(.inline) .navigationTitle("Security code") + .modifier(ThemedBackground(grouped: true)) } label: { Label( contact.verified ? "View security code" : "Verify security code", @@ -377,21 +474,23 @@ struct ChatInfoView: View { ) } } - + private func contactPreferencesButton() -> some View { NavigationLink { ContactPreferencesView( contact: $contact, - featuresAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences), - currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences) + featuresAllowed: $featuresAllowed, + currentFeaturesAllowed: $currentFeaturesAllowed, + savePreferences: savePreferences ) .navigationBarTitle("Contact preferences") + .modifier(ThemedBackground(grouped: true)) .navigationBarTitleDisplayMode(.large) } label: { Label("Contact preferences", systemImage: "switch.2") } } - + private func sendReceiptsOption() -> some View { Picker(selection: $sendReceipts) { ForEach([.yes, .no, .userDefault(sendReceiptsUserDefault)]) { (opt: SendReceipts) in @@ -405,7 +504,7 @@ struct ChatInfoView: View { setSendReceipts() } } - + private func setSendReceipts() { var chatSettings = chat.chatInfo.chatSettings ?? ChatSettings.defaults chatSettings.sendRcpts = sendReceipts.bool() @@ -414,13 +513,18 @@ struct ChatInfoView: View { private func synchronizeConnectionButton() -> some View { Button { - syncContactConnection(force: false) + Task { + if let stats = await syncContactConnection(contact, force: false, showAlert: { alert = .someAlert(alert: $0) }) { + connectionStats = stats + dismiss() + } + } } label: { Label("Fix connection", systemImage: "exclamationmark.arrow.triangle.2.circlepath") .foregroundColor(.orange) } } - + private func synchronizeConnectionButtonForce() -> some View { Button { alert = .syncConnectionForceAlert @@ -429,45 +533,43 @@ struct ChatInfoView: View { .foregroundColor(.red) } } - - private func allowPQButton() -> some View { - Button { - alert = .allowContactPQEncryptionAlert - } label: { - Label(String("Allow PQ encryption"), systemImage: "exclamationmark.triangle") - .foregroundColor(.orange) - } - } - + private func networkStatusRow() -> some View { HStack { Text("Network status") Image(systemName: "info.circle") - .foregroundColor(.accentColor) + .foregroundColor(theme.colors.primary) .font(.system(size: 14)) Spacer() - Text(chatModel.contactNetworkStatus(contact).statusString) - .foregroundColor(.secondary) + Text(networkModel.contactNetworkStatus(contact).statusString) + .foregroundColor(theme.colors.secondary) serverImage() } } - + private func serverImage() -> some View { - let status = chatModel.contactNetworkStatus(contact) + let status = networkModel.contactNetworkStatus(contact) return Image(systemName: status.imageName) - .foregroundColor(status == .connected ? .green : .secondary) + .foregroundColor(status == .connected ? .green : theme.colors.secondary) .font(.system(size: 12)) } - + private func deleteContactButton() -> some View { Button(role: .destructive) { - showDeleteContactActionSheet = true + deleteContactDialog( + chat, + contact, + dismissToChatList: true, + showAlert: { alert = .someAlert(alert: $0) }, + showActionSheet: { actionSheet = $0 }, + showSheetContent: { sheet = $0 } + ) } label: { - Label("Delete contact", systemImage: "trash") + Label("Delete contact", systemImage: "person.badge.minus") .foregroundColor(Color.red) } } - + private func clearChatButton() -> some View { Button() { alert = .clearChatAlert @@ -476,26 +578,7 @@ struct ChatInfoView: View { .foregroundColor(Color.orange) } } - - private func deleteContact(notify: Bool? = nil) { - Task { - do { - try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, notify: notify) - await MainActor.run { - dismiss() - chatModel.chatId = nil - chatModel.removeChat(chat.chatInfo.id) - } - } catch let error { - logger.error("deleteContactAlert apiDeleteChat error: \(responseError(error))") - let a = getErrorAlert(error, "Error deleting contact") - await MainActor.run { - alert = .error(title: a.title, error: a.message) - } - } - } - } - + private func clearChatAlert() -> Alert { Alert( title: Text("Clear conversation?"), @@ -509,14 +592,14 @@ struct ChatInfoView: View { secondaryButton: .cancel() ) } - + private func networkStatusAlert() -> Alert { Alert( title: Text("Network status"), - message: Text(chatModel.contactNetworkStatus(contact).statusExplanation) + message: Text(networkModel.contactNetworkStatus(contact).statusExplanation) ) } - + private func switchContactAddress() { Task { do { @@ -535,7 +618,7 @@ struct ChatInfoView: View { } } } - + private func abortSwitchContactAddress() { Task { do { @@ -553,55 +636,439 @@ struct ChatInfoView: View { } } } - - private func syncContactConnection(force: Bool) { + + private func savePreferences() { Task { do { - let stats = try apiSyncContactRatchet(contact.apiId, force) - connectionStats = stats - await MainActor.run { - chatModel.updateContactConnectionStats(contact, stats) - dismiss() - } - } catch let error { - logger.error("syncContactConnection apiSyncContactRatchet error: \(responseError(error))") - let a = getErrorAlert(error, "Error synchronizing connection") - await MainActor.run { - alert = .error(title: a.title, error: a.message) + let prefs = contactFeaturesAllowedToPrefs(featuresAllowed) + if let toContact = try await apiSetContactPrefs(contactId: contact.contactId, preferences: prefs) { + await MainActor.run { + contact = toContact + chatModel.updateContact(toContact) + currentFeaturesAllowed = featuresAllowed + } } + } catch { + logger.error("ContactPreferencesView apiSetContactPrefs error: \(responseError(error))") } } } +} - private func allowContactPQEncryption() { - Task { - do { - let ct = try await apiSetContactPQ(contact.apiId, true) - contact = ct - await MainActor.run { - chatModel.updateContact(contact) - dismiss() - } - } catch let error { - logger.error("allowContactPQEncryption apiSetContactPQ error: \(responseError(error))") - let a = getErrorAlert(error, "Error allowing contact PQ encryption") - await MainActor.run { - alert = .error(title: a.title, error: a.message) +struct ChatTTLOption: View { + @ObservedObject var chat: Chat + @Binding var progressIndicator: Bool + @State private var currentChatItemTTL: ChatTTL = ChatTTL.userDefault(.seconds(0)) + @State private var chatItemTTL: ChatTTL = ChatTTL.chat(.seconds(0)) + + var body: some View { + Picker("Delete messages after", selection: $chatItemTTL) { + ForEach(ChatItemTTL.values) { ttl in + Text(ttl.deleteAfterText).tag(ChatTTL.chat(ttl)) + } + let defaultTTL = ChatTTL.userDefault(ChatModel.shared.chatItemTTL) + Text(defaultTTL.text).tag(defaultTTL) + + if case .chat(let ttl) = chatItemTTL, case .seconds = ttl { + Text(ttl.deleteAfterText).tag(chatItemTTL) + } + } + .disabled(progressIndicator) + .frame(height: 36) + .onChange(of: chatItemTTL) { ttl in + if ttl == currentChatItemTTL { return } + setChatTTL( + ttl, + hasPreviousTTL: !currentChatItemTTL.neverExpires, + onCancel: { chatItemTTL = currentChatItemTTL } + ) { + progressIndicator = true + Task { + let m = ChatModel.shared + do { + try await setChatTTL(chatType: chat.chatInfo.chatType, id: chat.chatInfo.apiId, ttl) + await loadChat(chat: chat, clearItems: true) + await MainActor.run { + progressIndicator = false + currentChatItemTTL = chatItemTTL + if ItemsModel.shared.reversedChatItems.isEmpty && m.chatId == chat.id, + let chat = m.getChat(chat.id) { + chat.chatItems = [] + m.replaceChat(chat.id, chat) + } + } + } + catch let error { + logger.error("setChatTTL error \(responseError(error))") + await loadChat(chat: chat, clearItems: true) + await MainActor.run { + chatItemTTL = currentChatItemTTL + progressIndicator = false + } + } } } } + .onAppear { + let sm = ChatModel.shared + let ttl = chat.chatInfo.ttl(sm.chatItemTTL) + chatItemTTL = ttl + currentChatItemTTL = ttl + } } +} - func allowContactPQEncryptionAlert() -> Alert { - Alert( - title: Text(String("Allow quantum resistant encryption?")), - message: Text(String("This is an experimental feature, it is not recommended to enable it for important chats.")), - primaryButton: .destructive(Text(String("Allow")), action: allowContactPQEncryption), - secondaryButton: .cancel() +func syncContactConnection(_ contact: Contact, force: Bool, showAlert: (SomeAlert) -> Void) async -> ConnectionStats? { + do { + let stats = try apiSyncContactRatchet(contact.apiId, force) + await MainActor.run { + ChatModel.shared.updateContactConnectionStats(contact, stats) + } + return stats + } catch let error { + logger.error("syncContactConnection apiSyncContactRatchet error: \(responseError(error))") + let a = getErrorAlert(error, "Error synchronizing connection") + await MainActor.run { + showAlert( + SomeAlert( + alert: mkAlert(title: a.title, message: a.message), + id: "syncContactConnection error" + ) + ) + } + return nil + } +} + +struct AudioCallButton: View { + var chat: Chat + var contact: Contact + @Binding var connectionStats: ConnectionStats? + var width: CGFloat + var showAlert: (SomeAlert) -> Void + + var body: some View { + CallButton( + chat: chat, + contact: contact, + connectionStats: $connectionStats, + image: "phone.fill", + title: "call", + mediaType: .audio, + width: width, + showAlert: showAlert ) } } +struct VideoButton: View { + var chat: Chat + var contact: Contact + @Binding var connectionStats: ConnectionStats? + var width: CGFloat + var showAlert: (SomeAlert) -> Void + + var body: some View { + CallButton( + chat: chat, + contact: contact, + connectionStats: $connectionStats, + image: "video.fill", + title: "video", + mediaType: .video, + width: width, + showAlert: showAlert + ) + } +} + +private struct CallButton: View { + var chat: Chat + var contact: Contact + @Binding var connectionStats: ConnectionStats? + var image: String + var title: LocalizedStringKey + var mediaType: CallMediaType + var width: CGFloat + var showAlert: (SomeAlert) -> Void + + var body: some View { + let canCall = contact.ready && contact.active && chat.chatInfo.featureEnabled(.calls) && ChatModel.shared.activeCall == nil + + InfoViewButton(image: image, title: title, disabledLook: !canCall, width: width) { + if canCall { + if let connStats = connectionStats { + if connStats.ratchetSyncState == .ok { + if CallController.useCallKit() { + CallController.shared.startCall(contact, mediaType) + } else { + // When CallKit is not used, colorscheme will be changed and it will be visible if not hiding sheets first + dismissAllSheets(animated: true) { + CallController.shared.startCall(contact, mediaType) + } + } + } else if connStats.ratchetSyncAllowed { + showAlert(SomeAlert( + alert: Alert( + title: Text("Fix connection?"), + message: Text("Connection requires encryption renegotiation."), + primaryButton: .default(Text("Fix")) { + Task { + if let stats = await syncContactConnection(contact, force: false, showAlert: showAlert) { + connectionStats = stats + } + } + }, + secondaryButton: .cancel() + ), + id: "can't call contact, fix connection" + )) + } else { + showAlert(SomeAlert( + alert: mkAlert( + title: "Can't call contact", + message: "Encryption renegotiation in progress." + ), + id: "can't call contact, encryption renegotiation in progress" + )) + } + } + } else if contact.nextSendGrpInv { + showAlert(SomeAlert( + alert: mkAlert( + title: "Can't call contact", + message: "Send message to enable calls." + ), + id: "can't call contact, send message" + )) + } else if !contact.active { + showAlert(SomeAlert( + alert: mkAlert( + title: "Can't call contact", + message: "Contact is deleted." + ), + id: "can't call contact, contact deleted" + )) + } else if !contact.ready { + showAlert(SomeAlert( + alert: mkAlert( + title: "Can't call contact", + message: "Connecting to contact, please wait or check later!" + ), + id: "can't call contact, contact not ready" + )) + } else if !chat.chatInfo.featureEnabled(.calls) { + switch chat.chatInfo.showEnableCallsAlert { + case .userEnable: + showAlert(SomeAlert( + alert: Alert( + title: Text("Allow calls?"), + message: Text("You need to allow your contact to call to be able to call them."), + primaryButton: .default(Text("Allow")) { + allowFeatureToContact(contact, .calls) + }, + secondaryButton: .cancel() + ), + id: "allow calls" + )) + case .askContact: + showAlert(SomeAlert( + alert: mkAlert( + title: "Calls prohibited!", + message: "Please ask your contact to enable calls." + ), + id: "calls prohibited, ask contact" + )) + case .other: + showAlert(SomeAlert( + alert: mkAlert( + title: "Calls prohibited!", + message: "Please check yours and your contact preferences." + ) + , id: "calls prohibited, other" + )) + } + } else { + showAlert(SomeAlert( + alert: mkAlert(title: "Can't call contact"), + id: "can't call contact" + )) + } + } + .disabled(ChatModel.shared.activeCall != nil) + } +} + +let infoViewActionButtonHeight: CGFloat = 60 + +struct InfoViewButton: View { + var image: String + var title: LocalizedStringKey + var disabledLook: Bool = false + var width: CGFloat + var action: () -> Void + + var body: some View { + VStack(spacing: 4) { + Image(systemName: image) + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + Text(title) + .font(.caption) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .foregroundColor(.accentColor) + .background(Color(.secondarySystemGroupedBackground)) + .cornerRadius(10.0) + .frame(width: width, height: infoViewActionButtonHeight) + .disabled(disabledLook) + .onTapGesture(perform: action) + } +} + +struct ChatWallpaperEditorSheet: View { + @Environment(\.dismiss) var dismiss + @EnvironmentObject var theme: AppTheme + @State private var globalThemeUsed: Bool = false + @State var chat: Chat + @State private var themes: ThemeModeOverrides + + init(chat: Chat) { + self.chat = chat + self.themes = if case let ChatInfo.direct(contact) = chat.chatInfo, let uiThemes = contact.uiThemes { + uiThemes + } else if case let ChatInfo.group(groupInfo) = chat.chatInfo, let uiThemes = groupInfo.uiThemes { + uiThemes + } else { + ThemeModeOverrides() + } + } + + var body: some View { + let preferred = themes.preferredMode(!theme.colors.isLight) + let initialTheme = preferred ?? ThemeManager.defaultActiveTheme(ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + ChatWallpaperEditor( + initialTheme: initialTheme, + themeModeOverride: initialTheme, + applyToMode: themes.light == themes.dark ? nil : initialTheme.mode, + globalThemeUsed: $globalThemeUsed, + save: { applyToMode, newTheme in + await save(applyToMode, newTheme, $chat) + } + ) + .navigationTitle("Chat theme") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.inline) + .onAppear { + globalThemeUsed = preferred == nil + } + .onChange(of: theme.base.mode) { _ in + globalThemeUsed = themesFromChat(chat).preferredMode(!theme.colors.isLight) == nil + } + .onChange(of: ChatModel.shared.chatId) { _ in + dismiss() + } + } + + private func themesFromChat(_ chat: Chat) -> ThemeModeOverrides { + if case let ChatInfo.direct(contact) = chat.chatInfo, let uiThemes = contact.uiThemes { + uiThemes + } else if case let ChatInfo.group(groupInfo) = chat.chatInfo, let uiThemes = groupInfo.uiThemes { + uiThemes + } else { + ThemeModeOverrides() + } + } + + private static var updateBackendTask: Task = Task {} + private func save( + _ applyToMode: DefaultThemeMode?, + _ newTheme: ThemeModeOverride?, + _ chat: Binding + ) async { + let unchangedThemes: ThemeModeOverrides = themesFromChat(chat.wrappedValue) + var wallpaperFiles = Set([unchangedThemes.light?.wallpaper?.imageFile, unchangedThemes.dark?.wallpaper?.imageFile]) + var changedThemes: ThemeModeOverrides? = unchangedThemes + let light: ThemeModeOverride? = if let newTheme { + ThemeModeOverride(mode: DefaultThemeMode.light, colors: newTheme.colors, wallpaper: newTheme.wallpaper?.withFilledWallpaperPath()) + } else { + nil + } + let dark: ThemeModeOverride? = if let newTheme { + ThemeModeOverride(mode: DefaultThemeMode.dark, colors: newTheme.colors, wallpaper: newTheme.wallpaper?.withFilledWallpaperPath()) + } else { + nil + } + + if let applyToMode { + switch applyToMode { + case DefaultThemeMode.light: + changedThemes?.light = light + case DefaultThemeMode.dark: + changedThemes?.dark = dark + } + } else { + changedThemes?.light = light + changedThemes?.dark = dark + } + if changedThemes?.light != nil || changedThemes?.dark != nil { + let light = changedThemes?.light + let dark = changedThemes?.dark + let currentMode = CurrentColors.base.mode + // same image file for both modes, copy image to make them as different files + if var light, var dark, let lightWallpaper = light.wallpaper, let darkWallpaper = dark.wallpaper, let lightImageFile = lightWallpaper.imageFile, let darkImageFile = darkWallpaper.imageFile, lightWallpaper.imageFile == darkWallpaper.imageFile { + let imageFile = if currentMode == DefaultThemeMode.light { + darkImageFile + } else { + lightImageFile + } + let filePath = saveWallpaperFile(url: getWallpaperFilePath(imageFile)) + if currentMode == DefaultThemeMode.light { + dark.wallpaper?.imageFile = filePath + changedThemes = ThemeModeOverrides(light: changedThemes?.light, dark: dark) + } else { + light.wallpaper?.imageFile = filePath + changedThemes = ThemeModeOverrides(light: light, dark: changedThemes?.dark) + } + } + } else { + changedThemes = nil + } + wallpaperFiles.remove(changedThemes?.light?.wallpaper?.imageFile) + wallpaperFiles.remove(changedThemes?.dark?.wallpaper?.imageFile) + wallpaperFiles.forEach(removeWallpaperFile) + + let changedThemesConstant = changedThemes + ChatWallpaperEditorSheet.updateBackendTask.cancel() + ChatWallpaperEditorSheet.updateBackendTask = Task { + do { + try await Task.sleep(nanoseconds: 300_000000) + if await apiSetChatUIThemes(chatId: chat.id, themes: changedThemesConstant) { + if case var ChatInfo.direct(contact) = chat.wrappedValue.chatInfo { + contact.uiThemes = changedThemesConstant + await MainActor.run { + ChatModel.shared.updateChatInfo(ChatInfo.direct(contact: contact)) + chat.wrappedValue = Chat.init(chatInfo: ChatInfo.direct(contact: contact)) + themes = themesFromChat(chat.wrappedValue) + } + } else if case var ChatInfo.group(groupInfo) = chat.wrappedValue.chatInfo { + groupInfo.uiThemes = changedThemesConstant + + await MainActor.run { + ChatModel.shared.updateChatInfo(ChatInfo.group(groupInfo: groupInfo)) + chat.wrappedValue = Chat.init(chatInfo: ChatInfo.group(groupInfo: groupInfo)) + themes = themesFromChat(chat.wrappedValue) + } + } + } + } catch { + // canceled task + } + } + } +} + func switchAddressAlert(_ switchAddress: @escaping () -> Void) -> Alert { Alert( title: Text("Change receiving address?"), @@ -629,15 +1096,267 @@ func syncConnectionForceAlert(_ syncConnectionForce: @escaping () -> Void) -> Al ) } +func queueInfoText(_ info: (RcvMsgInfo?, ServerQueueInfo)) -> String { + let (rcvMsgInfo, qInfo) = info + var msgInfo: String + if let rcvMsgInfo { msgInfo = encodeJSON(rcvMsgInfo) } else { msgInfo = "none" } + return String.localizedStringWithFormat(NSLocalizedString("server queue info: %@\n\nlast received msg: %@", comment: "queue info"), encodeJSON(qInfo), msgInfo) +} + +func queueInfoAlert(_ info: String) -> Alert { + Alert( + title: Text("Message queue info"), + message: Text(info), + primaryButton: .default(Text("Ok")), + secondaryButton: .default(Text("Copy")) { UIPasteboard.general.string = info } + ) +} + +func deleteContactDialog( + _ chat: Chat, + _ contact: Contact, + dismissToChatList: Bool, + showAlert: @escaping (SomeAlert) -> Void, + showActionSheet: @escaping (SomeActionSheet) -> Void, + showSheetContent: @escaping (SomeSheet) -> Void +) { + if contact.sndReady && contact.active && !contact.chatDeleted { + deleteContactOrConversationDialog(chat, contact, dismissToChatList, showAlert, showActionSheet, showSheetContent) + } else if contact.sndReady && contact.active && contact.chatDeleted { + deleteContactWithoutConversation(chat, contact, dismissToChatList, showAlert, showActionSheet) + } else { // !(contact.sndReady && contact.active) + deleteNotReadyContact(chat, contact, dismissToChatList, showAlert, showActionSheet) + } +} + +func setChatTTL(_ ttl: ChatTTL, hasPreviousTTL: Bool, onCancel: @escaping () -> Void, onConfirm: @escaping () -> Void) { + let title = if ttl.neverExpires { + NSLocalizedString("Disable automatic message deletion?", comment: "alert title") + } else if ttl.usingDefault || hasPreviousTTL { + NSLocalizedString("Change automatic message deletion?", comment: "alert title") + } else { + NSLocalizedString("Enable automatic message deletion?", comment: "alert title") + } + + let message = if ttl.neverExpires { + NSLocalizedString("Messages in this chat will never be deleted.", comment: "alert message") + } else { + NSLocalizedString("This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted.", comment: "alert message") + } + + showAlert(title, message: message) { + [ + UIAlertAction( + title: ttl.neverExpires ? NSLocalizedString("Disable delete messages", comment: "alert button") : NSLocalizedString("Delete messages", comment: "alert button"), + style: .destructive, + handler: { _ in onConfirm() } + ), + UIAlertAction(title: NSLocalizedString("Cancel", comment: "alert button"), style: .cancel, handler: { _ in onCancel() }) + ] + } +} + +private func deleteContactOrConversationDialog( + _ chat: Chat, + _ contact: Contact, + _ dismissToChatList: Bool, + _ showAlert: @escaping (SomeAlert) -> Void, + _ showActionSheet: @escaping (SomeActionSheet) -> Void, + _ showSheetContent: @escaping (SomeSheet) -> Void +) { + showActionSheet(SomeActionSheet( + actionSheet: ActionSheet( + title: Text("Delete contact?"), + buttons: [ + .destructive(Text("Only delete conversation")) { + deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: .messages, dismissToChatList, showAlert) + }, + .destructive(Text("Delete contact")) { + showSheetContent(SomeSheet( + content: { AnyView( + DeleteActiveContactDialog( + chat: chat, + contact: contact, + dismissToChatList: dismissToChatList, + showAlert: showAlert + ) + ) }, + id: "DeleteActiveContactDialog" + )) + }, + .cancel() + ] + ), + id: "deleteContactOrConversationDialog" + )) +} + +private func deleteContactMaybeErrorAlert( + _ chat: Chat, + _ contact: Contact, + chatDeleteMode: ChatDeleteMode, + _ dismissToChatList: Bool, + _ showAlert: @escaping (SomeAlert) -> Void +) { + Task { + let alert_ = await deleteContactChat(chat, chatDeleteMode: chatDeleteMode) + if let alert = alert_ { + showAlert(SomeAlert(alert: alert, id: "deleteContactMaybeErrorAlert, error")) + } else { + if dismissToChatList { + await MainActor.run { + ChatModel.shared.chatId = nil + } + DispatchQueue.main.async { + dismissAllSheets(animated: true) { + if case .messages = chatDeleteMode, showDeleteConversationNoticeDefault.get() { + AlertManager.shared.showAlert(deleteConversationNotice(contact)) + } else if chatDeleteMode.isEntity, showDeleteContactNoticeDefault.get() { + AlertManager.shared.showAlert(deleteContactNotice(contact)) + } + } + } + } else { + if case .messages = chatDeleteMode, showDeleteConversationNoticeDefault.get() { + showAlert(SomeAlert(alert: deleteConversationNotice(contact), id: "deleteContactMaybeErrorAlert, deleteConversationNotice")) + } else if chatDeleteMode.isEntity, showDeleteContactNoticeDefault.get() { + showAlert(SomeAlert(alert: deleteContactNotice(contact), id: "deleteContactMaybeErrorAlert, deleteContactNotice")) + } + } + } + } +} + +private func deleteConversationNotice(_ contact: Contact) -> Alert { + return Alert( + title: Text("Conversation deleted!"), + message: Text("You can send messages to \(contact.displayName) from Archived contacts."), + primaryButton: .default(Text("Don't show again")) { + showDeleteConversationNoticeDefault.set(false) + }, + secondaryButton: .default(Text("Ok")) + ) +} + +private func deleteContactNotice(_ contact: Contact) -> Alert { + return Alert( + title: Text("Contact deleted!"), + message: Text("You can still view conversation with \(contact.displayName) in the list of chats."), + primaryButton: .default(Text("Don't show again")) { + showDeleteContactNoticeDefault.set(false) + }, + secondaryButton: .default(Text("Ok")) + ) +} + +enum ContactDeleteMode { + case full + case entity + + public func toChatDeleteMode(notify: Bool) -> ChatDeleteMode { + switch self { + case .full: .full(notify: notify) + case .entity: .entity(notify: notify) + } + } +} + +struct DeleteActiveContactDialog: View { + @Environment(\.dismiss) var dismiss + @EnvironmentObject var theme: AppTheme + var chat: Chat + var contact: Contact + var dismissToChatList: Bool + var showAlert: (SomeAlert) -> Void + @State private var keepConversation = false + + var body: some View { + NavigationView { + List { + Section { + Toggle("Keep conversation", isOn: $keepConversation) + + Button(role: .destructive) { + dismiss() + deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: contactDeleteMode.toChatDeleteMode(notify: false), dismissToChatList, showAlert) + } label: { + Text("Delete without notification") + } + + Button(role: .destructive) { + dismiss() + deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: contactDeleteMode.toChatDeleteMode(notify: true), dismissToChatList, showAlert) + } label: { + Text("Delete and notify contact") + } + } footer: { + Text("Contact will be deleted - this cannot be undone!") + .foregroundColor(theme.colors.secondary) + } + } + .modifier(ThemedBackground(grouped: true)) + } + } + + var contactDeleteMode: ContactDeleteMode { + keepConversation ? .entity : .full + } +} + +private func deleteContactWithoutConversation( + _ chat: Chat, + _ contact: Contact, + _ dismissToChatList: Bool, + _ showAlert: @escaping (SomeAlert) -> Void, + _ showActionSheet: @escaping (SomeActionSheet) -> Void +) { + showActionSheet(SomeActionSheet( + actionSheet: ActionSheet( + title: Text("Confirm contact deletion?"), + buttons: [ + .destructive(Text("Delete and notify contact")) { + deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: .full(notify: true), dismissToChatList, showAlert) + }, + .destructive(Text("Delete without notification")) { + deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: .full(notify: false), dismissToChatList, showAlert) + }, + .cancel() + ] + ), + id: "deleteContactWithoutConversation" + )) +} + +private func deleteNotReadyContact( + _ chat: Chat, + _ contact: Contact, + _ dismissToChatList: Bool, + _ showAlert: @escaping (SomeAlert) -> Void, + _ showActionSheet: @escaping (SomeActionSheet) -> Void +) { + showActionSheet(SomeActionSheet( + actionSheet: ActionSheet( + title: Text("Confirm contact deletion?"), + buttons: [ + .destructive(Text("Confirm")) { + deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: .full(notify: false), dismissToChatList, showAlert) + }, + .cancel() + ] + ), + id: "deleteNotReadyContact" + )) +} + struct ChatInfoView_Previews: PreviewProvider { static var previews: some View { ChatInfoView( chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []), contact: Contact.sampleData, - connectionStats: Binding.constant(nil), - customUserProfile: Binding.constant(nil), localAlias: "", - connectionCode: Binding.constant(nil) + featuresAllowed: contactUserPrefsToFeaturesAllowed(Contact.sampleData.mergedPreferences), + currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(Contact.sampleData.mergedPreferences), + onSearch: {} ) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/AnimatedImageView.swift b/apps/ios/Shared/Views/Chat/ChatItem/AnimatedImageView.swift index bcdeb7fd9c..30f5e7a589 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/AnimatedImageView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/AnimatedImageView.swift @@ -9,6 +9,7 @@ import SwiftUI class AnimatedImageView: UIView { var image: UIImage? = nil var imageView: UIImageView? = nil + var cMode: UIView.ContentMode = .scaleAspectFit override init(frame: CGRect) { super.init(frame: frame) @@ -18,11 +19,12 @@ class AnimatedImageView: UIView { fatalError("Not implemented") } - convenience init(image: UIImage) { + convenience init(image: UIImage, contentMode: UIView.ContentMode) { self.init() self.image = image + self.cMode = contentMode imageView = UIImageView(gifImage: image) - imageView!.contentMode = .scaleAspectFit + imageView!.contentMode = contentMode self.addSubview(imageView!) } @@ -35,7 +37,7 @@ class AnimatedImageView: UIView { if let subview = self.subviews.first as? UIImageView { if image.imageData != subview.gifImage?.imageData { imageView = UIImageView(gifImage: image) - imageView!.contentMode = .scaleAspectFit + imageView!.contentMode = contentMode self.addSubview(imageView!) subview.removeFromSuperview() } @@ -47,13 +49,15 @@ class AnimatedImageView: UIView { struct SwiftyGif: UIViewRepresentable { private let image: UIImage + private let contentMode: UIView.ContentMode - init(image: UIImage) { + init(image: UIImage, contentMode: UIView.ContentMode = .scaleAspectFit) { self.image = image + self.contentMode = contentMode } func makeUIView(context: Context) -> AnimatedImageView { - AnimatedImageView(image: image) + AnimatedImageView(image: image, contentMode: contentMode) } func updateUIView(_ imageView: AnimatedImageView, context: Context) { diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CICallItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CICallItemView.swift index f0bf43dffe..0283e9c07e 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CICallItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CICallItemView.swift @@ -11,6 +11,7 @@ import SimpleXChat struct CICallItemView: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @ObservedObject var chat: Chat var chatItem: ChatItem var status: CICallStatus @@ -22,7 +23,7 @@ struct CICallItemView: View { switch status { case .pending: if sent { - Image(systemName: "phone.arrow.up.right").foregroundColor(.secondary) + Image(systemName: "phone.arrow.up.right").foregroundColor(theme.colors.secondary) } else { acceptCallButton() } @@ -35,9 +36,7 @@ struct CICallItemView: View { case .error: missedCallIcon(sent).foregroundColor(.orange) } - chatItem.timestampText - .font(.caption) - .foregroundColor(.secondary) + CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary, showStatus: false, showEdited: false) .padding(.bottom, 8) .padding(.horizontal, 12) } @@ -51,28 +50,28 @@ struct CICallItemView: View { Image(systemName: "phone.connection").foregroundColor(.green) } - @ViewBuilder private func endedCallIcon(_ sent: Bool) -> some View { + private func endedCallIcon(_ sent: Bool) -> some View { HStack { Image(systemName: "phone.down") - Text(durationText(duration)).foregroundColor(.secondary) + Text(durationText(duration)).foregroundColor(theme.colors.secondary) } } @ViewBuilder private func acceptCallButton() -> some View { if case let .direct(contact) = chat.chatInfo { - Button { - if let invitation = m.callInvitations[contact.id] { - CallController.shared.answerCall(invitation: invitation) - logger.debug("acceptCallButton call answered") - } else { - AlertManager.shared.showAlertMsg(title: "Call already ended!") - } - } label: { - Label("Answer call", systemImage: "phone.arrow.down.left") - } + Label("Answer call", systemImage: "phone.arrow.down.left") + .foregroundColor(theme.colors.primary) + .simultaneousGesture(TapGesture().onEnded { + if let invitation = m.callInvitations[contact.id] { + CallController.shared.answerCall(invitation: invitation) + logger.debug("acceptCallButton call answered") + } else { + AlertManager.shared.showAlertMsg(title: "Call already ended!") + } + }) } else { - Image(systemName: "phone.arrow.down.left").foregroundColor(.secondary) + Image(systemName: "phone.arrow.down.left").foregroundColor(theme.colors.secondary) } } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIChatFeatureView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIChatFeatureView.swift index 03afa30331..02be8af73b 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIChatFeatureView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIChatFeatureView.swift @@ -11,14 +11,17 @@ import SimpleXChat struct CIChatFeatureView: View { @EnvironmentObject var m: ChatModel + @Environment(\.revealed) var revealed: Bool + @ObservedObject var im = ItemsModel.shared + @ObservedObject var chat: Chat + @EnvironmentObject var theme: AppTheme var chatItem: ChatItem - @Binding var revealed: Bool var feature: Feature var icon: String? = nil var iconColor: Color var body: some View { - if !revealed, let fs = mergedFeautures() { + if !revealed, let fs = mergedFeatures() { HStack { ForEach(fs, content: featureIconView) } @@ -47,12 +50,12 @@ struct CIChatFeatureView: View { } } - private func mergedFeautures() -> [FeatureInfo]? { + private func mergedFeatures() -> [FeatureInfo]? { var fs: [FeatureInfo] = [] var icons: Set = [] if var i = m.getChatItemIndex(chatItem) { - while i < m.reversedChatItems.count, - let f = featureInfo(m.reversedChatItems[i]) { + while i < im.reversedChatItems.count, + let f = featureInfo(im.reversedChatItems[i]) { if !icons.contains(f.icon) { fs.insert(f, at: 0) icons.insert(f.icon) @@ -65,10 +68,10 @@ struct CIChatFeatureView: View { private func featureInfo(_ ci: ChatItem) -> FeatureInfo? { switch ci.content { - case let .rcvChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor, param) - case let .sndChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor, param) - case let .rcvGroupFeature(feature, preference, param): FeatureInfo(feature, preference.enable.iconColor, param) - case let .sndGroupFeature(feature, preference, param): FeatureInfo(feature, preference.enable.iconColor, param) + case let .rcvChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor(theme.colors.secondary), param) + case let .sndChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor(theme.colors.secondary), param) + case let .rcvGroupFeature(feature, preference, param, role): FeatureInfo(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor(theme.colors.secondary), param) + case let .sndGroupFeature(feature, preference, param, role): FeatureInfo(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor(theme.colors.secondary), param) default: nil } } @@ -80,7 +83,7 @@ struct CIChatFeatureView: View { if let param = f.param { HStack { i - chatEventText(Text(param)).lineLimit(1) + chatEventText(Text(param), theme.colors.secondary).lineLimit(1) } } else { i @@ -92,7 +95,7 @@ struct CIChatFeatureView: View { Image(systemName: icon ?? feature.iconFilled) .foregroundColor(iconColor) .scaleEffect(feature.iconScale) - chatEventText(chatItem) + chatEventText(chatItem, theme.colors.secondary) } .padding(.horizontal, 6) .padding(.vertical, 4) @@ -103,6 +106,9 @@ struct CIChatFeatureView: View { struct CIChatFeatureView_Previews: PreviewProvider { static var previews: some View { let enabled = FeatureEnabled(forUser: false, forContact: false) - CIChatFeatureView(chatItem: ChatItem.getChatFeatureSample(.fullDelete, enabled), revealed: Binding.constant(true), feature: ChatFeature.fullDelete, iconColor: enabled.iconColor) + CIChatFeatureView( + chat: Chat.sampleData, + chatItem: ChatItem.getChatFeatureSample(.fullDelete, enabled), feature: ChatFeature.fullDelete, iconColor: enabled.iconColor(.secondary) + ).environment(\.revealed, true) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIEventView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIEventView.swift index b6be3f837e..1375b87a5a 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIEventView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIEventView.swift @@ -17,6 +17,7 @@ struct CIEventView: View { .padding(.horizontal, 6) .padding(.vertical, 4) .textSelection(.disabled) + .lineLimit(4) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIFeaturePreferenceView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIFeaturePreferenceView.swift index e52a92a3c6..67f7b69e2c 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIFeaturePreferenceView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIFeaturePreferenceView.swift @@ -11,6 +11,7 @@ import SimpleXChat struct CIFeaturePreferenceView: View { @ObservedObject var chat: Chat + @EnvironmentObject var theme: AppTheme var chatItem: ChatItem var feature: ChatFeature var allowed: FeatureAllowed @@ -19,15 +20,15 @@ struct CIFeaturePreferenceView: View { var body: some View { HStack(alignment: .center, spacing: 4) { Image(systemName: feature.icon) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .scaleEffect(feature.iconScale) if let ct = chat.chatInfo.contact, allowed != .no && ct.allowsFeature(feature) && !ct.userAllowsFeature(feature) { let setParam = feature == .timedMessages && ct.mergedPreferences.timedMessages.userPreference.preference.ttl == nil featurePreferenceView(acceptText: setParam ? "Set 1 day" : "Accept") - .onTapGesture { + .simultaneousGesture(TapGesture().onEnded { allowFeatureToContact(ct, feature, param: setParam ? 86400 : nil) - } + }) } else { featurePreferenceView() } @@ -40,17 +41,17 @@ struct CIFeaturePreferenceView: View { private func featurePreferenceView(acceptText: LocalizedStringKey? = nil) -> some View { var r = Text(CIContent.preferenceText(feature, allowed, param) + " ") .fontWeight(.light) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) if let acceptText { r = r + Text(acceptText) .fontWeight(.medium) - .foregroundColor(.accentColor) - + Text(" ") + .foregroundColor(theme.colors.primary) + + Text(verbatim: " ") } r = r + chatItem.timestampText .fontWeight(.light) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) return r.font(.caption) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift index c94ba3f830..b0b404d8b5 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift @@ -11,15 +11,19 @@ import SimpleXChat struct CIFileView: View { @EnvironmentObject var m: ChatModel - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme let file: CIFile? let edited: Bool + var smallViewSize: CGFloat? var body: some View { - let metaReserve = edited - ? " " - : " " - Button(action: fileAction) { + if smallViewSize != nil { + fileIndicator() + .simultaneousGesture(TapGesture().onEnded(fileAction)) + } else { + let metaReserve = edited + ? " " + : " " HStack(alignment: .bottom, spacing: 6) { fileIndicator() .padding(.top, 5) @@ -30,12 +34,12 @@ struct CIFileView: View { Text(file.fileName) .lineLimit(1) .multilineTextAlignment(.leading) - .foregroundColor(.primary) + .foregroundColor(theme.colors.onBackground) Text(prettyFileSize + metaReserve) .font(.caption) .lineLimit(1) .multilineTextAlignment(.leading) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } } else { Text(metaReserve) @@ -45,45 +49,43 @@ struct CIFileView: View { .padding(.bottom, 6) .padding(.leading, 10) .padding(.trailing, 12) + .simultaneousGesture(TapGesture().onEnded(fileAction)) + .disabled(!itemInteractive) } - .disabled(!itemInteractive) } + @inline(__always) private var itemInteractive: Bool { if let file = file { switch (file.fileStatus) { case .sndStored: return file.fileProtocol == .local case .sndTransfer: return false - case .sndComplete: return false + case .sndComplete: return true case .sndCancelled: return false - case .sndError: return false + case .sndError: return true + case .sndWarning: return true case .rcvInvitation: return true case .rcvAccepted: return true case .rcvTransfer: return false + case .rcvAborted: return true case .rcvComplete: return true case .rcvCancelled: return false - case .rcvError: return false + case .rcvError: return true + case .rcvWarning: return true case .invalid: return false } } return false } - private func fileSizeValid() -> Bool { - if let file = file { - return file.fileSize <= getMaxFileSize(file.fileProtocol) - } - return false - } - private func fileAction() { logger.debug("CIFileView fileAction") if let file = file { switch (file.fileStatus) { - case .rcvInvitation: - if fileSizeValid() { + case .rcvInvitation, .rcvAborted: + if fileSizeValid(file) { Task { - logger.debug("CIFileView fileAction - in .rcvInvitation, in Task") + logger.debug("CIFileView fileAction - in .rcvInvitation, .rcvAborted, in Task") if let user = m.currentUser { await receiveFile(user: user, fileId: file.fileId) } @@ -114,11 +116,28 @@ struct CIFileView: View { if let fileSource = getLoadedFileSource(file) { saveCryptoFile(fileSource) } + case let .rcvError(rcvFileError): + logger.debug("CIFileView fileAction - in .rcvError") + showFileErrorAlert(rcvFileError) + case let .rcvWarning(rcvFileError): + logger.debug("CIFileView fileAction - in .rcvWarning") + showFileErrorAlert(rcvFileError, temporary: true) case .sndStored: logger.debug("CIFileView fileAction - in .sndStored") if file.fileProtocol == .local, let fileSource = getLoadedFileSource(file) { saveCryptoFile(fileSource) } + case .sndComplete: + logger.debug("CIFileView fileAction - in .sndComplete") + if let fileSource = getLoadedFileSource(file) { + saveCryptoFile(fileSource) + } + case let .sndError(sndFileError): + logger.debug("CIFileView fileAction - in .sndError") + showFileErrorAlert(sndFileError) + case let .sndWarning(sndFileError): + logger.debug("CIFileView fileAction - in .sndWarning") + showFileErrorAlert(sndFileError, temporary: true) default: break } } @@ -142,9 +161,10 @@ struct CIFileView: View { case .sndComplete: fileIcon("doc.fill", innerIcon: "checkmark", innerIconSize: 10) case .sndCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10) case .sndError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10) + case .sndWarning: fileIcon("doc.fill", innerIcon: "exclamationmark.triangle.fill", innerIconSize: 10) case .rcvInvitation: - if fileSizeValid() { - fileIcon("arrow.down.doc.fill", color: .accentColor) + if fileSizeValid(file) { + fileIcon("arrow.down.doc.fill", color: theme.colors.primary) } else { fileIcon("doc.fill", color: .orange, innerIcon: "exclamationmark", innerIconSize: 12) } @@ -155,9 +175,12 @@ struct CIFileView: View { } else { progressView() } + case .rcvAborted: + fileIcon("doc.fill", color: theme.colors.primary, innerIcon: "exclamationmark.arrow.circlepath", innerIconSize: 12) case .rcvComplete: fileIcon("doc.fill") case .rcvCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10) case .rcvError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10) + case .rcvWarning: fileIcon("doc.fill", innerIcon: "exclamationmark.triangle.fill", innerIconSize: 10) case .invalid: fileIcon("doc.fill", innerIcon: "questionmark", innerIconSize: 10) } } else { @@ -166,21 +189,22 @@ struct CIFileView: View { } private func fileIcon(_ icon: String, color: Color = Color(uiColor: .tertiaryLabel), innerIcon: String? = nil, innerIconSize: CGFloat? = nil) -> some View { - ZStack(alignment: .center) { + let size = smallViewSize ?? 30 + return ZStack(alignment: .center) { Image(systemName: icon) .resizable() .aspectRatio(contentMode: .fit) - .frame(width: 30, height: 30) + .frame(width: size, height: size) .foregroundColor(color) if let innerIcon = innerIcon, - let innerIconSize = innerIconSize { + let innerIconSize = innerIconSize, (smallViewSize == nil || file?.showStatusIconInSmallView == true) { Image(systemName: innerIcon) .resizable() .aspectRatio(contentMode: .fit) .frame(maxHeight: 16) .frame(width: innerIconSize, height: innerIconSize) .foregroundColor(.white) - .padding(.top, 12) + .padding(.top, size / 2.5) } } } @@ -201,6 +225,13 @@ struct CIFileView: View { } } +func fileSizeValid(_ file: CIFile?) -> Bool { + if let file = file { + return file.fileSize <= getMaxFileSize(file.fileProtocol) + } + return false +} + func saveCryptoFile(_ fileSource: CryptoFile) { if let cfArgs = fileSource.cryptoArgs { let url = getAppFilePath(fileSource.filePath) @@ -225,6 +256,26 @@ func saveCryptoFile(_ fileSource: CryptoFile) { } } +func showFileErrorAlert(_ err: FileError, temporary: Bool = false) { + let title: String = if temporary { + NSLocalizedString("Temporary file error", comment: "file error alert title") + } else { + NSLocalizedString("File error", comment: "file error alert title") + } + if let btn = err.moreInfoButton { + showAlert(title, message: err.errorInfo) { + [ + okAlertAction, + UIAlertAction(title: NSLocalizedString("How it works", comment: "alert button"), style: .default, handler: { _ in + UIApplication.shared.open(contentModerationPostLink) + }) + ] + } + } else { + showAlert(title, message: err.errorInfo) + } +} + struct CIFileView_Previews: PreviewProvider { static var previews: some View { let sentFile: ChatItem = ChatItem( @@ -242,17 +293,18 @@ struct CIFileView_Previews: PreviewProvider { file: nil ) Group { - ChatItemView(chat: Chat.sampleData, chatItem: sentFile, revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileName: "some_long_file_name_here", fileStatus: .rcvInvitation), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvAccepted), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvCancelled), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileSize: 1_000_000_000, fileStatus: .rcvInvitation), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(text: "Hello there", fileStatus: .rcvInvitation), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", fileStatus: .rcvInvitation), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: fileChatItemWtFile, revealed: Binding.constant(false)) + ChatItemView(chat: Chat.sampleData, chatItem: sentFile, scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileName: "some_long_file_name_here", fileStatus: .rcvInvitation), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvAccepted), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileStatus: .rcvCancelled), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(fileSize: 1_000_000_000, fileStatus: .rcvInvitation), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(text: "Hello there", fileStatus: .rcvInvitation), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getFileMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", fileStatus: .rcvInvitation), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: fileChatItemWtFile, scrollToItemId: { _ in }) } + .environment(\.revealed, false) .previewLayout(.fixed(width: 360, height: 360)) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift index 72013877ca..3fcf578875 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift @@ -11,7 +11,9 @@ import SimpleXChat struct CIGroupInvitationView: View { @EnvironmentObject var chatModel: ChatModel - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme + @Environment(\.showTimestamp) var showTimestamp: Bool + @ObservedObject var chat: Chat var chatItem: ChatItem var groupInvitation: CIGroupInvitation var memberRole: GroupMemberRole @@ -20,6 +22,8 @@ struct CIGroupInvitationView: View { @State private var inProgress = false @State private var progressByTimeout = false + @AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false + var body: some View { let action = !chatItem.chatDir.sent && groupInvitation.status == .pending let v = ZStack(alignment: .bottomTrailing) { @@ -37,16 +41,22 @@ struct CIGroupInvitationView: View { VStack(alignment: .leading, spacing: 2) { groupInvitationText() .overlay(DetermineWidth()) - Text(chatIncognito ? "Tap to join incognito" : "Tap to join") - .foregroundColor(inProgress ? .secondary : chatIncognito ? .indigo : .accentColor) - .font(.callout) - .padding(.trailing, 60) - .overlay(DetermineWidth()) + ( + Text(chatIncognito ? "Tap to join incognito" : "Tap to join") + .foregroundColor(inProgress ? theme.colors.secondary : chatIncognito ? .indigo : theme.colors.primary) + .font(.callout) + + Text(verbatim: " ") + + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp) + ) + .overlay(DetermineWidth()) } } else { - groupInvitationText() - .padding(.trailing, 60) - .overlay(DetermineWidth()) + ( + groupInvitationText() + + Text(verbatim: " ") + + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp) + ) + .overlay(DetermineWidth()) } } .padding(.bottom, 2) @@ -56,14 +66,11 @@ struct CIGroupInvitationView: View { } } - chatItem.timestampText - .font(.caption) - .foregroundColor(.secondary) + CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary, showStatus: false, showEdited: false) } .padding(.horizontal, 12) .padding(.vertical, 6) - .background(chatItemFrameColor(chatItem, colorScheme)) - .cornerRadius(18) + .background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) } .textSelection(.disabled) .onPreferenceChange(DetermineWidth.Key.self) { frameWidth = $0 } .onChange(of: inProgress) { inProgress in @@ -77,12 +84,12 @@ struct CIGroupInvitationView: View { } if action { - v.onTapGesture { + v.simultaneousGesture(TapGesture().onEnded { inProgress = true joinGroup(groupInvitation.groupId) { await MainActor.run { inProgress = false } } - } + }) .disabled(inProgress) } else { v @@ -92,7 +99,7 @@ struct CIGroupInvitationView: View { private func groupInfoView(_ action: Bool) -> some View { var color: Color if action && !inProgress { - color = chatIncognito ? .indigo : .accentColor + color = chatIncognito ? .indigo : theme.colors.primary } else { color = Color(uiColor: .tertiaryLabel) } @@ -100,9 +107,9 @@ struct CIGroupInvitationView: View { ProfileImage( imageStr: groupInvitation.groupProfile.image, iconName: "person.2.circle.fill", + size: 44, color: color ) - .frame(width: 44, height: 44) .padding(.trailing, 4) VStack(alignment: .leading) { let p = groupInvitation.groupProfile @@ -115,7 +122,7 @@ struct CIGroupInvitationView: View { } } - private func groupInvitationText() -> some View { + private func groupInvitationText() -> Text { Text(groupInvitationStr()) .font(.callout) } @@ -137,8 +144,8 @@ struct CIGroupInvitationView: View { struct CIGroupInvitationView_Previews: PreviewProvider { static var previews: some View { Group { - CIGroupInvitationView(chatItem: ChatItem.getGroupInvitationSample(), groupInvitation: CIGroupInvitation.getSample(groupProfile: GroupProfile(displayName: "team", fullName: "team")), memberRole: .admin) - CIGroupInvitationView(chatItem: ChatItem.getGroupInvitationSample(), groupInvitation: CIGroupInvitation.getSample(status: .accepted), memberRole: .admin) + CIGroupInvitationView(chat: Chat.sampleData, chatItem: ChatItem.getGroupInvitationSample(), groupInvitation: CIGroupInvitation.getSample(groupProfile: GroupProfile(displayName: "team", fullName: "team")), memberRole: .admin) + CIGroupInvitationView(chat: Chat.sampleData, chatItem: ChatItem.getGroupInvitationSample(), groupInvitation: CIGroupInvitation.getSample(status: .accepted), memberRole: .admin) } } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift index c3e4805bf3..d30369339d 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift @@ -11,34 +11,44 @@ import SimpleXChat struct CIImageView: View { @EnvironmentObject var m: ChatModel - @Environment(\.colorScheme) var colorScheme let chatItem: ChatItem - let image: String + var scrollToItemId: ((ChatItem.ID) -> Void)? = nil + var preview: UIImage? let maxWidth: CGFloat - @Binding var imgWidth: CGFloat? - @State var scrollProxy: ScrollViewProxy? - @State var metaColor: Color - @State private var showFullScreenImage = false + var imgWidth: CGFloat? + var smallView: Bool = false + @Binding var showFullScreenImage: Bool + @State private var blurred: Bool = UserDefaults.standard.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) > 0 var body: some View { let file = chatItem.file VStack(alignment: .center, spacing: 6) { if let uiImage = getLoadedImage(file) { - imageView(uiImage) + Group { if smallView { smallViewImageView(uiImage) } else { imageView(uiImage) } } .fullScreenCover(isPresented: $showFullScreenImage) { - FullScreenMediaView(chatItem: chatItem, image: uiImage, showView: $showFullScreenImage, scrollProxy: scrollProxy) + FullScreenMediaView(chatItem: chatItem, scrollToItemId: scrollToItemId, image: uiImage, showView: $showFullScreenImage) + } + .if(!smallView) { view in + view.modifier(PrivacyBlur(blurred: $blurred)) + } + .if(!blurred) { v in + v.simultaneousGesture(TapGesture().onEnded { showFullScreenImage = true }) } - .onTapGesture { showFullScreenImage = true } .onChange(of: m.activeCallViewIsCollapsed) { _ in showFullScreenImage = false } - } else if let data = Data(base64Encoded: dropImagePrefix(image)), - let uiImage = UIImage(data: data) { - imageView(uiImage) - .onTapGesture { + } else if let preview { + Group { + if smallView { + smallViewImageView(preview) + } else { + imageView(preview).modifier(PrivacyBlur(blurred: $blurred)) + } + } + .simultaneousGesture(TapGesture().onEnded { if let file = file { switch file.fileStatus { - case .rcvInvitation: + case .rcvInvitation, .rcvAborted: Task { if let user = m.currentUser { await receiveFile(user: user, fileId: file.fileId) @@ -61,29 +71,58 @@ struct CIImageView: View { case .rcvTransfer: () // ? case .rcvComplete: () // ? case .rcvCancelled: () // TODO + case let .rcvError(rcvFileError): + showFileErrorAlert(rcvFileError) + case let .rcvWarning(rcvFileError): + showFileErrorAlert(rcvFileError, temporary: true) + case let .sndError(sndFileError): + showFileErrorAlert(sndFileError) + case let .sndWarning(sndFileError): + showFileErrorAlert(sndFileError, temporary: true) default: () } } - } + }) } } + .onDisappear { + showFullScreenImage = false + } } private func imageView(_ img: UIImage) -> some View { - let w = img.size.width <= img.size.height ? maxWidth * 0.75 : img.imageData == nil ? .infinity : maxWidth - DispatchQueue.main.async { imgWidth = w } + let w = img.size.width <= img.size.height ? maxWidth * 0.75 : maxWidth return ZStack(alignment: .topTrailing) { if img.imageData == nil { Image(uiImage: img) .resizable() .scaledToFit() - .frame(maxWidth: w) + .frame(width: w) } else { SwiftyGif(image: img) .frame(width: w, height: w * img.size.height / img.size.width) .scaledToFit() } - loadingIndicator() + if !blurred || !showDownloadButton(chatItem.file?.fileStatus) { + loadingIndicator() + } + } + } + + private func smallViewImageView(_ img: UIImage) -> some View { + ZStack(alignment: .topTrailing) { + if img.imageData == nil { + Image(uiImage: img) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: maxWidth, height: maxWidth) + } else { + SwiftyGif(image: img, contentMode: .scaleAspectFill) + .frame(width: maxWidth, height: maxWidth) + } + if chatItem.file?.showStatusIconInSmallView == true { + loadingIndicator() + } } } @@ -100,13 +139,16 @@ struct CIImageView: View { case .sndComplete: fileIcon("checkmark", 10, 13) case .sndCancelled: fileIcon("xmark", 10, 13) case .sndError: fileIcon("xmark", 10, 13) + case .sndWarning: fileIcon("exclamationmark.triangle.fill", 10, 13) case .rcvInvitation: fileIcon("arrow.down", 10, 13) case .rcvAccepted: fileIcon("ellipsis", 14, 11) case .rcvTransfer: progressView() + case .rcvAborted: fileIcon("exclamationmark.arrow.circlepath", 14, 11) + case .rcvComplete: EmptyView() case .rcvCancelled: fileIcon("xmark", 10, 13) case .rcvError: fileIcon("xmark", 10, 13) + case .rcvWarning: fileIcon("exclamationmark.triangle.fill", 10, 13) case .invalid: fileIcon("questionmark", 10, 13) - default: EmptyView() } } } @@ -114,9 +156,9 @@ struct CIImageView: View { private func fileIcon(_ icon: String, _ size: CGFloat, _ padding: CGFloat) -> some View { Image(systemName: icon) .resizable() + .invertedForegroundStyle() .aspectRatio(contentMode: .fit) .frame(width: size, height: size) - .foregroundColor(metaColor) .padding(padding) } @@ -127,4 +169,12 @@ struct CIImageView: View { .tint(.white) .padding(8) } + + private func showDownloadButton(_ fileStatus: CIFileStatus?) -> Bool { + switch fileStatus { + case .rcvInvitation: true + case .rcvAborted: true + default: false + } + } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIInvalidJSONView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIInvalidJSONView.swift index 0299a5e6f8..5e9fa691de 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIInvalidJSONView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIInvalidJSONView.swift @@ -7,9 +7,11 @@ // import SwiftUI +import SimpleXChat struct CIInvalidJSONView: View { - var json: String + @EnvironmentObject var theme: AppTheme + var json: Data? @State private var showJSON = false var body: some View { @@ -21,18 +23,17 @@ struct CIInvalidJSONView: View { .padding(.horizontal, 12) .padding(.vertical, 6) .background(Color(uiColor: .tertiarySystemGroupedBackground)) - .cornerRadius(18) .textSelection(.disabled) - .onTapGesture { showJSON = true } - .sheet(isPresented: $showJSON) { - invalidJSONView(json) + .simultaneousGesture(TapGesture().onEnded { showJSON = true }) + .appSheet(isPresented: $showJSON) { + invalidJSONView(dataToString(json)) } } } func invalidJSONView(_ json: String) -> some View { VStack(alignment: .leading, spacing: 16) { - Button { + Button { // this is used in the sheet, Button works here showShareSheet(items: [json]) } label: { Image(systemName: "square.and.arrow.up") @@ -44,10 +45,11 @@ func invalidJSONView(_ json: String) -> some View { } .frame(maxHeight: .infinity) .padding() + .modifier(ThemedBackground()) } struct CIInvalidJSONView_Previews: PreviewProvider { static var previews: some View { - CIInvalidJSONView(json: "{}") + CIInvalidJSONView(json: "{}".data(using: .utf8)!) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift index 4c12c7312a..f9dbaede63 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift @@ -10,36 +10,59 @@ import SwiftUI import SimpleXChat struct CILinkView: View { - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme let linkPreview: LinkPreview + @State private var blurred: Bool = UserDefaults.standard.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) > 0 var body: some View { VStack(alignment: .center, spacing: 6) { - if let data = Data(base64Encoded: dropImagePrefix(linkPreview.image)), - let uiImage = UIImage(data: data) { + if let uiImage = imageFromBase64(linkPreview.image) { Image(uiImage: uiImage) .resizable() .scaledToFit() + .modifier(PrivacyBlur(blurred: $blurred)) + .if(!blurred) { v in + v.simultaneousGesture(TapGesture().onEnded { + openBrowserAlert(uri: linkPreview.uri) + }) + } } VStack(alignment: .leading, spacing: 6) { Text(linkPreview.title) .lineLimit(3) -// if linkPreview.description != "" { -// Text(linkPreview.description) -// .font(.subheadline) -// .lineLimit(12) -// } Text(linkPreview.uri.absoluteString) .font(.caption) .lineLimit(1) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } .padding(.horizontal, 12) .frame(maxWidth: .infinity, alignment: .leading) + .simultaneousGesture(TapGesture().onEnded { + openBrowserAlert(uri: linkPreview.uri) + }) } } } +func openBrowserAlert(uri: URL) { + showAlert( + NSLocalizedString("Open link?", comment: "alert title"), + message: uri.absoluteString, + actions: {[ + UIAlertAction( + title: NSLocalizedString("Cancel", comment: "alert action"), + style: .default, + handler: { _ in } + ), + UIAlertAction( + title: NSLocalizedString("Open", comment: "alert action"), + style: .default, + handler: { _ in UIApplication.shared.open(uri) } + ) + ]} + ) +} + struct LargeLinkPreview_Previews: PreviewProvider { static var previews: some View { let preview = LinkPreview( diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIMemberCreatedContactView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIMemberCreatedContactView.swift index da82ed4dd2..2898a318a9 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIMemberCreatedContactView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIMemberCreatedContactView.swift @@ -11,6 +11,7 @@ import SimpleXChat struct CIMemberCreatedContactView: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme var chatItem: ChatItem var body: some View { @@ -19,12 +20,11 @@ struct CIMemberCreatedContactView: View { case let .groupRcv(groupMember): if let contactId = groupMember.memberContactId { memberCreatedContactView(openText: "Open") - .onTapGesture { - dismissAllSheets(animated: true) - DispatchQueue.main.async { - m.chatId = "@\(contactId)" + .simultaneousGesture(TapGesture().onEnded { + ItemsModel.shared.loadOpenChat("@\(contactId)") { + dismissAllSheets(animated: true) } - } + }) } else { memberCreatedContactView() } @@ -43,12 +43,12 @@ struct CIMemberCreatedContactView: View { r = r + Text(openText) .fontWeight(.medium) - .foregroundColor(.accentColor) - + Text(" ") + .foregroundColor(theme.colors.primary) + + Text(verbatim: " ") } r = r + chatItem.timestampText .fontWeight(.light) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) return r.font(.caption) } @@ -56,11 +56,11 @@ struct CIMemberCreatedContactView: View { if let member = chatItem.memberDisplayName { return Text(member + " " + chatItem.content.text + " ") .fontWeight(.light) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } else { return Text(chatItem.content.text + " ") .fontWeight(.light) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIMetaView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIMetaView.swift index c189abde24..fc73778239 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIMetaView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIMetaView.swift @@ -11,97 +11,173 @@ import SimpleXChat struct CIMetaView: View { @ObservedObject var chat: Chat + @EnvironmentObject var theme: AppTheme + @Environment(\.showTimestamp) var showTimestamp: Bool var chatItem: ChatItem - var metaColor = Color.secondary - var paleMetaColor = Color(UIColor.tertiaryLabel) + var metaColor: Color + var paleMetaColor = Color(uiColor: .tertiaryLabel) + var showStatus = true + var showEdited = true + var invertedMaterial = false + + @AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false var body: some View { if chatItem.isDeletedContent { chatItem.timestampText.font(.caption).foregroundColor(metaColor) } else { - let meta = chatItem.meta - let ttl = chat.chatInfo.timedMessagesTTL - let encrypted = chatItem.encryptedFile - switch meta.itemStatus { - case let .sndSent(sndProgress): - switch sndProgress { - case .complete: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .sent) - case .partial: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .sent) + ZStack { + ciMetaText( + chatItem.meta, + chatTTL: chat.chatInfo.timedMessagesTTL, + encrypted: chatItem.encryptedFile, + color: metaColor, + paleColor: paleMetaColor, + colorMode: invertedMaterial + ? .invertedMaterial + : .normal, + showStatus: showStatus, + showEdited: showEdited, + showViaProxy: showSentViaProxy, + showTimesamp: showTimestamp + ).invertedForegroundStyle(enabled: invertedMaterial) + if invertedMaterial { + ciMetaText( + chatItem.meta, + chatTTL: chat.chatInfo.timedMessagesTTL, + encrypted: chatItem.encryptedFile, + colorMode: .normal, + onlyOverrides: true, + showStatus: showStatus, + showEdited: showEdited, + showViaProxy: showSentViaProxy, + showTimesamp: showTimestamp + ) } - case let .sndRcvd(_, sndProgress): - switch sndProgress { - case .complete: - ZStack { - ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd1) - ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd2) - } - case .partial: - ZStack { - ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd1) - ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd2) - } - } - default: - ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor) } } } } -enum SentCheckmark { - case sent - case rcvd1 - case rcvd2 +enum MetaColorMode { + // Renders provided colours + case normal + // Fully transparent meta - used for reserving space + case transparent + // Renders white on dark backgrounds and black on light ones + case invertedMaterial + + func resolve(_ c: Color?) -> Color? { + switch self { + case .normal: c + case .transparent: .clear + case .invertedMaterial: nil + } + } + + func statusSpacer(_ sent: Bool) -> Text { + switch self { + case .normal, .transparent: + Text( + sent + ? Image("checkmark.wide") + : Image(systemName: "circlebadge.fill") + ).foregroundColor(.clear) + case .invertedMaterial: textSpace.kerning(13) + } + } } -func ciMetaText(_ meta: CIMeta, chatTTL: Int?, encrypted: Bool?, color: Color = .clear, transparent: Bool = false, sent: SentCheckmark? = nil) -> Text { +func ciMetaText( + _ meta: CIMeta, + chatTTL: Int?, + encrypted: Bool?, + color: Color = .clear, // we use this function to reserve space without rendering meta + paleColor: Color? = nil, + primaryColor: Color = .accentColor, + colorMode: MetaColorMode = .normal, + onlyOverrides: Bool = false, // only render colors that differ from base + showStatus: Bool = true, + showEdited: Bool = true, + showViaProxy: Bool, + showTimesamp: Bool +) -> Text { var r = Text("") - if meta.itemEdited { - r = r + statusIconText("pencil", color) + var space: Text? = nil + let appendSpace = { + if let sp = space { + r = r + sp + space = nil + } + } + let resolved = colorMode.resolve(color) + if showEdited, meta.itemEdited { + r = r + statusIconText("pencil", resolved) } if meta.disappearing { - r = r + statusIconText("timer", color).font(.caption2) + r = r + statusIconText("timer", resolved).font(.caption2) let ttl = meta.itemTimed?.ttl if ttl != chatTTL { - r = r + Text(shortTimeText(ttl)).foregroundColor(color) + r = r + colored(Text(shortTimeText(ttl)), resolved) } - r = r + Text(" ") + space = textSpace } - if let (icon, statusColor) = meta.statusIcon(color) { - let t = Text(Image(systemName: icon)).font(.caption2) - let gap = Text(" ").kerning(-1.25) - let t1 = t.foregroundColor(transparent ? .clear : statusColor.opacity(0.67)) - switch sent { - case nil: r = r + t1 - case .sent: r = r + t1 + gap - case .rcvd1: r = r + t.foregroundColor(transparent ? .clear : statusColor.opacity(0.67)) + gap - case .rcvd2: r = r + gap + t1 + if showViaProxy, meta.sentViaProxy == true { + appendSpace() + r = r + statusIconText("arrow.forward", resolved?.opacity(0.67)).font(.caption2) + } + if showStatus { + appendSpace() + if let (image, statusColor) = meta.itemStatus.statusIcon(color, paleColor ?? color, primaryColor) { + let metaColor = if onlyOverrides && statusColor == color { + Color.clear + } else { + colorMode.resolve(statusColor) + } + r = r + colored(Text(image), metaColor) + } else if !meta.disappearing { + r = r + colorMode.statusSpacer(meta.itemStatus.sent) } - r = r + Text(" ") - } else if !meta.disappearing { - r = r + statusIconText("circlebadge.fill", .clear) + Text(" ") + space = textSpace } if let enc = encrypted { - r = r + statusIconText(enc ? "lock" : "lock.open", color) + Text(" ") + appendSpace() + r = r + statusIconText(enc ? "lock" : "lock.open", resolved) + space = textSpace + } + if showTimesamp { + appendSpace() + r = r + colored(meta.timestampText, resolved) } - r = r + meta.timestampText.foregroundColor(color) return r.font(.caption) } -private func statusIconText(_ icon: String, _ color: Color) -> Text { - Text(Image(systemName: icon)).foregroundColor(color) +@inline(__always) +private func statusIconText(_ icon: String, _ color: Color?) -> Text { + colored(Text(Image(systemName: icon)), color) +} + +// Applying `foregroundColor(nil)` breaks `.invertedForegroundStyle` modifier +@inline(__always) +private func colored(_ t: Text, _ color: Color?) -> Text { + if let color { + t.foregroundColor(color) + } else { + t + } } struct CIMetaView_Previews: PreviewProvider { + static let metaColor = Color.secondary static var previews: some View { Group { - CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete))) - CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .partial))) - CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .complete))) - CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .partial))) - CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .badMsgHash, sndProgress: .complete))) - CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), itemEdited: true)) - CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getDeletedContentSample()) + CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete)), metaColor: metaColor) + CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .partial)), metaColor: metaColor) + CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .complete)), metaColor: metaColor) + CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .partial)), metaColor: metaColor) + CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .badMsgHash, sndProgress: .complete)), metaColor: metaColor) + CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), itemEdited: true), metaColor: metaColor) + CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getDeletedContentSample(), metaColor: metaColor) } .previewLayout(.fixed(width: 360, height: 100)) } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift index 3ad45d6987..4e5713c263 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift @@ -13,18 +13,22 @@ let decryptErrorReason: LocalizedStringKey = "It can happen when you or your con struct CIRcvDecryptionError: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @ObservedObject var chat: Chat + @Environment(\.showTimestamp) var showTimestamp: Bool var msgDecryptError: MsgDecryptError var msgCount: UInt32 var chatItem: ChatItem @State private var alert: CIRcvDecryptionErrorAlert? + @AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false + enum CIRcvDecryptionErrorAlert: Identifiable { case syncAllowedAlert(_ syncConnection: () -> Void) case syncNotSupportedContactAlert case syncNotSupportedMemberAlert case decryptionErrorAlert - case error(title: LocalizedStringKey, error: LocalizedStringKey) + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -44,7 +48,7 @@ struct CIRcvDecryptionError: View { if case let .group(groupInfo) = chat.chatInfo, case let .groupRcv(groupMember) = chatItem.chatDir { do { - let (member, stats) = try apiGroupMemberInfo(groupInfo.apiId, groupMember.groupMemberId) + let (member, stats) = try apiGroupMemberInfoSync(groupInfo.apiId, groupMember.groupMemberId) if let s = stats { m.updateGroupMemberConnectionStats(groupInfo, member, s) } @@ -59,43 +63,46 @@ struct CIRcvDecryptionError: View { case .syncNotSupportedContactAlert: return Alert(title: Text("Fix not supported by contact"), message: message()) case .syncNotSupportedMemberAlert: return Alert(title: Text("Fix not supported by group member"), message: message()) case .decryptionErrorAlert: return Alert(title: Text("Decryption error"), message: message()) - case let .error(title, error): return Alert(title: Text(title), message: Text(error)) + case let .error(title, error): return mkAlert(title: title, message: error) } } } - @ViewBuilder private func viewBody() -> some View { - if case let .direct(contact) = chat.chatInfo, - let contactStats = contact.activeConn?.connectionStats { - if contactStats.ratchetSyncAllowed { - decryptionErrorItemFixButton(syncSupported: true) { - alert = .syncAllowedAlert { syncContactConnection(contact) } + private func viewBody() -> some View { + Group { + if case let .direct(contact) = chat.chatInfo, + let contactStats = contact.activeConn?.connectionStats { + if contactStats.ratchetSyncAllowed { + decryptionErrorItemFixButton(syncSupported: true) { + alert = .syncAllowedAlert { syncContactConnection(contact) } + } + } else if !contactStats.ratchetSyncSupported { + decryptionErrorItemFixButton(syncSupported: false) { + alert = .syncNotSupportedContactAlert + } + } else { + basicDecryptionErrorItem() } - } else if !contactStats.ratchetSyncSupported { - decryptionErrorItemFixButton(syncSupported: false) { - alert = .syncNotSupportedContactAlert + } else if case let .group(groupInfo) = chat.chatInfo, + case let .groupRcv(groupMember) = chatItem.chatDir, + let mem = m.getGroupMember(groupMember.groupMemberId), + let memberStats = mem.wrapped.activeConn?.connectionStats { + if memberStats.ratchetSyncAllowed { + decryptionErrorItemFixButton(syncSupported: true) { + alert = .syncAllowedAlert { syncMemberConnection(groupInfo, groupMember) } + } + } else if !memberStats.ratchetSyncSupported { + decryptionErrorItemFixButton(syncSupported: false) { + alert = .syncNotSupportedMemberAlert + } + } else { + basicDecryptionErrorItem() } } else { basicDecryptionErrorItem() } - } else if case let .group(groupInfo) = chat.chatInfo, - case let .groupRcv(groupMember) = chatItem.chatDir, - let mem = m.getGroupMember(groupMember.groupMemberId), - let memberStats = mem.wrapped.activeConn?.connectionStats { - if memberStats.ratchetSyncAllowed { - decryptionErrorItemFixButton(syncSupported: true) { - alert = .syncAllowedAlert { syncMemberConnection(groupInfo, groupMember) } - } - } else if !memberStats.ratchetSyncSupported { - decryptionErrorItemFixButton(syncSupported: false) { - alert = .syncNotSupportedMemberAlert - } - } else { - basicDecryptionErrorItem() - } - } else { - basicDecryptionErrorItem() } + .background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) } } private func basicDecryptionErrorItem() -> some View { @@ -112,24 +119,22 @@ struct CIRcvDecryptionError: View { } ( Text(Image(systemName: "exclamationmark.arrow.triangle.2.circlepath")) - .foregroundColor(syncSupported ? .accentColor : .secondary) + .foregroundColor(syncSupported ? theme.colors.primary : theme.colors.secondary) .font(.callout) - + Text(" ") + + textSpace + Text("Fix connection") - .foregroundColor(syncSupported ? .accentColor : .secondary) + .foregroundColor(syncSupported ? theme.colors.primary : theme.colors.secondary) .font(.callout) - + Text(" ") - + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true) + + Text(verbatim: " ") + + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp) ) } .padding(.horizontal, 12) - CIMetaView(chat: chat, chatItem: chatItem) + CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary) .padding(.horizontal, 12) } - .onTapGesture(perform: { onClick() }) + .simultaneousGesture(TapGesture().onEnded(onClick)) .padding(.vertical, 6) - .background(Color(uiColor: .tertiarySystemGroupedBackground)) - .cornerRadius(18) .textSelection(.disabled) } @@ -139,17 +144,15 @@ struct CIRcvDecryptionError: View { Text(chatItem.content.text) .foregroundColor(.red) .italic() - + Text(" ") - + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true) + + Text(verbatim: " ") + + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp) } .padding(.horizontal, 12) - CIMetaView(chat: chat, chatItem: chatItem) + CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary) .padding(.horizontal, 12) } - .onTapGesture(perform: { onClick() }) + .simultaneousGesture(TapGesture().onEnded(onClick)) .padding(.vertical, 6) - .background(Color(uiColor: .tertiarySystemGroupedBackground)) - .cornerRadius(18) .textSelection(.disabled) } @@ -158,13 +161,13 @@ struct CIRcvDecryptionError: View { let why = Text(decryptErrorReason) switch msgDecryptError { case .ratchetHeader: - message = Text("\(msgCount) messages failed to decrypt.") + Text("\n") + why + message = Text("\(msgCount) messages failed to decrypt.") + textNewLine + why case .tooManySkipped: - message = Text("\(msgCount) messages skipped.") + Text("\n") + why + message = Text("\(msgCount) messages skipped.") + textNewLine + why case .ratchetEarlier: - message = Text("\(msgCount) messages failed to decrypt.") + Text("\n") + why + message = Text("\(msgCount) messages failed to decrypt.") + textNewLine + why case .other: - message = Text("\(msgCount) messages failed to decrypt.") + Text("\n") + why + message = Text("\(msgCount) messages failed to decrypt.") + textNewLine + why case .ratchetSync: message = Text("Encryption re-negotiation failed.") } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift index ff208fe58a..eacbe9360a 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift @@ -13,95 +13,136 @@ import Combine struct CIVideoView: View { @EnvironmentObject var m: ChatModel - @Environment(\.colorScheme) var colorScheme private let chatItem: ChatItem - private let image: String + private let preview: UIImage? @State private var duration: Int @State private var progress: Int = 0 @State private var videoPlaying: Bool = false private let maxWidth: CGFloat - @Binding private var videoWidth: CGFloat? - @State private var scrollProxy: ScrollViewProxy? - @State private var preview: UIImage? = nil + private var videoWidth: CGFloat? + private let smallView: Bool @State private var player: AVPlayer? @State private var fullPlayer: AVPlayer? @State private var url: URL? @State private var urlDecrypted: URL? @State private var decryptionInProgress: Bool = false - @State private var showFullScreenPlayer = false + @Binding private var showFullScreenPlayer: Bool @State private var timeObserver: Any? = nil @State private var fullScreenTimeObserver: Any? = nil @State private var publisher: AnyCancellable? = nil + private var sizeMultiplier: CGFloat { smallView ? 0.38 : 1 } + @State private var blurred: Bool = UserDefaults.standard.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) > 0 - init(chatItem: ChatItem, image: String, duration: Int, maxWidth: CGFloat, videoWidth: Binding, scrollProxy: ScrollViewProxy?) { + init(chatItem: ChatItem, preview: UIImage?, duration: Int, maxWidth: CGFloat, videoWidth: CGFloat?, smallView: Bool = false, showFullscreenPlayer: Binding) { self.chatItem = chatItem - self.image = image + self.preview = preview self._duration = State(initialValue: duration) self.maxWidth = maxWidth - self._videoWidth = videoWidth - self.scrollProxy = scrollProxy - if let url = getLoadedVideo(chatItem.file) { - let decrypted = chatItem.file?.fileSource?.cryptoArgs == nil ? url : chatItem.file?.fileSource?.decryptedGet() - self._urlDecrypted = State(initialValue: decrypted) - if let decrypted = decrypted { - self._player = State(initialValue: VideoPlayerView.getOrCreatePlayer(decrypted, false)) - self._fullPlayer = State(initialValue: AVPlayer(url: decrypted)) - } - self._url = State(initialValue: url) - } - if let data = Data(base64Encoded: dropImagePrefix(image)), - let uiImage = UIImage(data: data) { - self._preview = State(initialValue: uiImage) - } + self.videoWidth = videoWidth + self.smallView = smallView + self._showFullScreenPlayer = showFullscreenPlayer } var body: some View { let file = chatItem.file - ZStack { + ZStack(alignment: smallView ? .topLeading : .center) { ZStack(alignment: .topLeading) { - if let file = file, let preview = preview, let player = player, let decrypted = urlDecrypted { - videoView(player, decrypted, file, preview, duration) - } else if let file = file, let defaultPreview = preview, file.loaded && urlDecrypted == nil { - videoViewEncrypted(file, defaultPreview, duration) - } else if let data = Data(base64Encoded: dropImagePrefix(image)), - let uiImage = UIImage(data: data) { - imageView(uiImage) - .onTapGesture { - if let file = file { - switch file.fileStatus { - case .rcvInvitation: - receiveFileIfValidSize(file: file, receiveFile: receiveFile) - case .rcvAccepted: - switch file.fileProtocol { - case .xftp: - AlertManager.shared.showAlertMsg( - title: "Waiting for video", - message: "Video will be received when your contact completes uploading it." - ) - case .smp: - AlertManager.shared.showAlertMsg( - title: "Waiting for video", - message: "Video will be received when your contact is online, please wait or check later!" - ) - case .local: () - } - case .rcvTransfer: () // ? - case .rcvComplete: () // ? - case .rcvCancelled: () // TODO - default: () - } + if let file, let preview { + if let urlDecrypted { + if smallView { + smallVideoView(urlDecrypted, file, preview) + } else if let player { + videoView(player, urlDecrypted, file, preview, duration) } + } else if file.loaded { + if smallView { + smallVideoViewEncrypted(file, preview) + } else { + videoViewEncrypted(file, preview, duration) + } + } else { + Group { if smallView { smallViewImageView(preview, file) } else { imageView(preview) } } + .simultaneousGesture(TapGesture().onEnded { + switch file.fileStatus { + case .rcvInvitation, .rcvAborted: + receiveFileIfValidSize(file: file, receiveFile: receiveFile) + case .rcvAccepted: + switch file.fileProtocol { + case .xftp: + AlertManager.shared.showAlertMsg( + title: "Waiting for video", + message: "Video will be received when your contact completes uploading it." + ) + case .smp: + AlertManager.shared.showAlertMsg( + title: "Waiting for video", + message: "Video will be received when your contact is online, please wait or check later!" + ) + case .local: () + } + case .rcvTransfer: () // ? + case .rcvComplete: () // ? + case .rcvCancelled: () // TODO + default: () + } + }) } } - durationProgress() - } - if let file = file, case .rcvInvitation = file.fileStatus { - Button { - receiveFileIfValidSize(file: file, receiveFile: receiveFile) - } label: { - playPauseIcon("play.fill") + if !smallView { + durationProgress() } } + if !blurred, let file, showDownloadButton(file.fileStatus) { + if !smallView || !file.showStatusIconInSmallView { + playPauseIcon("play.fill") + .simultaneousGesture(TapGesture().onEnded { + receiveFileIfValidSize(file: file, receiveFile: receiveFile) + }) + } + } + } + .fullScreenCover(isPresented: $showFullScreenPlayer) { + if let decrypted = urlDecrypted { + fullScreenPlayer(decrypted) + } + } + .onAppear { + setupPlayer(chatItem.file) + } + .onChange(of: chatItem.file) { file in + // ChatItem can be changed in small view on chat list screen + setupPlayer(file) + } + .onDisappear { + showFullScreenPlayer = false + } + } + + private func setupPlayer(_ file: CIFile?) { + let newUrl = getLoadedVideo(file) + if newUrl == url { + return + } + url = nil + urlDecrypted = nil + player = nil + fullPlayer = nil + if let newUrl { + let decrypted = file?.fileSource?.cryptoArgs == nil ? newUrl : file?.fileSource?.decryptedGet() + urlDecrypted = decrypted + if let decrypted = decrypted { + player = VideoPlayerView.getOrCreatePlayer(decrypted, false) + fullPlayer = AVPlayer(url: decrypted) + } + url = newUrl + } + } + + private func showDownloadButton(_ fileStatus: CIFileStatus?) -> Bool { + switch fileStatus { + case .rcvInvitation: true + case .rcvAborted: true + default: false } } @@ -110,33 +151,29 @@ struct CIVideoView: View { ZStack(alignment: .center) { let canBePlayed = !chatItem.chatDir.sent || file.fileStatus == CIFileStatus.sndComplete || (file.fileStatus == .sndStored && file.fileProtocol == .local) imageView(defaultPreview) - .fullScreenCover(isPresented: $showFullScreenPlayer) { - if let decrypted = urlDecrypted { - fullScreenPlayer(decrypted) - } - } - .onTapGesture { + .simultaneousGesture(TapGesture().onEnded { decrypt(file: file) { showFullScreenPlayer = urlDecrypted != nil } - } + }) .onChange(of: m.activeCallViewIsCollapsed) { _ in showFullScreenPlayer = false } - if !decryptionInProgress { - Button { - decrypt(file: file) { - if let decrypted = urlDecrypted { - videoPlaying = true - player?.play() - } - } - } label: { + if !blurred { + if !decryptionInProgress { playPauseIcon(canBePlayed ? "play.fill" : "play.slash") + .simultaneousGesture(TapGesture().onEnded { + decrypt(file: file) { + if urlDecrypted != nil { + videoPlaying = true + player?.play() + } + } + }) + .disabled(!canBePlayed) + } else { + videoDecryptionProgress() } - .disabled(!canBePlayed) - } else { - videoDecryptionProgress() } } } @@ -144,7 +181,6 @@ struct CIVideoView: View { private func videoView(_ player: AVPlayer, _ url: URL, _ file: CIFile, _ preview: UIImage, _ duration: Int) -> some View { let w = preview.size.width <= preview.size.height ? maxWidth * 0.75 : maxWidth - DispatchQueue.main.async { videoWidth = w } return ZStack(alignment: .topTrailing) { ZStack(alignment: .center) { let canBePlayed = !chatItem.chatDir.sent || file.fileStatus == CIFileStatus.sndComplete || (file.fileStatus == .sndStored && file.fileProtocol == .local) @@ -156,35 +192,34 @@ struct CIVideoView: View { videoPlaying = false } } - .fullScreenCover(isPresented: $showFullScreenPlayer) { - fullScreenPlayer(url) - } - .onTapGesture { - switch player.timeControlStatus { - case .playing: - player.pause() - videoPlaying = false - case .paused: - if canBePlayed { - showFullScreenPlayer = true + .modifier(PrivacyBlur(enabled: !videoPlaying, blurred: $blurred)) + .if(!blurred) { v in + v.simultaneousGesture(TapGesture().onEnded { + switch player.timeControlStatus { + case .playing: + player.pause() + videoPlaying = false + case .paused: + if canBePlayed { + showFullScreenPlayer = true + } + default: () } - default: () - } + }) } .onChange(of: m.activeCallViewIsCollapsed) { _ in showFullScreenPlayer = false } - if !videoPlaying { - Button { - m.stopPreviousRecPlay = url - player.play() - } label: { - playPauseIcon(canBePlayed ? "play.fill" : "play.slash") - } - .disabled(!canBePlayed) + if !videoPlaying && !blurred { + playPauseIcon(canBePlayed ? "play.fill" : "play.slash") + .simultaneousGesture(TapGesture().onEnded { + m.stopPreviousRecPlay = url + player.play() + }) + .disabled(!canBePlayed) } } - loadingIndicator() + fileStatusIcon() } .onAppear { addObserver(player, url) @@ -196,14 +231,53 @@ struct CIVideoView: View { } } + private func smallVideoViewEncrypted(_ file: CIFile, _ preview: UIImage) -> some View { + return ZStack(alignment: .topLeading) { + let canBePlayed = !chatItem.chatDir.sent || file.fileStatus == CIFileStatus.sndComplete || (file.fileStatus == .sndStored && file.fileProtocol == .local) + smallViewImageView(preview, file) + .onTapGesture { // this is shown in chat list, where onTapGesture works + decrypt(file: file) { + showFullScreenPlayer = urlDecrypted != nil + } + } + .onChange(of: m.activeCallViewIsCollapsed) { _ in + showFullScreenPlayer = false + } + if file.showStatusIconInSmallView { + // Show nothing + } else if !decryptionInProgress { + playPauseIcon(canBePlayed ? "play.fill" : "play.slash") + } else { + videoDecryptionProgress() + } + } + } + + private func smallVideoView(_ url: URL, _ file: CIFile, _ preview: UIImage) -> some View { + return ZStack(alignment: .topLeading) { + smallViewImageView(preview, file) + .onTapGesture { // this is shown in chat list, where onTapGesture works + showFullScreenPlayer = true + } + .onChange(of: m.activeCallViewIsCollapsed) { _ in + showFullScreenPlayer = false + } + + if !file.showStatusIconInSmallView { + playPauseIcon("play.fill") + } + } + } + + private func playPauseIcon(_ image: String, _ color: Color = .white) -> some View { Image(systemName: image) .resizable() .aspectRatio(contentMode: .fit) - .frame(width: 12, height: 12) + .frame(width: smallView ? 12 * sizeMultiplier * 1.6 : 12, height: smallView ? 12 * sizeMultiplier * 1.6 : 12) .foregroundColor(color) - .padding(.leading, 4) - .frame(width: 40, height: 40) + .padding(.leading, smallView ? 0 : 4) + .frame(width: 40 * sizeMultiplier, height: 40 * sizeMultiplier) .background(Color.black.opacity(0.35)) .clipShape(Circle()) } @@ -211,50 +285,57 @@ struct CIVideoView: View { private func videoDecryptionProgress(_ color: Color = .white) -> some View { ProgressView() .progressViewStyle(.circular) - .frame(width: 12, height: 12) + .frame(width: smallView ? 12 * sizeMultiplier : 12, height: smallView ? 12 * sizeMultiplier : 12) .tint(color) - .frame(width: 40, height: 40) + .frame(width: smallView ? 40 * sizeMultiplier * 0.9 : 40, height: smallView ? 40 * sizeMultiplier * 0.9 : 40) .background(Color.black.opacity(0.35)) .clipShape(Circle()) } - private func durationProgress() -> some View { - HStack { - Text("\(durationText(videoPlaying ? progress : duration))") - .foregroundColor(.white) - .font(.caption) - .padding(.vertical, 3) - .padding(.horizontal, 6) - .background(Color.black.opacity(0.35)) - .cornerRadius(10) - .padding([.top, .leading], 6) - - if let file = chatItem.file, !videoPlaying { - Text("\(ByteCountFormatter.string(fromByteCount: file.fileSize, countStyle: .binary))") - .foregroundColor(.white) - .font(.caption) - .padding(.vertical, 3) - .padding(.horizontal, 6) - .background(Color.black.opacity(0.35)) - .cornerRadius(10) - .padding(.top, 6) - } + private var fileSizeString: String { + if let file = chatItem.file, !videoPlaying { + " " + ByteCountFormatter.string(fromByteCount: file.fileSize, countStyle: .binary) + } else { + "" } } + private func durationProgress() -> some View { + Text((durationText(videoPlaying ? progress : duration)) + fileSizeString) + .invertedForegroundStyle() + .font(.caption) + .padding(.vertical, 6) + .padding(.horizontal, 12) + } + private func imageView(_ img: UIImage) -> some View { - let w = img.size.width <= img.size.height ? maxWidth * 0.75 : .infinity - DispatchQueue.main.async { videoWidth = w } + let w = img.size.width <= img.size.height ? maxWidth * 0.75 : maxWidth return ZStack(alignment: .topTrailing) { Image(uiImage: img) .resizable() .scaledToFit() - .frame(maxWidth: w) - loadingIndicator() + .frame(width: w) + .modifier(PrivacyBlur(blurred: $blurred)) + if !blurred || !showDownloadButton(chatItem.file?.fileStatus) { + fileStatusIcon() + } } } - @ViewBuilder private func loadingIndicator() -> some View { + private func smallViewImageView(_ img: UIImage, _ file: CIFile) -> some View { + ZStack(alignment: .center) { + Image(uiImage: img) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: maxWidth, height: maxWidth) + if file.showStatusIconInSmallView { + fileStatusIcon() + .allowsHitTesting(false) + } + } + } + + @ViewBuilder private func fileStatusIcon() -> some View { if let file = chatItem.file { switch file.fileStatus { case .sndStored: @@ -271,7 +352,16 @@ struct CIVideoView: View { } case .sndComplete: fileIcon("checkmark", 10, 13) case .sndCancelled: fileIcon("xmark", 10, 13) - case .sndError: fileIcon("xmark", 10, 13) + case let .sndError(sndFileError): + fileIcon("xmark", 10, 13) + .simultaneousGesture(TapGesture().onEnded { + showFileErrorAlert(sndFileError) + }) + case let .sndWarning(sndFileError): + fileIcon("exclamationmark.triangle.fill", 10, 13) + .simultaneousGesture(TapGesture().onEnded { + showFileErrorAlert(sndFileError, temporary: true) + }) case .rcvInvitation: fileIcon("arrow.down", 10, 13) case .rcvAccepted: fileIcon("ellipsis", 14, 11) case let .rcvTransfer(rcvProgress, rcvTotal): @@ -280,10 +370,20 @@ struct CIVideoView: View { } else { progressView() } + case .rcvAborted: fileIcon("exclamationmark.arrow.circlepath", 14, 11) + case .rcvComplete: EmptyView() case .rcvCancelled: fileIcon("xmark", 10, 13) - case .rcvError: fileIcon("xmark", 10, 13) + case let .rcvError(rcvFileError): + fileIcon("xmark", 10, 13) + .simultaneousGesture(TapGesture().onEnded { + showFileErrorAlert(rcvFileError) + }) + case let .rcvWarning(rcvFileError): + fileIcon("exclamationmark.triangle.fill", 10, 13) + .simultaneousGesture(TapGesture().onEnded { + showFileErrorAlert(rcvFileError, temporary: true) + }) case .invalid: fileIcon("questionmark", 10, 13) - default: EmptyView() } } } @@ -291,10 +391,10 @@ struct CIVideoView: View { private func fileIcon(_ icon: String, _ size: CGFloat, _ padding: CGFloat) -> some View { Image(systemName: icon) .resizable() + .invertedForegroundStyle() .aspectRatio(contentMode: .fit) .frame(width: size, height: size) - .foregroundColor(.white) - .padding(padding) + .padding(smallView ? 0 : padding) } private func progressView() -> some View { @@ -302,26 +402,24 @@ struct CIVideoView: View { .progressViewStyle(.circular) .frame(width: 16, height: 16) .tint(.white) - .padding(11) + .padding(smallView ? 0 : 11) } private func progressCircle(_ progress: Int64, _ total: Int64) -> some View { Circle() .trim(from: 0, to: Double(progress) / Double(total)) - .stroke( - Color(uiColor: .white), - style: StrokeStyle(lineWidth: 2) - ) + .stroke(style: StrokeStyle(lineWidth: 2)) + .invertedForegroundStyle() .rotationEffect(.degrees(-90)) .frame(width: 16, height: 16) - .padding([.trailing, .top], 11) + .padding([.trailing, .top], smallView ? 0 : 11) } // TODO encrypt: where file size is checked? - private func receiveFileIfValidSize(file: CIFile, receiveFile: @escaping (User, Int64, Bool) async -> Void) { + private func receiveFileIfValidSize(file: CIFile, receiveFile: @escaping (User, Int64, Bool, Bool) async -> Void) { Task { if let user = m.currentUser { - await receiveFile(user, file.fileId, false) + await receiveFile(user, file.fileId, false, false) } } } @@ -331,7 +429,7 @@ struct CIVideoView: View { Color.black.edgesIgnoringSafeArea(.all) VideoPlayer(player: fullPlayer) .overlay(alignment: .topLeading, content: { - Button(action: { showFullScreenPlayer = false }, + Button(action: { showFullScreenPlayer = false }, // this is used in full screen player, Button works here label: { Image(systemName: "multiply") .resizable() @@ -354,7 +452,8 @@ struct CIVideoView: View { ) .onAppear { DispatchQueue.main.asyncAfter(deadline: .now()) { - m.stopPreviousRecPlay = url + // Prevent feedback loop - setting `ChatModel`s property causes `onAppear` to be called on iOS17+ + if m.stopPreviousRecPlay != url { m.stopPreviousRecPlay = url } if let player = fullPlayer { player.play() var played = false @@ -391,10 +490,12 @@ struct CIVideoView: View { urlDecrypted = await file.fileSource?.decryptedGetOrCreate(&ChatModel.shared.filesToDelete) await MainActor.run { if let decrypted = urlDecrypted { - player = VideoPlayerView.getOrCreatePlayer(decrypted, false) + if !smallView { + player = VideoPlayerView.getOrCreatePlayer(decrypted, false) + } fullPlayer = AVPlayer(url: decrypted) } - decryptionInProgress = true + decryptionInProgress = false completed?() } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift index 3aecb65ebd..715e606a74 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift @@ -11,18 +11,30 @@ import SimpleXChat struct CIVoiceView: View { @ObservedObject var chat: Chat + @EnvironmentObject var theme: AppTheme var chatItem: ChatItem let recordingFile: CIFile? let duration: Int - @Binding var audioPlayer: AudioPlayer? - @Binding var playbackState: VoiceMessagePlaybackState - @Binding var playbackTime: TimeInterval? + @State var audioPlayer: AudioPlayer? = nil + @State var playbackState: VoiceMessagePlaybackState = .noPlayback + @State var playbackTime: TimeInterval? = nil + @Binding var allowMenu: Bool + var smallViewSize: CGFloat? @State private var seek: (TimeInterval) -> Void = { _ in } var body: some View { Group { - if chatItem.chatDir.sent { + if smallViewSize != nil { + HStack(spacing: 10) { + player() + playerTime() + .allowsHitTesting(false) + if .playing == playbackState || (playbackTime ?? 0) > 0 || !allowMenu { + playbackSlider() + } + } + } else if chatItem.chatDir.sent { VStack (alignment: .trailing, spacing: 6) { HStack { if .playing == playbackState || (playbackTime ?? 0) > 0 || !allowMenu { @@ -53,7 +65,13 @@ struct CIVoiceView: View { } private func player() -> some View { - VoiceMessagePlayer( + let sizeMultiplier: CGFloat = if let sz = smallViewSize { + voiceMessageSizeBasedOnSquareSize(sz) / 56 + } else { + 1 + } + return VoiceMessagePlayer( + chat: chat, chatItem: chatItem, recordingFile: recordingFile, recordingTime: TimeInterval(duration), @@ -62,7 +80,8 @@ struct CIVoiceView: View { audioPlayer: $audioPlayer, playbackState: $playbackState, playbackTime: $playbackTime, - allowMenu: $allowMenu + allowMenu: $allowMenu, + sizeMultiplier: sizeMultiplier ) } @@ -72,7 +91,7 @@ struct CIVoiceView: View { playbackState: $playbackState, playbackTime: $playbackTime ) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } private func playbackSlider() -> some View { @@ -89,10 +108,11 @@ struct CIVoiceView: View { allowMenu = true } } + .tint(theme.colors.primary) } private func metaView() -> some View { - CIMetaView(chat: chat, chatItem: chatItem) + CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary) } } @@ -117,8 +137,9 @@ struct VoiceMessagePlayerTime: View { } struct VoiceMessagePlayer: View { + @ObservedObject var chat: Chat @EnvironmentObject var chatModel: ChatModel - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme var chatItem: ChatItem var recordingFile: CIFile? var recordingTime: TimeInterval @@ -128,23 +149,49 @@ struct VoiceMessagePlayer: View { @Binding var audioPlayer: AudioPlayer? @Binding var playbackState: VoiceMessagePlaybackState @Binding var playbackTime: TimeInterval? + @Binding var allowMenu: Bool + var sizeMultiplier: CGFloat var body: some View { ZStack { if let recordingFile = recordingFile { switch recordingFile.fileStatus { - case .sndStored: playbackButton() - case .sndTransfer: playbackButton() + case .sndStored: + if recordingFile.fileProtocol == .local { + playbackButton() + } else { + loadingIcon() + } + case .sndTransfer: loadingIcon() case .sndComplete: playbackButton() case .sndCancelled: playbackButton() - case .sndError: playbackButton() - case .rcvInvitation: downloadButton(recordingFile) + case let .sndError(sndFileError): + fileStatusIcon("multiply", 14) + .simultaneousGesture(TapGesture().onEnded { + showFileErrorAlert(sndFileError) + }) + case let .sndWarning(sndFileError): + fileStatusIcon("exclamationmark.triangle.fill", 16) + .simultaneousGesture(TapGesture().onEnded { + showFileErrorAlert(sndFileError, temporary: true) + }) + case .rcvInvitation: downloadButton(recordingFile, "play.fill") case .rcvAccepted: loadingIcon() case .rcvTransfer: loadingIcon() + case .rcvAborted: downloadButton(recordingFile, "exclamationmark.arrow.circlepath") case .rcvComplete: playbackButton() case .rcvCancelled: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel)) - case .rcvError: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel)) + case let .rcvError(rcvFileError): + fileStatusIcon("multiply", 14) + .simultaneousGesture(TapGesture().onEnded { + showFileErrorAlert(rcvFileError) + }) + case let .rcvWarning(rcvFileError): + fileStatusIcon("exclamationmark.triangle.fill", 16) + .simultaneousGesture(TapGesture().onEnded { + showFileErrorAlert(rcvFileError, temporary: true) + }) case .invalid: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel)) } } else { @@ -152,84 +199,126 @@ struct VoiceMessagePlayer: View { } } .onAppear { + if audioPlayer == nil { + let small = sizeMultiplier != 1 + audioPlayer = small ? VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)]?.audioPlayer : VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)]?.audioPlayer + playbackState = (small ? VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)]?.playbackState : VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)]?.playbackState) ?? .noPlayback + playbackTime = small ? VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)]?.playbackTime : VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)]?.playbackTime + } seek = { to in audioPlayer?.seek(to) } - audioPlayer?.onTimer = { playbackTime = $0 } + let audioPath: URL? = if let recordingSource = getLoadedFileSource(recordingFile) { + getAppFilePath(recordingSource.filePath) + } else { + nil + } + let chatId = chatModel.chatId + let userId = chatModel.currentUser?.userId + audioPlayer?.onTimer = { + playbackTime = $0 + notifyStateChange() + // Manual check here is needed because when this view is not visible, SwiftUI don't react on stopPreviousRecPlay, chatId and current user changes and audio keeps playing when it should stop + if (audioPath != nil && chatModel.stopPreviousRecPlay != audioPath) || chatModel.chatId != chatId || chatModel.currentUser?.userId != userId { + stopPlayback() + } + } audioPlayer?.onFinishPlayback = { playbackState = .noPlayback playbackTime = TimeInterval(0) + notifyStateChange() + } + // One voice message was paused, then scrolled far from it, started to play another one, drop to stopped state + if let audioPath, chatModel.stopPreviousRecPlay != audioPath { + stopPlayback() } } .onChange(of: chatModel.stopPreviousRecPlay) { it in if let recordingFileName = getLoadedFileSource(recordingFile)?.filePath, chatModel.stopPreviousRecPlay != getAppFilePath(recordingFileName) { - audioPlayer?.stop() - playbackState = .noPlayback - playbackTime = TimeInterval(0) + stopPlayback() } } .onChange(of: playbackState) { state in allowMenu = state == .paused || state == .noPlayback + // Notify activeContentPreview in ChatPreviewView that playback is finished + if state == .noPlayback, let recordingFileName = getLoadedFileSource(recordingFile)?.filePath, + chatModel.stopPreviousRecPlay == getAppFilePath(recordingFileName) { + chatModel.stopPreviousRecPlay = nil + } + } + .onChange(of: chatModel.chatId) { _ in + stopPlayback() + } + .onDisappear { + if sizeMultiplier == 1 && chatModel.chatId == nil { + stopPlayback() + } } } - @ViewBuilder private func playbackButton() -> some View { - switch playbackState { - case .noPlayback: - Button { - if let recordingSource = getLoadedFileSource(recordingFile) { - startPlayback(recordingSource) + private func playbackButton() -> some View { + let icon = switch playbackState { + case .noPlayback: "play.fill" + case .playing: "pause.fill" + case .paused: "play.fill" + } + return playPauseIcon(icon, theme.colors.primary) + .simultaneousGesture(TapGesture().onEnded { _ in + switch playbackState { + case .noPlayback: + if let recordingSource = getLoadedFileSource(recordingFile) { + startPlayback(recordingSource) + } + case .playing: + audioPlayer?.pause() + playbackState = .paused + notifyStateChange() + case .paused: + audioPlayer?.play() + playbackState = .playing + notifyStateChange() } - } label: { - playPauseIcon("play.fill") - } - case .playing: - Button { - audioPlayer?.pause() - playbackState = .paused - } label: { - playPauseIcon("pause.fill") - } - case .paused: - Button { - audioPlayer?.play() - playbackState = .playing - } label: { - playPauseIcon("play.fill") - } - } + }) } - private func playPauseIcon(_ image: String, _ color: Color = .accentColor) -> some View { + private func playPauseIcon(_ image: String, _ color: Color/* = .accentColor*/) -> some View { ZStack { Image(systemName: image) .resizable() .aspectRatio(contentMode: .fit) - .frame(width: 20, height: 20) + .frame(width: 20 * sizeMultiplier, height: 20 * sizeMultiplier) .foregroundColor(color) .padding(.leading, image == "play.fill" ? 4 : 0) - .frame(width: 56, height: 56) - .background(showBackground ? chatItemFrameColor(chatItem, colorScheme) : .clear) + .frame(width: 56 * sizeMultiplier, height: 56 * sizeMultiplier) + .background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear) .clipShape(Circle()) if recordingTime > 0 { ProgressCircle(length: recordingTime, progress: $playbackTime) - .frame(width: 53, height: 53) // this + ProgressCircle lineWidth = background circle diameter + .frame(width: 53 * sizeMultiplier, height: 53 * sizeMultiplier) // this + ProgressCircle lineWidth = background circle diameter } } } - private func downloadButton(_ recordingFile: CIFile) -> some View { - Button { - Task { - if let user = chatModel.currentUser { - await receiveFile(user: user, fileId: recordingFile.fileId) + private func downloadButton(_ recordingFile: CIFile, _ icon: String) -> some View { + playPauseIcon(icon, theme.colors.primary) + .simultaneousGesture(TapGesture().onEnded { + Task { + if let user = chatModel.currentUser { + await receiveFile(user: user, fileId: recordingFile.fileId) + } } - } - } label: { - playPauseIcon("play.fill") + }) + } + + func notifyStateChange() { + if sizeMultiplier != 1 { + VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)] = VoiceItemState(audioPlayer: audioPlayer, playbackState: playbackState, playbackTime: playbackTime) + } else { + VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)] = VoiceItemState(audioPlayer: audioPlayer, playbackState: playbackState, playbackTime: playbackTime) } } private struct ProgressCircle: View { + @EnvironmentObject var theme: AppTheme var length: TimeInterval @Binding var progress: TimeInterval? @@ -237,7 +326,7 @@ struct VoiceMessagePlayer: View { Circle() .trim(from: 0, to: ((progress ?? TimeInterval(0)) / length)) .stroke( - Color.accentColor, + theme.colors.primary, style: StrokeStyle(lineWidth: 3) ) .rotationEffect(.degrees(-90)) @@ -245,26 +334,103 @@ struct VoiceMessagePlayer: View { } } + private func fileStatusIcon(_ image: String, _ size: CGFloat) -> some View { + Image(systemName: image) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: size * sizeMultiplier, height: size * sizeMultiplier) + .foregroundColor(Color(uiColor: .tertiaryLabel)) + .frame(width: 56 * sizeMultiplier, height: 56 * sizeMultiplier) + .background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear) + .clipShape(Circle()) + } + private func loadingIcon() -> some View { ProgressView() - .frame(width: 30, height: 30) - .frame(width: 56, height: 56) - .background(showBackground ? chatItemFrameColor(chatItem, colorScheme) : .clear) + .frame(width: 30 * sizeMultiplier, height: 30 * sizeMultiplier) + .frame(width: 56 * sizeMultiplier, height: 56 * sizeMultiplier) + .background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear) .clipShape(Circle()) } private func startPlayback(_ recordingSource: CryptoFile) { - chatModel.stopPreviousRecPlay = getAppFilePath(recordingSource.filePath) + let audioPath = getAppFilePath(recordingSource.filePath) + let chatId = chatModel.chatId + let userId = chatModel.currentUser?.userId + chatModel.stopPreviousRecPlay = audioPath audioPlayer = AudioPlayer( - onTimer: { playbackTime = $0 }, + onTimer: { + playbackTime = $0 + notifyStateChange() + // Manual check here is needed because when this view is not visible, SwiftUI don't react on stopPreviousRecPlay, chatId and current user changes and audio keeps playing when it should stop + if chatModel.stopPreviousRecPlay != audioPath || chatModel.chatId != chatId || chatModel.currentUser?.userId != userId { + stopPlayback() + } + }, onFinishPlayback: { playbackState = .noPlayback playbackTime = TimeInterval(0) + notifyStateChange() } ) audioPlayer?.start(fileSource: recordingSource, at: playbackTime) playbackState = .playing + notifyStateChange() } + + private func stopPlayback() { + audioPlayer?.stop() + playbackState = .noPlayback + playbackTime = TimeInterval(0) + notifyStateChange() + } +} + +@inline(__always) +func voiceMessageSizeBasedOnSquareSize(_ squareSize: CGFloat) -> CGFloat { + let squareToCircleRatio = 0.935 + return squareSize + squareSize * (1 - squareToCircleRatio) +} + +class VoiceItemState { + var audioPlayer: AudioPlayer? + var playbackState: VoiceMessagePlaybackState + var playbackTime: TimeInterval? + + init(audioPlayer: AudioPlayer? = nil, playbackState: VoiceMessagePlaybackState, playbackTime: TimeInterval? = nil) { + self.audioPlayer = audioPlayer + self.playbackState = playbackState + self.playbackTime = playbackTime + } + + @inline(__always) + static func id(_ chat: Chat, _ chatItem: ChatItem) -> String { + "\(chat.id) \(chatItem.id)" + } + + @inline(__always) + static func id(_ chatInfo: ChatInfo, _ chatItem: ChatItem) -> String { + "\(chatInfo.id) \(chatItem.id)" + } + + static func stopVoiceInSmallView(_ chatInfo: ChatInfo, _ chatItem: ChatItem) { + let id = id(chatInfo, chatItem) + if let item = smallView[id] { + item.audioPlayer?.stop() + ChatModel.shared.stopPreviousRecPlay = nil + } + } + + static func stopVoiceInChatView(_ chatInfo: ChatInfo, _ chatItem: ChatItem) { + let id = id(chatInfo, chatItem) + if let item = chatView[id] { + item.audioPlayer?.stop() + ChatModel.shared.stopPreviousRecPlay = nil + } + } + + static var smallView: [String: VoiceItemState] = [:] + static var chatView: [String: VoiceItemState] = [:] } struct CIVoiceView_Previews: PreviewProvider { @@ -289,15 +455,12 @@ struct CIVoiceView_Previews: PreviewProvider { chatItem: ChatItem.getVoiceMsgContentSample(), recordingFile: CIFile.getSample(fileName: "voice.m4a", fileSize: 65536, fileStatus: .rcvComplete), duration: 30, - audioPlayer: .constant(nil), - playbackState: .constant(.playing), - playbackTime: .constant(TimeInterval(20)), allowMenu: Binding.constant(true) ) - ChatItemView(chat: Chat.sampleData, chatItem: sentVoiceMessage, revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(), revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - ChatItemView(chat: Chat.sampleData, chatItem: voiceMessageWtFile, revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) + ChatItemView(chat: Chat.sampleData, chatItem: sentVoiceMessage, scrollToItemId: { _ in }, allowMenu: .constant(true)) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(), scrollToItemId: { _ in }, allowMenu: .constant(true)) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItemId: { _ in }, allowMenu: .constant(true)) + ChatItemView(chat: Chat.sampleData, chatItem: voiceMessageWtFile, scrollToItemId: { _ in }, allowMenu: .constant(true)) } .previewLayout(.fixed(width: 360, height: 360)) } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/DeletedItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/DeletedItemView.swift index 4763707421..ed2340b6c4 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/DeletedItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/DeletedItemView.swift @@ -10,22 +10,21 @@ import SwiftUI import SimpleXChat struct DeletedItemView: View { - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme @ObservedObject var chat: Chat var chatItem: ChatItem var body: some View { HStack(alignment: .bottom, spacing: 0) { Text(chatItem.content.text) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .italic() - CIMetaView(chat: chat, chatItem: chatItem) + CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary) .padding(.horizontal, 12) } .padding(.leading, 12) .padding(.vertical, 6) - .background(chatItemFrameColor(chatItem, colorScheme)) - .cornerRadius(18) + .background(chatItemFrameColor(chatItem, theme)) .textSelection(.disabled) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/EmojiItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/EmojiItemView.swift index f57e45fed0..250d9d5636 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/EmojiItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/EmojiItemView.swift @@ -11,6 +11,7 @@ import SimpleXChat struct EmojiItemView: View { @ObservedObject var chat: Chat + @EnvironmentObject var theme: AppTheme var chatItem: ChatItem var body: some View { @@ -18,7 +19,7 @@ struct EmojiItemView: View { emojiText(chatItem.content.text) .padding(.top, 8) .padding(.horizontal, 6) - CIMetaView(chat: chat, chatItem: chatItem) + CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary) .padding(.bottom, 8) .padding(.horizontal, 12) } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/FramedCIVoiceView.swift b/apps/ios/Shared/Views/Chat/ChatItem/FramedCIVoiceView.swift index af5c917dc8..f4e2a4135a 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/FramedCIVoiceView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/FramedCIVoiceView.swift @@ -12,21 +12,24 @@ import SwiftUI import SimpleXChat struct FramedCIVoiceView: View { + @EnvironmentObject var theme: AppTheme + @ObservedObject var chat: Chat var chatItem: ChatItem let recordingFile: CIFile? let duration: Int - + + @State var audioPlayer: AudioPlayer? = nil + @State var playbackState: VoiceMessagePlaybackState = .noPlayback + @State var playbackTime: TimeInterval? = nil + @Binding var allowMenu: Bool - - @Binding var audioPlayer: AudioPlayer? - @Binding var playbackState: VoiceMessagePlaybackState - @Binding var playbackTime: TimeInterval? - + @State private var seek: (TimeInterval) -> Void = { _ in } var body: some View { HStack { VoiceMessagePlayer( + chat: chat, chatItem: chatItem, recordingFile: recordingFile, recordingTime: TimeInterval(duration), @@ -35,14 +38,15 @@ struct FramedCIVoiceView: View { audioPlayer: $audioPlayer, playbackState: $playbackState, playbackTime: $playbackTime, - allowMenu: $allowMenu + allowMenu: $allowMenu, + sizeMultiplier: 1 ) VoiceMessagePlayerTime( recordingTime: TimeInterval(duration), playbackState: $playbackState, playbackTime: $playbackTime ) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .frame(width: 50, alignment: .leading) if .playing == playbackState || (playbackTime ?? 0) > 0 || !allowMenu { playbackSlider() @@ -88,12 +92,13 @@ struct FramedCIVoiceView_Previews: PreviewProvider { file: CIFile.getSample(fileStatus: .sndComplete) ) Group { - ChatItemView(chat: Chat.sampleData, chatItem: sentVoiceMessage, revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there"), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there", fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: voiceMessageWithQuote, revealed: Binding.constant(false)) + ChatItemView(chat: Chat.sampleData, chatItem: sentVoiceMessage, scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there"), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(text: "Hello there", fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: voiceMessageWithQuote, scrollToItemId: { _ in }) } + .environment(\.revealed, false) .previewLayout(.fixed(width: 360, height: 360)) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift index 3475e7a8b6..b27d266d8a 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift @@ -9,38 +9,36 @@ import SwiftUI import SimpleXChat -let notesChatColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.21) -let notesChatColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.19) -let sentColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.12) -let sentColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.17) -private let sentQuoteColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.11) -private let sentQuoteColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.09) - struct FramedItemView: View { @EnvironmentObject var m: ChatModel - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme @ObservedObject var chat: Chat var chatItem: ChatItem - @Binding var revealed: Bool + var scrollToItemId: (ChatItem.ID) -> Void + var preview: UIImage? var maxWidth: CGFloat = .infinity - @State var scrollProxy: ScrollViewProxy? = nil @State var msgWidth: CGFloat = 0 - @State var imgWidth: CGFloat? = nil - @State var videoWidth: CGFloat? = nil - @State var metaColor = Color.secondary + var imgWidth: CGFloat? = nil + var videoWidth: CGFloat? = nil + @State private var useWhiteMetaColor: Bool = false @State var showFullScreenImage = false @Binding var allowMenu: Bool - @State private var showSecrets = false - @State private var showQuoteSecrets = false - - @Binding var audioPlayer: AudioPlayer? - @Binding var playbackState: VoiceMessagePlaybackState - @Binding var playbackTime: TimeInterval? + @State private var showFullscreenGallery: Bool = false var body: some View { let v = ZStack(alignment: .bottomTrailing) { VStack(alignment: .leading, spacing: 0) { - if let di = chatItem.meta.itemDeleted { + if chatItem.isReport { + if chatItem.meta.itemDeleted == nil { + let txt = chatItem.chatDir.sent ? + Text("Only you and moderators see it") : + Text("Only sender and moderators see it") + + framedItemHeader(icon: "flag", iconColor: .red, caption: txt.italic()) + } else { + framedItemHeader(icon: "flag", caption: Text("archived report").italic()) + } + } else if let di = chatItem.meta.itemDeleted { switch di { case let .moderated(_, byGroupMember): framedItemHeader(icon: "flag", caption: Text("moderated by \(byGroupMember.displayName)").italic()) @@ -57,40 +55,53 @@ struct FramedItemView: View { if let qi = chatItem.quotedItem { ciQuoteView(qi) - .onTapGesture { - if let proxy = scrollProxy, - let ci = m.reversedChatItems.first(where: { $0.id == qi.itemId }) { + .simultaneousGesture(TapGesture().onEnded { + if let ci = ItemsModel.shared.reversedChatItems.first(where: { $0.id == qi.itemId }) { withAnimation { - proxy.scrollTo(ci.viewId, anchor: .bottom) + scrollToItemId(ci.id) } + } else if let id = qi.itemId { + scrollToItemId(id) + } else { + showQuotedItemDoesNotExistAlert() } - } + }) + } else if let itemForwarded = chatItem.meta.itemForwarded { + framedItemHeader(icon: "arrowshape.turn.up.forward", caption: Text(itemForwarded.text(chat.chatInfo.chatType)).italic(), pad: true) } - ChatItemContentView(chat: chat, chatItem: chatItem, revealed: $revealed, msgContentView: framedMsgContentView) + ChatItemContentView(chat: chat, chatItem: chatItem, msgContentView: framedMsgContentView) .padding(chatItem.content.msgContent != nil ? 0 : 4) .overlay(DetermineWidth()) } - .onPreferenceChange(MetaColorPreferenceKey.self) { metaColor = $0 } - if chatItem.content.msgContent != nil { - CIMetaView(chat: chat, chatItem: chatItem, metaColor: metaColor) - .padding(.horizontal, 12) - .padding(.bottom, 6) - .overlay(DetermineWidth()) - .accessibilityLabel("") + if let content = chatItem.content.msgContent { + CIMetaView( + chat: chat, + chatItem: chatItem, + metaColor: theme.colors.secondary, + invertedMaterial: useWhiteMetaColor + ) + .padding(.horizontal, 12) + .padding(.bottom, 6) + .overlay(DetermineWidth()) + .accessibilityLabel("") } } - .background(chatItemFrameColorMaybeImageOrVideo(chatItem, colorScheme)) - .cornerRadius(18) + .background { chatItemFrameColorMaybeImageOrVideo(chatItem, theme).modifier(ChatTailPadding()) } .onPreferenceChange(DetermineWidth.Key.self) { msgWidth = $0 } - switch chatItem.meta.itemStatus { - case .sndErrorAuth: - v.onTapGesture { msgDeliveryError("Most likely this contact has deleted the connection with you.") } - case let .sndError(agentError): - v.onTapGesture { msgDeliveryError("Unexpected error: \(agentError)") } - default: v + if let (title, text) = chatItem.meta.itemStatus.statusInfo { + v.simultaneousGesture(TapGesture().onEnded { + AlertManager.shared.showAlert( + Alert( + title: Text(title), + message: Text(text) + ) + ) + }) + } else { + v } } @@ -107,40 +118,46 @@ struct FramedItemView: View { .padding(.bottom, 2) } else { switch (chatItem.content.msgContent) { - case let .image(text, image): - CIImageView(chatItem: chatItem, image: image, maxWidth: maxWidth, imgWidth: $imgWidth, scrollProxy: scrollProxy, metaColor: metaColor) + case let .image(text, _): + CIImageView(chatItem: chatItem, scrollToItemId: scrollToItemId, preview: preview, maxWidth: maxWidth, imgWidth: imgWidth, showFullScreenImage: $showFullscreenGallery) .overlay(DetermineWidth()) if text == "" && !chatItem.meta.isLive { Color.clear .frame(width: 0, height: 0) - .preference( - key: MetaColorPreferenceKey.self, - value: .white - ) + .onAppear { + useWhiteMetaColor = true + } + .onDisappear { + useWhiteMetaColor = false + } } else { ciMsgContentView(chatItem) } - case let .video(text, image, duration): - CIVideoView(chatItem: chatItem, image: image, duration: duration, maxWidth: maxWidth, videoWidth: $videoWidth, scrollProxy: scrollProxy) + case let .video(text, _, duration): + CIVideoView(chatItem: chatItem, preview: preview, duration: duration, maxWidth: maxWidth, videoWidth: videoWidth, showFullscreenPlayer: $showFullscreenGallery) .overlay(DetermineWidth()) if text == "" && !chatItem.meta.isLive { Color.clear .frame(width: 0, height: 0) - .preference( - key: MetaColorPreferenceKey.self, - value: .white - ) + .onAppear { + useWhiteMetaColor = true + } + .onDisappear { + useWhiteMetaColor = false + } } else { ciMsgContentView(chatItem) } case let .voice(text, duration): - FramedCIVoiceView(chatItem: chatItem, recordingFile: chatItem.file, duration: duration, allowMenu: $allowMenu, audioPlayer: $audioPlayer, playbackState: $playbackState, playbackTime: $playbackTime) + FramedCIVoiceView(chat: chat, chatItem: chatItem, recordingFile: chatItem.file, duration: duration, allowMenu: $allowMenu) .overlay(DetermineWidth()) if text != "" { ciMsgContentView(chatItem) } case let .file(text): ciFileView(chatItem, text) + case let .report(text, reason): + ciMsgContentView(chatItem, txtPrefix: reason.attrString) case let .link(_, preview): CILinkView(linkPreview: preview) ciMsgContentView(chatItem) @@ -155,33 +172,27 @@ struct FramedItemView: View { } } } - - private func msgDeliveryError(_ err: LocalizedStringKey) { - AlertManager.shared.showAlertMsg( - title: "Message delivery error", - message: err - ) - } - @ViewBuilder func framedItemHeader(icon: String? = nil, caption: Text) -> some View { + @ViewBuilder func framedItemHeader(icon: String? = nil, iconColor: Color? = nil, caption: Text, pad: Bool = false) -> some View { let v = HStack(spacing: 6) { if let icon = icon { Image(systemName: icon) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 14, height: 14) + .foregroundColor(iconColor ?? theme.colors.secondary) } caption .font(.caption) .lineLimit(1) } - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .padding(.horizontal, 12) .padding(.top, 6) - .padding(.bottom, chatItem.quotedItem == nil ? 6 : 0) // TODO think how to regroup + .padding(.bottom, pad || (chatItem.quotedItem == nil && chatItem.meta.itemForwarded == nil) ? 6 : 0) .overlay(DetermineWidth()) .frame(minWidth: msgWidth, alignment: .leading) - .background(chatItemFrameContextColor(chatItem, colorScheme)) + .background(chatItemFrameContextColor(chatItem, theme)) if let mediaWidth = maxMediaWidth(), mediaWidth < maxWidth { v.frame(maxWidth: mediaWidth, alignment: .leading) } else { @@ -190,11 +201,11 @@ struct FramedItemView: View { } @ViewBuilder private func ciQuoteView(_ qi: CIQuote) -> some View { + let backgroundColor = chatItemFrameContextColor(chatItem, theme) let v = ZStack(alignment: .topTrailing) { switch (qi.content) { case let .image(_, image): - if let data = Data(base64Encoded: dropImagePrefix(image)), - let uiImage = UIImage(data: data) { + if let uiImage = imageFromBase64(image) { ciQuotedMsgView(qi) .padding(.trailing, 70).frame(minWidth: msgWidth, alignment: .leading) Image(uiImage: uiImage) @@ -206,8 +217,7 @@ struct FramedItemView: View { ciQuotedMsgView(qi) } case let .video(_, image, _): - if let data = Data(base64Encoded: dropImagePrefix(image)), - let uiImage = UIImage(data: data) { + if let uiImage = imageFromBase64(image) { ciQuotedMsgView(qi) .padding(.trailing, 70).frame(minWidth: msgWidth, alignment: .leading) Image(uiImage: uiImage) @@ -233,8 +243,8 @@ struct FramedItemView: View { // if enable this always, size of the framed voice message item will be incorrect after end of playback .overlay { if case .voice = chatItem.content.msgContent {} else { DetermineWidth() } } .frame(minWidth: msgWidth, alignment: .leading) - .background(chatItemFrameContextColor(chatItem, colorScheme)) - + .background(backgroundColor) + .environment(\.containerBackground, UIColor(backgroundColor)) if let mediaWidth = maxMediaWidth(), mediaWidth < maxWidth { v.frame(maxWidth: mediaWidth, alignment: .leading) } else { @@ -246,7 +256,10 @@ struct FramedItemView: View { Group { if let sender = qi.getSender(membership()) { VStack(alignment: .leading, spacing: 2) { - Text(sender).font(.caption).foregroundColor(.secondary) + Text(sender) + .font(.caption) + .foregroundColor(qi.chatDir == .groupSnd ? .accentColor : theme.colors.secondary) + .lineLimit(1) ciQuotedMsgTextView(qi, lines: 2) } } else { @@ -257,14 +270,12 @@ struct FramedItemView: View { .padding(.top, 6) .padding(.horizontal, 12) } - + + @inline(__always) private func ciQuotedMsgTextView(_ qi: CIQuote, lines: Int) -> some View { - toggleSecrets(qi.formattedText, $showQuoteSecrets, - MsgContentView(chat: chat, text: qi.text, formattedText: qi.formattedText, showSecrets: showQuoteSecrets) - .lineLimit(lines) - .font(.subheadline) - .padding(.bottom, 6) - ) + MsgContentView(chat: chat, text: qi.text, formattedText: qi.formattedText, textStyle: .subheadline) + .lineLimit(lines) + .padding(.bottom, 6) } private func ciQuoteIconView(_ image: String) -> some View { @@ -284,24 +295,27 @@ struct FramedItemView: View { } } - @ViewBuilder private func ciMsgContentView(_ ci: ChatItem) -> some View { + @ViewBuilder private func ciMsgContentView(_ ci: ChatItem, txtPrefix: NSAttributedString? = nil) -> some View { let text = ci.meta.isLive ? ci.content.msgContent?.text ?? ci.text : ci.text let rtl = isRightToLeft(text) let ft = text == "" ? [] : ci.formattedText - let v = toggleSecrets(ft, $showSecrets, MsgContentView( + let v = MsgContentView( chat: chat, text: text, formattedText: ft, + textStyle: .body, meta: ci.meta, + mentions: ci.mentions, + userMemberId: chat.chatInfo.groupInfo?.membership.memberId, rightToLeft: rtl, - showSecrets: showSecrets - )) + prefix: txtPrefix + ) + .environment(\.containerBackground, UIColor(chatItemFrameColor(ci, theme))) .multilineTextAlignment(rtl ? .trailing : .leading) .padding(.vertical, 6) .padding(.horizontal, 12) .overlay(DetermineWidth()) .frame(minWidth: 0, alignment: .leading) - .textSelection(.enabled) if let mediaWidth = maxMediaWidth(), mediaWidth < maxWidth { v.frame(maxWidth: mediaWidth, alignment: .leading) @@ -327,13 +341,12 @@ struct FramedItemView: View { return videoWidth } } -} -@ViewBuilder func toggleSecrets(_ ft: [FormattedText]?, _ showSecrets: Binding, _ v: V) -> some View { - if let ft = ft, ft.contains(where: { $0.isSecret }) { - v.onTapGesture { showSecrets.wrappedValue.toggle() } - } else { - v + private func showQuotedItemDoesNotExistAlert() { + AlertManager.shared.showAlertMsg( + title: "No message", + message: "This message was deleted or not received yet." + ) } } @@ -344,51 +357,44 @@ func isRightToLeft(_ s: String) -> Bool { return false } -private struct MetaColorPreferenceKey: PreferenceKey { - static var defaultValue = Color.secondary - static func reduce(value: inout Color, nextValue: () -> Color) { - value = nextValue() - } -} - func onlyImageOrVideo(_ ci: ChatItem) -> Bool { if case let .image(text, _) = ci.content.msgContent { - return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && text == "" + return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && ci.meta.itemForwarded == nil && text == "" } else if case let .video(text, _, _) = ci.content.msgContent { - return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && text == "" + return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && ci.meta.itemForwarded == nil && text == "" } return false } -func chatItemFrameColorMaybeImageOrVideo(_ ci: ChatItem, _ colorScheme: ColorScheme) -> Color { +func chatItemFrameColorMaybeImageOrVideo(_ ci: ChatItem, _ theme: AppTheme) -> Color { onlyImageOrVideo(ci) ? Color.clear - : chatItemFrameColor(ci, colorScheme) + : chatItemFrameColor(ci, theme) } -func chatItemFrameColor(_ ci: ChatItem, _ colorScheme: ColorScheme) -> Color { +func chatItemFrameColor(_ ci: ChatItem, _ theme: AppTheme) -> Color { ci.chatDir.sent - ? (colorScheme == .light ? sentColorLight : sentColorDark) - : Color(uiColor: .tertiarySystemGroupedBackground) + ? theme.appColors.sentMessage + : theme.appColors.receivedMessage } -func chatItemFrameContextColor(_ ci: ChatItem, _ colorScheme: ColorScheme) -> Color { +func chatItemFrameContextColor(_ ci: ChatItem, _ theme: AppTheme) -> Color { ci.chatDir.sent - ? (colorScheme == .light ? sentQuoteColorLight : sentQuoteColorDark) - : Color(uiColor: .quaternarySystemFill) + ? theme.appColors.sentQuote + : theme.appColors.receivedQuote } struct FramedItemView_Previews: PreviewProvider { static var previews: some View { Group{ - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello"), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello", quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directSnd)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directRcv)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "👍", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "Hello too", chatDir: .directRcv)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this covers -"), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this text has the time on the same line "), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "https://simplex.chat"), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "chaT@simplex.chat"), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello"), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello", quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directSnd)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directRcv)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "👍", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "Hello too", chatDir: .directRcv)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this covers -"), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this text has the time on the same line "), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "https://simplex.chat"), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "chaT@simplex.chat"), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) } .previewLayout(.fixed(width: 360, height: 200)) } @@ -397,17 +403,18 @@ struct FramedItemView_Previews: PreviewProvider { struct FramedItemView_Edited_Previews: PreviewProvider { static var previews: some View { Group { - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemEdited: true), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello", quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directSnd), itemEdited: true), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directRcv), itemEdited: true), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "👍", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "Hello too", chatDir: .directRcv), itemEdited: true), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this covers -", .rcvRead, itemEdited: true), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this text has the time on the same line ", .rcvRead, itemEdited: true), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "https://simplex.chat", .rcvRead, itemEdited: true), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "chaT@simplex.chat", .rcvRead, itemEdited: true), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello", quotedItem: CIQuote.getSample(1, .now, "hi there hello hello hello ther hello hello", chatDir: .directSnd, image: "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAuKADAAQAAAABAAAAYAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAYAC4AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAgEBAgMCAgIDBAMDAwMEBgQEBAQEBgcGBgYGBgYHBwcHBwcHBwgICAgICAkJCQkJCwsLCwsLCwsLC//bAEMBAgICAwMDBQMDBQsIBggLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLC//dAAQADP/aAAwDAQACEQMRAD8A/v4ooooAKKKKACiiigAooooAKK+CP2vP+ChXwZ/ZPibw7dMfEHi2VAYdGs3G9N33TO/IiU9hgu3ZSOa/NzXNL/4KJ/td6JJ49+NXiq2+Cvw7kG/ZNKbDMLcjKblmfI/57SRqewrwMdxBRo1HQoRdWqt1HaP+KT0j838j7XKOCMXiqEcbjKkcPh5bSne8/wDr3BXlN+is+5+43jb45/Bf4bs0fj/xZpGjSL1jvL2KF/8AvlmDfpXjH/DfH7GQuPsv/CydD35x/wAfIx+fT9a/AO58D/8ABJj4UzvF4v8AFfif4l6mp/evpkfkWzP3w2Isg+omb61X/wCF0/8ABJr/AI9f+FQeJPL6ed9vbzPrj7ZivnavFuIT+KhHyc5Sf3wjY+7w/hlgZQv7PF1P70aUKa+SqTUvwP6afBXx2+CnxIZYvAHi3R9ZkfpHZ3sUz/8AfKsW/SvVq/lItvBf/BJX4rTLF4V8UeJ/hpqTH91JqUfn2yv2y2JcD3MqfUV9OaFon/BRH9krQ4vH3wI8XW3xq+HkY3+XDKb/ABCvJxHuaZMDr5Ergd1ruwvFNVrmq0VOK3lSkp29Y6SS+R5GY+HGGi1DD4qVKo9oYmm6XN5RqK9Nvsro/obor4A/ZC/4KH/Bv9qxV8MLnw54vjU+bo9443SFPvG3k4EoHdcB17rjmvv+vqcHjaGKpKth5qUX1X9aPyZ+b5rlOMy3ESwmOpOFRdH+aezT6NXTCiiiuo84KKKKACiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/Q/v4ooooAKKKKACiiigAr8tf+ChP7cWs/BEWfwD+A8R1P4k+JQkUCQr5rWUc52o+zndNIf9Up4H324wD9x/tDfGjw/wDs9fBnX/i/4jAeHRrZpI4c4M87YWKIe7yFV9gc9q/n6+B3iOb4GfCLxL/wU1+Oypq3jzxndT2nhK2uBwZptyvcBeoQBSq4xthjwPvivluIs0lSthKM+WUk5Sl/JBbtebekfM/R+BOHaeIcszxVL2kISUKdP/n7WlrGL/uxXvT8u6uizc6b8I/+CbmmRePPi9HD8Q/j7rifbktLmTz7bSGm582ZzktITyX++5+5tX5z5L8LPgv+0X/wVH12+8ZfEbxneW/2SRxB9o02eTSosdY4XRlgjYZGV++e5Jr8xvF3i7xN4+8UX/jXxney6jquqTNcXVzMcvJI5ySfQdgBwBgDgV+sP/BPX9jj9oL9oXw9H4tuvG2s+DfAVlM8VsthcyJLdSBsyCBNwREDZ3SEHLcBTgkfmuX4j+0MXHB06LdBXagna/8AenK6u+7el9Ej9+zvA/2Jls81r4uMcY7J1px5lHf93ShaVo9FFJNq8pMyPil/wRs/aj8D6dLq3gq70vxdHECxgtZGtrogf3UmAQn2EmT2r8rPEPh3xB4R1u58M+KrGfTdRsnMdxa3MbRTROOzKwBBr+674VfCnTfhNoI0DTtX1jWFAGZtYvpL2U4934X/AICAK8V/aW/Yf/Z9/areHUvibpkkerWsRhg1KxkMFyqHkBiMrIAeQJFYDJxjJr6bNPD+nOkqmAfLP+WTuvk7XX4/I/PeHvG6tSxDo5zH2lLpUhHll6uN7NelmvPY/iir2T4KftA/GD9njxMvir4Q65caTPkGWFTutrgD+GaE/I4+oyOxB5r2n9tb9jTxj+x18RYvD+pTtqmgaqrS6VqezZ5qpjfHIBwsseRuA4IIYdcD4yr80q0sRgcQ4SvCpB+jT8mvzP6Bw2JwOcYGNany1aFRdVdNdmn22aauno9T9tLO0+D/APwUr02Txd8NI4Ph38ftGT7b5NtIYLXWGh58yJwQVkBGd/8ArEP3i6fMP0R/4J7ftw6/8YZ7z9nb9oGJtN+JPhoPFIJ18p75IPlclegnj/5aKOGHzrxnH8rPhXxT4j8D+JbHxj4QvZdO1TTJkuLW5hba8UqHIIP8x0I4PFfsZ8bPEdx+0N8FvDv/AAUl+CgXSfiJ4EuYLXxZBbDALw4CXO0clMEZznMLlSf3Zr7PJM+nzyxUF+9ir1IrRVILeVtlOO+lrr5n5RxfwbRdKGXVXfDzfLRm9ZUKr+GDlq3RqP3UnfllZfy2/ptorw/9m/43aF+0X8FNA+L+gARpq1uGnhByYLlCUmiP+44IHqMHvXuFfsNGtCrTjVpu8ZJNPyZ/LWKwtXDVp4evG04Nxa7NOzX3hRRRWhzhRRRQBBdf8e0n+6f5Vx1djdf8e0n+6f5Vx1AH/9H+/iiiigAooooAKKKKAPw9/wCCvXiPWviH4q+F/wCyN4XlKT+K9TS6uQvoXFvAT7AvI3/AQe1fnF/wVO+IOnXfxx034AeDj5Xhv4ZaXb6TawKfkE7Ro0rY6bgvlofdT61+h3xNj/4Tv/gtd4Q0W/8Anh8P6THLGp6Ax21xOD/324Nfg3+0T4kufGH7QHjjxRdtukvte1GXJ9PPcKPwAAr8a4pxUpLEz6zq8n/btOK0+cpX9Uf1d4c5bCDy+lbSlh3W/wC38RNq/qoQcV5M8fjiaeRYEOGchR9TxX9svw9+GHijSvgB4I+Gnwr1ceGbGztYY728gijluhbohLLAJVeJZJJCN0jo+0Zwu4gj+JgO8REsf3l+YfUV/bf8DNVm+Mv7KtkNF1CTTZ9Z0d4Ir2D/AFls9zF8sidPmj3hhz1Fel4YyhGtiHpzWjur6e9f9Dw/H9VXQwFvgvUv62hb8Oa3zPoDwfp6aPoiaONXuNaa1Zo3ubp43nLDqrmJEXI/3QfWukmjMsTRBihYEbl6jPcZ7ivxk/4JMf8ABOv9ob9hBvFdr8ZvGOma9Yak22wttLiYGV2kMkl1dzSIkkkzcKisX8tSwDYNfs/X7Bj6NOlXlCjUU4/zJWv8j+ZsNUnOmpThyvtufj/+1Z8Hf2bPi58PviF8Avh/4wl1j4iaBZjXG0m71qfU7i3u4FMqt5VxLL5LzR70Kx7AVfJXAXH8sysGUMOh5r+vzwl+wD+y78KP2wPEX7bGn6xqFv4g8QmWa70+fUFGlrdTRmGS4EGATIY2dRvdlXe+0DPH83Nh+x58bPFev3kljpSaVYPcymGS+kEX7oudp2DL/dx/DX4Z4xZxkmCxGHxdTGRTlG0ueUU7q3S93a7S69Oh/SngTnNSjgcZhMc1CnCSlC70966dr/4U7Lq79T5Kr9MP+CWfxHsNH+P138EPF2JvDfxL0640a9gc/I0vls0Rx6kb4x/v1x3iz9hmHwV4KuPFHiLxlaWkltGzt5sBSAsBkIHL7iT0GFJJ7V8qfAnxLc+D/jd4N8V2bFJdP1vT5wR/szoT+YyK/NeD+Lcvx+Ijisuq88ackpPlklruveSvdX2ufsmavC5zlWKw9CV7xaTs1aSV4tXS1Ukmrdj9/P8Agkfrus/DD4ifFP8AY/8AEkrPJ4Z1F7y1DeiSG3mI9m2wv/wI1+5Ffhd4Ki/4Qf8A4Lb+INM0/wCSHxDpDySqOhL2cMx/8fizX7o1/RnC7ccLPDP/AJdTnBeid1+DP5M8RkqmZUselZ4ijSqv1lG0vvcWwooor6Q+BCiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/S/v4ooooAKKKKACiiigD8LfiNIfBP/BbLwpq9/wDJDr2kJHGTwCZLS4gH/j0eK/Bj9oPw7c+Evj3428M3ilZLHXtRiIPoJ3x+Ywa/fL/grnoWsfDPx98K/wBrzw5EzyeGNSS0uSvokguYQfZtsy/8CFfnB/wVP+HNho/7QFp8bvCeJvDnxK0231mznQfI0vlqsoz6kbJD/v1+M8U4WUViYW1hV5/+3akVr/4FG3qz+r/DnMYTeX1b6VcP7L/t/Dzenq4Tcl5I/M2v6yP+CR3j4eLP2XbLRZZN0uku9sRnp5bMB/45sr+Tev3u/wCCJXj7yNW8T/DyZ+C6XUak9pUw36xD865uAcV7LNFTf24tfd736Hd405d9Y4cddLWlOMvk7wf/AKUvuP6Kq/P/APaa+InjJfF8vge3lez06KONgIyVM+8ZJYjkgHIx045r9AK/Gr/gsB8UPHXwg8N+AvFfgV4oWmv7u3uTJEsiyL5SsiNkZxkMeCDmvU8bsgzPN+Fa+FyrEujUUot6tKcdnBtapO6fny2ejZ/OnAOFWJzqjheVOU+ZK+yaTlfr2t8z85td/b18H6D4n1DQLrw5fSLY3Elv5okRWcxsVJKMAVyR0yTivEPHf7f3jjVFe18BaXb6PGeBPcH7RN9QMBAfqGrFP7UPwj8c3f2/4y/DuzvbxgA93ZNtd8dyGwT+Lmuvh/aP/ZT8IxC58EfD0y3Y5UzwxKAf99mlP5Cv49wvCeBwUoc3D9Sday3qRlTb73c7Wf8Aej8j+rKWVUKLV8vlKf8AiTj/AOlW+9Hw74w8ceNvHl8NX8bajc6jK2SjTsSo/wBxeFUf7orovgf4dufF3xp8H+F7NS0uoa3p8Cgf7c6A/pW98avjx4q+NmoW0mswW9jY2G/7LaWy4WPfjJLHlicD0HoBX13/AMEtPhrZeI/2jH+L3inEPh34cWE+t31w/wBxJFRliBPqPmkH/XOv3fhXCVa/1ahUoRoybV4RacYq/dKK0jq7Ky1s3uezm+PeByeviqkFBxhK0U767RirJattLTqz9H/CMg8af8Futd1DT/ni8P6OySsOxSyiiP8A49Niv3Qr8NP+CS+j6t8V/iv8V/2wdfiZD4i1B7K0LDtLJ9olUf7imFfwr9y6/oLhe88LUxPSrUnNejdl+CP5G8RWqeY0cAnd4ejSpP8AxRjd/c5NBRRRX0h8CFFFFAEF1/x7Sf7p/lXHV2N1/wAe0n+6f5Vx1AH/0/7+KKKKACiiigAooooA8M/aT+B+iftGfBLxB8INcIjGrWxFvORnyLmMh4ZB/uSAE46jI71+AfwU8N3H7SXwL8Qf8E5fjFt0r4kfD65nuvCstycbmhz5ltuPVcE4x1idWHEdf031+UX/AAUL/Yj8T/FG/sv2mP2c5H074keGtkoFufLe+jg5Taennx9Ezw6/Ie2PleI8slUtjKUOZpOM4/zwe6X96L1j5/cfpPAXEMKF8rxNX2cZSU6VR7Uq0dE3/cmvcn5dldn8r/iXw3r/AIN8Q3vhPxXZy6fqemzPb3VtMNskUsZwysPY/n1HFfe3/BL3x/8A8IP+1bptvK+2HVbeSBvdoyso/RWH419SX8fwg/4Kc6QmleIpLfwB8f8ASI/ssiXCGC11kwfLtZSNwkGMbceZH0w6Dj88tM+HvxW/ZK/aO8OQ/FvR7nQ7uw1OElpV/czQs+x2ilGUkUqTypPvivy3DYWWX46hjaT56HOrSXa+ql/LK26fy0P6LzDMYZ3lGMynEx9ni/ZyvTfV2bjKD+3BtJqS9HZn9gnxB/aM+Cvwp8XWXgj4ja/Bo+o6hB9ogW5DrG0ZYoCZNvlr8wI+Zh0r48/4KkfDey+NP7GOqeIPDUsV7L4elh1u0khYOskcOVl2MCQcwu5GDyRXwx/wVBnbVPH3gjxGeVvPDwUt2LxzOW/9Cr87tO8PfFXVdPisbDS9avNImbzLNILa4mtXfo5j2KULZwDjmvqs+4srKvi8rqYfnjays2nqlq9JX3v0P4FwfiDisjzqNanQU3RnGUbNq9rOz0ej207nxZovhrV9enMNhHwpwztwq/U+vt1qrrWlT6JqUumXBDNHj5l6EEZr7U+IHhHxF8JvEUHhL4j2Umiald2sV/Hb3Q8t2hnztbB75BDKfmVgQQCK8e0f4N/E349/FRvBvwh0a41y+YRq/kD91ECPvSyHCRqPVmFfl8aNZ1vYcj59rWd79rbn9T+HPjFnnEPE1WhmmEWEwKw8qkVJNbSppTdSSimmpO1ko2a3aueH+H/D+ueLNds/DHhi0lv9R1CZLe2toV3SSyyHCqoHUk1+yfxl8N3X7Ln7P+h/8E9/hOF1X4nfEm4gufFDWp3FBMR5dqGHRTgLzx5au5wJKtaZZ/B7/gmFpBhsJLbx78fdVi+zwQWyma00UzjbgAfMZDnGMCSToAiElvv/AP4J7fsS+LPh5q15+1H+0q76h8R/Em+ZUuSHksI5/vFj0E8g4YDiNPkH8VfeZJkVTnlhYfxpK02tqUHur7c8trdFfzt9dxdxjQ9lDMKi/wBlpvmpRejxFVfDK26o03713bmla2yv90/sw/ArRv2bvgboHwh0crK2mQZup1GPPu5Tvmk9fmcnGei4HavfKKK/YaFGFGnGlTVoxSSXkj+WMXi6uKr1MTXlec25N923dsKKKK1OcKKKKAILr/j2k/3T/KuOrsbr/j2k/wB0/wAq46gD/9T+/iiiigAooooAKKKKACiiigD87P2wf+Ccnwm/ahmbxvosh8K+NY8NHq1onyzOn3ftEYK7yMcSKVkX1IAFfnT4m8f/ALdv7L+gyfDn9rjwFb/GLwFD8q3ssf2srGOjfaAjspA6GeMMOzV/RTRXz+N4eo1akq+Hm6VR7uNrS/xRekvzPuMo45xOGoQweOpRxFCPwqd1KH/XuorSh8m0uiPwz0L/AIKEf8E3vi6miH4saHd6Xc6B5gs4tWs3vYIPNILAGFpA65UcSLxjgCvtS1/4KT/sLWVlHFZePrCGCJAqRJa3K7VHQBRFxj0xXv8A48/Zc/Zx+J0z3Xj3wPoupzyHLTS2cfnE+8iqH/WvGP8Ah23+w953n/8ACu9PznOPMn2/98+bj9K5oYTOqMpSpyoyb3k4yjJ2015Xqac/BNSbrPD4mlKW6hKlJf8AgUkpP5n5zfta/tof8Ex/jPq+k+IPHelan491HQlljtI7KGWyikWUqSkryNCzJlcgc4JPHNcZ4V+Iv7c37TGgJ8N/2Ovh7bfB7wHN8pvoo/shMZ4LfaSiMxx1MERf/ar9sPAn7LH7N3wxmS68B+BtF02eM5WaOzjMwI9JGBf9a98AAGBWSyDF16kquKrqPN8Xso8rfrN3lY9SXG+WYPDww2W4SdRQ+B4io5xjre6pRtTvfW+up+cv7H//AATg+FX7MdynjzxHMfFnjeTLvqt2vyQO/wB77OjFtpOeZGLSH1AOK/Rqiivo8FgaGEpKjh4KMV/V33fmz4LNs5xuZ4h4rHVXOb6vouyWyS6JJIKKKK6zzAooooAKKKKAILr/AI9pP90/yrjq7G6/49pP90/yrjqAP//Z"), itemEdited: true), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello there this is a long text", quotedItem: CIQuote.getSample(1, .now, "hi there", chatDir: .directSnd, image: "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAuKADAAQAAAABAAAAYAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAYAC4AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAgEBAgMCAgIDBAMDAwMEBgQEBAQEBgcGBgYGBgYHBwcHBwcHBwgICAgICAkJCQkJCwsLCwsLCwsLC//bAEMBAgICAwMDBQMDBQsIBggLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLC//dAAQADP/aAAwDAQACEQMRAD8A/v4ooooAKKKKACiiigAooooAKK+CP2vP+ChXwZ/ZPibw7dMfEHi2VAYdGs3G9N33TO/IiU9hgu3ZSOa/NzXNL/4KJ/td6JJ49+NXiq2+Cvw7kG/ZNKbDMLcjKblmfI/57SRqewrwMdxBRo1HQoRdWqt1HaP+KT0j838j7XKOCMXiqEcbjKkcPh5bSne8/wDr3BXlN+is+5+43jb45/Bf4bs0fj/xZpGjSL1jvL2KF/8AvlmDfpXjH/DfH7GQuPsv/CydD35x/wAfIx+fT9a/AO58D/8ABJj4UzvF4v8AFfif4l6mp/evpkfkWzP3w2Isg+omb61X/wCF0/8ABJr/AI9f+FQeJPL6ed9vbzPrj7ZivnavFuIT+KhHyc5Sf3wjY+7w/hlgZQv7PF1P70aUKa+SqTUvwP6afBXx2+CnxIZYvAHi3R9ZkfpHZ3sUz/8AfKsW/SvVq/lItvBf/BJX4rTLF4V8UeJ/hpqTH91JqUfn2yv2y2JcD3MqfUV9OaFon/BRH9krQ4vH3wI8XW3xq+HkY3+XDKb/ABCvJxHuaZMDr5Ergd1ruwvFNVrmq0VOK3lSkp29Y6SS+R5GY+HGGi1DD4qVKo9oYmm6XN5RqK9Nvsro/obor4A/ZC/4KH/Bv9qxV8MLnw54vjU+bo9443SFPvG3k4EoHdcB17rjmvv+vqcHjaGKpKth5qUX1X9aPyZ+b5rlOMy3ESwmOpOFRdH+aezT6NXTCiiiuo84KKKKACiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/Q/v4ooooAKKKKACiiigAr8tf+ChP7cWs/BEWfwD+A8R1P4k+JQkUCQr5rWUc52o+zndNIf9Up4H324wD9x/tDfGjw/wDs9fBnX/i/4jAeHRrZpI4c4M87YWKIe7yFV9gc9q/n6+B3iOb4GfCLxL/wU1+Oypq3jzxndT2nhK2uBwZptyvcBeoQBSq4xthjwPvivluIs0lSthKM+WUk5Sl/JBbtebekfM/R+BOHaeIcszxVL2kISUKdP/n7WlrGL/uxXvT8u6uizc6b8I/+CbmmRePPi9HD8Q/j7rifbktLmTz7bSGm582ZzktITyX++5+5tX5z5L8LPgv+0X/wVH12+8ZfEbxneW/2SRxB9o02eTSosdY4XRlgjYZGV++e5Jr8xvF3i7xN4+8UX/jXxney6jquqTNcXVzMcvJI5ySfQdgBwBgDgV+sP/BPX9jj9oL9oXw9H4tuvG2s+DfAVlM8VsthcyJLdSBsyCBNwREDZ3SEHLcBTgkfmuX4j+0MXHB06LdBXagna/8AenK6u+7el9Ej9+zvA/2Jls81r4uMcY7J1px5lHf93ShaVo9FFJNq8pMyPil/wRs/aj8D6dLq3gq70vxdHECxgtZGtrogf3UmAQn2EmT2r8rPEPh3xB4R1u58M+KrGfTdRsnMdxa3MbRTROOzKwBBr+674VfCnTfhNoI0DTtX1jWFAGZtYvpL2U4934X/AICAK8V/aW/Yf/Z9/areHUvibpkkerWsRhg1KxkMFyqHkBiMrIAeQJFYDJxjJr6bNPD+nOkqmAfLP+WTuvk7XX4/I/PeHvG6tSxDo5zH2lLpUhHll6uN7NelmvPY/iir2T4KftA/GD9njxMvir4Q65caTPkGWFTutrgD+GaE/I4+oyOxB5r2n9tb9jTxj+x18RYvD+pTtqmgaqrS6VqezZ5qpjfHIBwsseRuA4IIYdcD4yr80q0sRgcQ4SvCpB+jT8mvzP6Bw2JwOcYGNany1aFRdVdNdmn22aauno9T9tLO0+D/APwUr02Txd8NI4Ph38ftGT7b5NtIYLXWGh58yJwQVkBGd/8ArEP3i6fMP0R/4J7ftw6/8YZ7z9nb9oGJtN+JPhoPFIJ18p75IPlclegnj/5aKOGHzrxnH8rPhXxT4j8D+JbHxj4QvZdO1TTJkuLW5hba8UqHIIP8x0I4PFfsZ8bPEdx+0N8FvDv/AAUl+CgXSfiJ4EuYLXxZBbDALw4CXO0clMEZznMLlSf3Zr7PJM+nzyxUF+9ir1IrRVILeVtlOO+lrr5n5RxfwbRdKGXVXfDzfLRm9ZUKr+GDlq3RqP3UnfllZfy2/ptorw/9m/43aF+0X8FNA+L+gARpq1uGnhByYLlCUmiP+44IHqMHvXuFfsNGtCrTjVpu8ZJNPyZ/LWKwtXDVp4evG04Nxa7NOzX3hRRRWhzhRRRQBBdf8e0n+6f5Vx1djdf8e0n+6f5Vx1AH/9H+/iiiigAooooAKKKKAPw9/wCCvXiPWviH4q+F/wCyN4XlKT+K9TS6uQvoXFvAT7AvI3/AQe1fnF/wVO+IOnXfxx034AeDj5Xhv4ZaXb6TawKfkE7Ro0rY6bgvlofdT61+h3xNj/4Tv/gtd4Q0W/8Anh8P6THLGp6Ax21xOD/324Nfg3+0T4kufGH7QHjjxRdtukvte1GXJ9PPcKPwAAr8a4pxUpLEz6zq8n/btOK0+cpX9Uf1d4c5bCDy+lbSlh3W/wC38RNq/qoQcV5M8fjiaeRYEOGchR9TxX9svw9+GHijSvgB4I+Gnwr1ceGbGztYY728gijluhbohLLAJVeJZJJCN0jo+0Zwu4gj+JgO8REsf3l+YfUV/bf8DNVm+Mv7KtkNF1CTTZ9Z0d4Ir2D/AFls9zF8sidPmj3hhz1Fel4YyhGtiHpzWjur6e9f9Dw/H9VXQwFvgvUv62hb8Oa3zPoDwfp6aPoiaONXuNaa1Zo3ubp43nLDqrmJEXI/3QfWukmjMsTRBihYEbl6jPcZ7ivxk/4JMf8ABOv9ob9hBvFdr8ZvGOma9Yak22wttLiYGV2kMkl1dzSIkkkzcKisX8tSwDYNfs/X7Bj6NOlXlCjUU4/zJWv8j+ZsNUnOmpThyvtufj/+1Z8Hf2bPi58PviF8Avh/4wl1j4iaBZjXG0m71qfU7i3u4FMqt5VxLL5LzR70Kx7AVfJXAXH8sysGUMOh5r+vzwl+wD+y78KP2wPEX7bGn6xqFv4g8QmWa70+fUFGlrdTRmGS4EGATIY2dRvdlXe+0DPH83Nh+x58bPFev3kljpSaVYPcymGS+kEX7oudp2DL/dx/DX4Z4xZxkmCxGHxdTGRTlG0ueUU7q3S93a7S69Oh/SngTnNSjgcZhMc1CnCSlC70966dr/4U7Lq79T5Kr9MP+CWfxHsNH+P138EPF2JvDfxL0640a9gc/I0vls0Rx6kb4x/v1x3iz9hmHwV4KuPFHiLxlaWkltGzt5sBSAsBkIHL7iT0GFJJ7V8qfAnxLc+D/jd4N8V2bFJdP1vT5wR/szoT+YyK/NeD+Lcvx+Ijisuq88ackpPlklruveSvdX2ufsmavC5zlWKw9CV7xaTs1aSV4tXS1Ukmrdj9/P8Agkfrus/DD4ifFP8AY/8AEkrPJ4Z1F7y1DeiSG3mI9m2wv/wI1+5Ffhd4Ki/4Qf8A4Lb+INM0/wCSHxDpDySqOhL2cMx/8fizX7o1/RnC7ccLPDP/AJdTnBeid1+DP5M8RkqmZUselZ4ijSqv1lG0vvcWwooor6Q+BCiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/S/v4ooooAKKKKACiiigD8LfiNIfBP/BbLwpq9/wDJDr2kJHGTwCZLS4gH/j0eK/Bj9oPw7c+Evj3428M3ilZLHXtRiIPoJ3x+Ywa/fL/grnoWsfDPx98K/wBrzw5EzyeGNSS0uSvokguYQfZtsy/8CFfnB/wVP+HNho/7QFp8bvCeJvDnxK0231mznQfI0vlqsoz6kbJD/v1+M8U4WUViYW1hV5/+3akVr/4FG3qz+r/DnMYTeX1b6VcP7L/t/Dzenq4Tcl5I/M2v6yP+CR3j4eLP2XbLRZZN0uku9sRnp5bMB/45sr+Tev3u/wCCJXj7yNW8T/DyZ+C6XUak9pUw36xD865uAcV7LNFTf24tfd736Hd405d9Y4cddLWlOMvk7wf/AKUvuP6Kq/P/APaa+InjJfF8vge3lez06KONgIyVM+8ZJYjkgHIx045r9AK/Gr/gsB8UPHXwg8N+AvFfgV4oWmv7u3uTJEsiyL5SsiNkZxkMeCDmvU8bsgzPN+Fa+FyrEujUUot6tKcdnBtapO6fny2ejZ/OnAOFWJzqjheVOU+ZK+yaTlfr2t8z85td/b18H6D4n1DQLrw5fSLY3Elv5okRWcxsVJKMAVyR0yTivEPHf7f3jjVFe18BaXb6PGeBPcH7RN9QMBAfqGrFP7UPwj8c3f2/4y/DuzvbxgA93ZNtd8dyGwT+Lmuvh/aP/ZT8IxC58EfD0y3Y5UzwxKAf99mlP5Cv49wvCeBwUoc3D9Sday3qRlTb73c7Wf8Aej8j+rKWVUKLV8vlKf8AiTj/AOlW+9Hw74w8ceNvHl8NX8bajc6jK2SjTsSo/wBxeFUf7orovgf4dufF3xp8H+F7NS0uoa3p8Cgf7c6A/pW98avjx4q+NmoW0mswW9jY2G/7LaWy4WPfjJLHlicD0HoBX13/AMEtPhrZeI/2jH+L3inEPh34cWE+t31w/wBxJFRliBPqPmkH/XOv3fhXCVa/1ahUoRoybV4RacYq/dKK0jq7Ky1s3uezm+PeByeviqkFBxhK0U767RirJattLTqz9H/CMg8af8Futd1DT/ni8P6OySsOxSyiiP8A49Niv3Qr8NP+CS+j6t8V/iv8V/2wdfiZD4i1B7K0LDtLJ9olUf7imFfwr9y6/oLhe88LUxPSrUnNejdl+CP5G8RWqeY0cAnd4ejSpP8AxRjd/c5NBRRRX0h8CFFFFAEF1/x7Sf7p/lXHV2N1/wAe0n+6f5Vx1AH/0/7+KKKKACiiigAooooA8M/aT+B+iftGfBLxB8INcIjGrWxFvORnyLmMh4ZB/uSAE46jI71+AfwU8N3H7SXwL8Qf8E5fjFt0r4kfD65nuvCstycbmhz5ltuPVcE4x1idWHEdf031+UX/AAUL/Yj8T/FG/sv2mP2c5H074keGtkoFufLe+jg5Taennx9Ezw6/Ie2PleI8slUtjKUOZpOM4/zwe6X96L1j5/cfpPAXEMKF8rxNX2cZSU6VR7Uq0dE3/cmvcn5dldn8r/iXw3r/AIN8Q3vhPxXZy6fqemzPb3VtMNskUsZwysPY/n1HFfe3/BL3x/8A8IP+1bptvK+2HVbeSBvdoyso/RWH419SX8fwg/4Kc6QmleIpLfwB8f8ASI/ssiXCGC11kwfLtZSNwkGMbceZH0w6Dj88tM+HvxW/ZK/aO8OQ/FvR7nQ7uw1OElpV/czQs+x2ilGUkUqTypPvivy3DYWWX46hjaT56HOrSXa+ql/LK26fy0P6LzDMYZ3lGMynEx9ni/ZyvTfV2bjKD+3BtJqS9HZn9gnxB/aM+Cvwp8XWXgj4ja/Bo+o6hB9ogW5DrG0ZYoCZNvlr8wI+Zh0r48/4KkfDey+NP7GOqeIPDUsV7L4elh1u0khYOskcOVl2MCQcwu5GDyRXwx/wVBnbVPH3gjxGeVvPDwUt2LxzOW/9Cr87tO8PfFXVdPisbDS9avNImbzLNILa4mtXfo5j2KULZwDjmvqs+4srKvi8rqYfnjays2nqlq9JX3v0P4FwfiDisjzqNanQU3RnGUbNq9rOz0ej207nxZovhrV9enMNhHwpwztwq/U+vt1qrrWlT6JqUumXBDNHj5l6EEZr7U+IHhHxF8JvEUHhL4j2Umiald2sV/Hb3Q8t2hnztbB75BDKfmVgQQCK8e0f4N/E349/FRvBvwh0a41y+YRq/kD91ECPvSyHCRqPVmFfl8aNZ1vYcj59rWd79rbn9T+HPjFnnEPE1WhmmEWEwKw8qkVJNbSppTdSSimmpO1ko2a3aueH+H/D+ueLNds/DHhi0lv9R1CZLe2toV3SSyyHCqoHUk1+yfxl8N3X7Ln7P+h/8E9/hOF1X4nfEm4gufFDWp3FBMR5dqGHRTgLzx5au5wJKtaZZ/B7/gmFpBhsJLbx78fdVi+zwQWyma00UzjbgAfMZDnGMCSToAiElvv/AP4J7fsS+LPh5q15+1H+0q76h8R/Em+ZUuSHksI5/vFj0E8g4YDiNPkH8VfeZJkVTnlhYfxpK02tqUHur7c8trdFfzt9dxdxjQ9lDMKi/wBlpvmpRejxFVfDK26o03713bmla2yv90/sw/ArRv2bvgboHwh0crK2mQZup1GPPu5Tvmk9fmcnGei4HavfKKK/YaFGFGnGlTVoxSSXkj+WMXi6uKr1MTXlec25N923dsKKKK1OcKKKKAILr/j2k/3T/KuOrsbr/j2k/wB0/wAq46gD/9T+/iiiigAooooAKKKKACiiigD87P2wf+Ccnwm/ahmbxvosh8K+NY8NHq1onyzOn3ftEYK7yMcSKVkX1IAFfnT4m8f/ALdv7L+gyfDn9rjwFb/GLwFD8q3ssf2srGOjfaAjspA6GeMMOzV/RTRXz+N4eo1akq+Hm6VR7uNrS/xRekvzPuMo45xOGoQweOpRxFCPwqd1KH/XuorSh8m0uiPwz0L/AIKEf8E3vi6miH4saHd6Xc6B5gs4tWs3vYIPNILAGFpA65UcSLxjgCvtS1/4KT/sLWVlHFZePrCGCJAqRJa3K7VHQBRFxj0xXv8A48/Zc/Zx+J0z3Xj3wPoupzyHLTS2cfnE+8iqH/WvGP8Ah23+w953n/8ACu9PznOPMn2/98+bj9K5oYTOqMpSpyoyb3k4yjJ2015Xqac/BNSbrPD4mlKW6hKlJf8AgUkpP5n5zfta/tof8Ex/jPq+k+IPHelan491HQlljtI7KGWyikWUqSkryNCzJlcgc4JPHNcZ4V+Iv7c37TGgJ8N/2Ovh7bfB7wHN8pvoo/shMZ4LfaSiMxx1MERf/ar9sPAn7LH7N3wxmS68B+BtF02eM5WaOzjMwI9JGBf9a98AAGBWSyDF16kquKrqPN8Xso8rfrN3lY9SXG+WYPDww2W4SdRQ+B4io5xjre6pRtTvfW+up+cv7H//AATg+FX7MdynjzxHMfFnjeTLvqt2vyQO/wB77OjFtpOeZGLSH1AOK/Rqiivo8FgaGEpKjh4KMV/V33fmz4LNs5xuZ4h4rHVXOb6vouyWyS6JJIKKKK6zzAooooAKKKKAILr/AI9pP90/yrjq7G6/49pP90/yrjqAP//Z"), itemEdited: true), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemEdited: true), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello", quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directSnd), itemEdited: true), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directRcv), itemEdited: true), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "👍", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "Hello too", chatDir: .directRcv), itemEdited: true), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this covers -", .rcvRead, itemEdited: true), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this text has the time on the same line ", .rcvRead, itemEdited: true), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "https://simplex.chat", .rcvRead, itemEdited: true), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "chaT@simplex.chat", .rcvRead, itemEdited: true), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello", quotedItem: CIQuote.getSample(1, .now, "hi there hello hello hello ther hello hello", chatDir: .directSnd, image: "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAuKADAAQAAAABAAAAYAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAYAC4AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAgEBAgMCAgIDBAMDAwMEBgQEBAQEBgcGBgYGBgYHBwcHBwcHBwgICAgICAkJCQkJCwsLCwsLCwsLC//bAEMBAgICAwMDBQMDBQsIBggLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLC//dAAQADP/aAAwDAQACEQMRAD8A/v4ooooAKKKKACiiigAooooAKK+CP2vP+ChXwZ/ZPibw7dMfEHi2VAYdGs3G9N33TO/IiU9hgu3ZSOa/NzXNL/4KJ/td6JJ49+NXiq2+Cvw7kG/ZNKbDMLcjKblmfI/57SRqewrwMdxBRo1HQoRdWqt1HaP+KT0j838j7XKOCMXiqEcbjKkcPh5bSne8/wDr3BXlN+is+5+43jb45/Bf4bs0fj/xZpGjSL1jvL2KF/8AvlmDfpXjH/DfH7GQuPsv/CydD35x/wAfIx+fT9a/AO58D/8ABJj4UzvF4v8AFfif4l6mp/evpkfkWzP3w2Isg+omb61X/wCF0/8ABJr/AI9f+FQeJPL6ed9vbzPrj7ZivnavFuIT+KhHyc5Sf3wjY+7w/hlgZQv7PF1P70aUKa+SqTUvwP6afBXx2+CnxIZYvAHi3R9ZkfpHZ3sUz/8AfKsW/SvVq/lItvBf/BJX4rTLF4V8UeJ/hpqTH91JqUfn2yv2y2JcD3MqfUV9OaFon/BRH9krQ4vH3wI8XW3xq+HkY3+XDKb/ABCvJxHuaZMDr5Ergd1ruwvFNVrmq0VOK3lSkp29Y6SS+R5GY+HGGi1DD4qVKo9oYmm6XN5RqK9Nvsro/obor4A/ZC/4KH/Bv9qxV8MLnw54vjU+bo9443SFPvG3k4EoHdcB17rjmvv+vqcHjaGKpKth5qUX1X9aPyZ+b5rlOMy3ESwmOpOFRdH+aezT6NXTCiiiuo84KKKKACiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/Q/v4ooooAKKKKACiiigAr8tf+ChP7cWs/BEWfwD+A8R1P4k+JQkUCQr5rWUc52o+zndNIf9Up4H324wD9x/tDfGjw/wDs9fBnX/i/4jAeHRrZpI4c4M87YWKIe7yFV9gc9q/n6+B3iOb4GfCLxL/wU1+Oypq3jzxndT2nhK2uBwZptyvcBeoQBSq4xthjwPvivluIs0lSthKM+WUk5Sl/JBbtebekfM/R+BOHaeIcszxVL2kISUKdP/n7WlrGL/uxXvT8u6uizc6b8I/+CbmmRePPi9HD8Q/j7rifbktLmTz7bSGm582ZzktITyX++5+5tX5z5L8LPgv+0X/wVH12+8ZfEbxneW/2SRxB9o02eTSosdY4XRlgjYZGV++e5Jr8xvF3i7xN4+8UX/jXxney6jquqTNcXVzMcvJI5ySfQdgBwBgDgV+sP/BPX9jj9oL9oXw9H4tuvG2s+DfAVlM8VsthcyJLdSBsyCBNwREDZ3SEHLcBTgkfmuX4j+0MXHB06LdBXagna/8AenK6u+7el9Ej9+zvA/2Jls81r4uMcY7J1px5lHf93ShaVo9FFJNq8pMyPil/wRs/aj8D6dLq3gq70vxdHECxgtZGtrogf3UmAQn2EmT2r8rPEPh3xB4R1u58M+KrGfTdRsnMdxa3MbRTROOzKwBBr+674VfCnTfhNoI0DTtX1jWFAGZtYvpL2U4934X/AICAK8V/aW/Yf/Z9/areHUvibpkkerWsRhg1KxkMFyqHkBiMrIAeQJFYDJxjJr6bNPD+nOkqmAfLP+WTuvk7XX4/I/PeHvG6tSxDo5zH2lLpUhHll6uN7NelmvPY/iir2T4KftA/GD9njxMvir4Q65caTPkGWFTutrgD+GaE/I4+oyOxB5r2n9tb9jTxj+x18RYvD+pTtqmgaqrS6VqezZ5qpjfHIBwsseRuA4IIYdcD4yr80q0sRgcQ4SvCpB+jT8mvzP6Bw2JwOcYGNany1aFRdVdNdmn22aauno9T9tLO0+D/APwUr02Txd8NI4Ph38ftGT7b5NtIYLXWGh58yJwQVkBGd/8ArEP3i6fMP0R/4J7ftw6/8YZ7z9nb9oGJtN+JPhoPFIJ18p75IPlclegnj/5aKOGHzrxnH8rPhXxT4j8D+JbHxj4QvZdO1TTJkuLW5hba8UqHIIP8x0I4PFfsZ8bPEdx+0N8FvDv/AAUl+CgXSfiJ4EuYLXxZBbDALw4CXO0clMEZznMLlSf3Zr7PJM+nzyxUF+9ir1IrRVILeVtlOO+lrr5n5RxfwbRdKGXVXfDzfLRm9ZUKr+GDlq3RqP3UnfllZfy2/ptorw/9m/43aF+0X8FNA+L+gARpq1uGnhByYLlCUmiP+44IHqMHvXuFfsNGtCrTjVpu8ZJNPyZ/LWKwtXDVp4evG04Nxa7NOzX3hRRRWhzhRRRQBBdf8e0n+6f5Vx1djdf8e0n+6f5Vx1AH/9H+/iiiigAooooAKKKKAPw9/wCCvXiPWviH4q+F/wCyN4XlKT+K9TS6uQvoXFvAT7AvI3/AQe1fnF/wVO+IOnXfxx034AeDj5Xhv4ZaXb6TawKfkE7Ro0rY6bgvlofdT61+h3xNj/4Tv/gtd4Q0W/8Anh8P6THLGp6Ax21xOD/324Nfg3+0T4kufGH7QHjjxRdtukvte1GXJ9PPcKPwAAr8a4pxUpLEz6zq8n/btOK0+cpX9Uf1d4c5bCDy+lbSlh3W/wC38RNq/qoQcV5M8fjiaeRYEOGchR9TxX9svw9+GHijSvgB4I+Gnwr1ceGbGztYY728gijluhbohLLAJVeJZJJCN0jo+0Zwu4gj+JgO8REsf3l+YfUV/bf8DNVm+Mv7KtkNF1CTTZ9Z0d4Ir2D/AFls9zF8sidPmj3hhz1Fel4YyhGtiHpzWjur6e9f9Dw/H9VXQwFvgvUv62hb8Oa3zPoDwfp6aPoiaONXuNaa1Zo3ubp43nLDqrmJEXI/3QfWukmjMsTRBihYEbl6jPcZ7ivxk/4JMf8ABOv9ob9hBvFdr8ZvGOma9Yak22wttLiYGV2kMkl1dzSIkkkzcKisX8tSwDYNfs/X7Bj6NOlXlCjUU4/zJWv8j+ZsNUnOmpThyvtufj/+1Z8Hf2bPi58PviF8Avh/4wl1j4iaBZjXG0m71qfU7i3u4FMqt5VxLL5LzR70Kx7AVfJXAXH8sysGUMOh5r+vzwl+wD+y78KP2wPEX7bGn6xqFv4g8QmWa70+fUFGlrdTRmGS4EGATIY2dRvdlXe+0DPH83Nh+x58bPFev3kljpSaVYPcymGS+kEX7oudp2DL/dx/DX4Z4xZxkmCxGHxdTGRTlG0ueUU7q3S93a7S69Oh/SngTnNSjgcZhMc1CnCSlC70966dr/4U7Lq79T5Kr9MP+CWfxHsNH+P138EPF2JvDfxL0640a9gc/I0vls0Rx6kb4x/v1x3iz9hmHwV4KuPFHiLxlaWkltGzt5sBSAsBkIHL7iT0GFJJ7V8qfAnxLc+D/jd4N8V2bFJdP1vT5wR/szoT+YyK/NeD+Lcvx+Ijisuq88ackpPlklruveSvdX2ufsmavC5zlWKw9CV7xaTs1aSV4tXS1Ukmrdj9/P8Agkfrus/DD4ifFP8AY/8AEkrPJ4Z1F7y1DeiSG3mI9m2wv/wI1+5Ffhd4Ki/4Qf8A4Lb+INM0/wCSHxDpDySqOhL2cMx/8fizX7o1/RnC7ccLPDP/AJdTnBeid1+DP5M8RkqmZUselZ4ijSqv1lG0vvcWwooor6Q+BCiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/S/v4ooooAKKKKACiiigD8LfiNIfBP/BbLwpq9/wDJDr2kJHGTwCZLS4gH/j0eK/Bj9oPw7c+Evj3428M3ilZLHXtRiIPoJ3x+Ywa/fL/grnoWsfDPx98K/wBrzw5EzyeGNSS0uSvokguYQfZtsy/8CFfnB/wVP+HNho/7QFp8bvCeJvDnxK0231mznQfI0vlqsoz6kbJD/v1+M8U4WUViYW1hV5/+3akVr/4FG3qz+r/DnMYTeX1b6VcP7L/t/Dzenq4Tcl5I/M2v6yP+CR3j4eLP2XbLRZZN0uku9sRnp5bMB/45sr+Tev3u/wCCJXj7yNW8T/DyZ+C6XUak9pUw36xD865uAcV7LNFTf24tfd736Hd405d9Y4cddLWlOMvk7wf/AKUvuP6Kq/P/APaa+InjJfF8vge3lez06KONgIyVM+8ZJYjkgHIx045r9AK/Gr/gsB8UPHXwg8N+AvFfgV4oWmv7u3uTJEsiyL5SsiNkZxkMeCDmvU8bsgzPN+Fa+FyrEujUUot6tKcdnBtapO6fny2ejZ/OnAOFWJzqjheVOU+ZK+yaTlfr2t8z85td/b18H6D4n1DQLrw5fSLY3Elv5okRWcxsVJKMAVyR0yTivEPHf7f3jjVFe18BaXb6PGeBPcH7RN9QMBAfqGrFP7UPwj8c3f2/4y/DuzvbxgA93ZNtd8dyGwT+Lmuvh/aP/ZT8IxC58EfD0y3Y5UzwxKAf99mlP5Cv49wvCeBwUoc3D9Sday3qRlTb73c7Wf8Aej8j+rKWVUKLV8vlKf8AiTj/AOlW+9Hw74w8ceNvHl8NX8bajc6jK2SjTsSo/wBxeFUf7orovgf4dufF3xp8H+F7NS0uoa3p8Cgf7c6A/pW98avjx4q+NmoW0mswW9jY2G/7LaWy4WPfjJLHlicD0HoBX13/AMEtPhrZeI/2jH+L3inEPh34cWE+t31w/wBxJFRliBPqPmkH/XOv3fhXCVa/1ahUoRoybV4RacYq/dKK0jq7Ky1s3uezm+PeByeviqkFBxhK0U767RirJattLTqz9H/CMg8af8Futd1DT/ni8P6OySsOxSyiiP8A49Niv3Qr8NP+CS+j6t8V/iv8V/2wdfiZD4i1B7K0LDtLJ9olUf7imFfwr9y6/oLhe88LUxPSrUnNejdl+CP5G8RWqeY0cAnd4ejSpP8AxRjd/c5NBRRRX0h8CFFFFAEF1/x7Sf7p/lXHV2N1/wAe0n+6f5Vx1AH/0/7+KKKKACiiigAooooA8M/aT+B+iftGfBLxB8INcIjGrWxFvORnyLmMh4ZB/uSAE46jI71+AfwU8N3H7SXwL8Qf8E5fjFt0r4kfD65nuvCstycbmhz5ltuPVcE4x1idWHEdf031+UX/AAUL/Yj8T/FG/sv2mP2c5H074keGtkoFufLe+jg5Taennx9Ezw6/Ie2PleI8slUtjKUOZpOM4/zwe6X96L1j5/cfpPAXEMKF8rxNX2cZSU6VR7Uq0dE3/cmvcn5dldn8r/iXw3r/AIN8Q3vhPxXZy6fqemzPb3VtMNskUsZwysPY/n1HFfe3/BL3x/8A8IP+1bptvK+2HVbeSBvdoyso/RWH419SX8fwg/4Kc6QmleIpLfwB8f8ASI/ssiXCGC11kwfLtZSNwkGMbceZH0w6Dj88tM+HvxW/ZK/aO8OQ/FvR7nQ7uw1OElpV/czQs+x2ilGUkUqTypPvivy3DYWWX46hjaT56HOrSXa+ql/LK26fy0P6LzDMYZ3lGMynEx9ni/ZyvTfV2bjKD+3BtJqS9HZn9gnxB/aM+Cvwp8XWXgj4ja/Bo+o6hB9ogW5DrG0ZYoCZNvlr8wI+Zh0r48/4KkfDey+NP7GOqeIPDUsV7L4elh1u0khYOskcOVl2MCQcwu5GDyRXwx/wVBnbVPH3gjxGeVvPDwUt2LxzOW/9Cr87tO8PfFXVdPisbDS9avNImbzLNILa4mtXfo5j2KULZwDjmvqs+4srKvi8rqYfnjays2nqlq9JX3v0P4FwfiDisjzqNanQU3RnGUbNq9rOz0ej207nxZovhrV9enMNhHwpwztwq/U+vt1qrrWlT6JqUumXBDNHj5l6EEZr7U+IHhHxF8JvEUHhL4j2Umiald2sV/Hb3Q8t2hnztbB75BDKfmVgQQCK8e0f4N/E349/FRvBvwh0a41y+YRq/kD91ECPvSyHCRqPVmFfl8aNZ1vYcj59rWd79rbn9T+HPjFnnEPE1WhmmEWEwKw8qkVJNbSppTdSSimmpO1ko2a3aueH+H/D+ueLNds/DHhi0lv9R1CZLe2toV3SSyyHCqoHUk1+yfxl8N3X7Ln7P+h/8E9/hOF1X4nfEm4gufFDWp3FBMR5dqGHRTgLzx5au5wJKtaZZ/B7/gmFpBhsJLbx78fdVi+zwQWyma00UzjbgAfMZDnGMCSToAiElvv/AP4J7fsS+LPh5q15+1H+0q76h8R/Em+ZUuSHksI5/vFj0E8g4YDiNPkH8VfeZJkVTnlhYfxpK02tqUHur7c8trdFfzt9dxdxjQ9lDMKi/wBlpvmpRejxFVfDK26o03713bmla2yv90/sw/ArRv2bvgboHwh0crK2mQZup1GPPu5Tvmk9fmcnGei4HavfKKK/YaFGFGnGlTVoxSSXkj+WMXi6uKr1MTXlec25N923dsKKKK1OcKKKKAILr/j2k/3T/KuOrsbr/j2k/wB0/wAq46gD/9T+/iiiigAooooAKKKKACiiigD87P2wf+Ccnwm/ahmbxvosh8K+NY8NHq1onyzOn3ftEYK7yMcSKVkX1IAFfnT4m8f/ALdv7L+gyfDn9rjwFb/GLwFD8q3ssf2srGOjfaAjspA6GeMMOzV/RTRXz+N4eo1akq+Hm6VR7uNrS/xRekvzPuMo45xOGoQweOpRxFCPwqd1KH/XuorSh8m0uiPwz0L/AIKEf8E3vi6miH4saHd6Xc6B5gs4tWs3vYIPNILAGFpA65UcSLxjgCvtS1/4KT/sLWVlHFZePrCGCJAqRJa3K7VHQBRFxj0xXv8A48/Zc/Zx+J0z3Xj3wPoupzyHLTS2cfnE+8iqH/WvGP8Ah23+w953n/8ACu9PznOPMn2/98+bj9K5oYTOqMpSpyoyb3k4yjJ2015Xqac/BNSbrPD4mlKW6hKlJf8AgUkpP5n5zfta/tof8Ex/jPq+k+IPHelan491HQlljtI7KGWyikWUqSkryNCzJlcgc4JPHNcZ4V+Iv7c37TGgJ8N/2Ovh7bfB7wHN8pvoo/shMZ4LfaSiMxx1MERf/ar9sPAn7LH7N3wxmS68B+BtF02eM5WaOzjMwI9JGBf9a98AAGBWSyDF16kquKrqPN8Xso8rfrN3lY9SXG+WYPDww2W4SdRQ+B4io5xjre6pRtTvfW+up+cv7H//AATg+FX7MdynjzxHMfFnjeTLvqt2vyQO/wB77OjFtpOeZGLSH1AOK/Rqiivo8FgaGEpKjh4KMV/V33fmz4LNs5xuZ4h4rHVXOb6vouyWyS6JJIKKKK6zzAooooAKKKKAILr/AI9pP90/yrjq7G6/49pP90/yrjqAP//Z"), itemEdited: true), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello there this is a long text", quotedItem: CIQuote.getSample(1, .now, "hi there", chatDir: .directSnd, image: "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAuKADAAQAAAABAAAAYAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAYAC4AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAgEBAgMCAgIDBAMDAwMEBgQEBAQEBgcGBgYGBgYHBwcHBwcHBwgICAgICAkJCQkJCwsLCwsLCwsLC//bAEMBAgICAwMDBQMDBQsIBggLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLC//dAAQADP/aAAwDAQACEQMRAD8A/v4ooooAKKKKACiiigAooooAKK+CP2vP+ChXwZ/ZPibw7dMfEHi2VAYdGs3G9N33TO/IiU9hgu3ZSOa/NzXNL/4KJ/td6JJ49+NXiq2+Cvw7kG/ZNKbDMLcjKblmfI/57SRqewrwMdxBRo1HQoRdWqt1HaP+KT0j838j7XKOCMXiqEcbjKkcPh5bSne8/wDr3BXlN+is+5+43jb45/Bf4bs0fj/xZpGjSL1jvL2KF/8AvlmDfpXjH/DfH7GQuPsv/CydD35x/wAfIx+fT9a/AO58D/8ABJj4UzvF4v8AFfif4l6mp/evpkfkWzP3w2Isg+omb61X/wCF0/8ABJr/AI9f+FQeJPL6ed9vbzPrj7ZivnavFuIT+KhHyc5Sf3wjY+7w/hlgZQv7PF1P70aUKa+SqTUvwP6afBXx2+CnxIZYvAHi3R9ZkfpHZ3sUz/8AfKsW/SvVq/lItvBf/BJX4rTLF4V8UeJ/hpqTH91JqUfn2yv2y2JcD3MqfUV9OaFon/BRH9krQ4vH3wI8XW3xq+HkY3+XDKb/ABCvJxHuaZMDr5Ergd1ruwvFNVrmq0VOK3lSkp29Y6SS+R5GY+HGGi1DD4qVKo9oYmm6XN5RqK9Nvsro/obor4A/ZC/4KH/Bv9qxV8MLnw54vjU+bo9443SFPvG3k4EoHdcB17rjmvv+vqcHjaGKpKth5qUX1X9aPyZ+b5rlOMy3ESwmOpOFRdH+aezT6NXTCiiiuo84KKKKACiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/Q/v4ooooAKKKKACiiigAr8tf+ChP7cWs/BEWfwD+A8R1P4k+JQkUCQr5rWUc52o+zndNIf9Up4H324wD9x/tDfGjw/wDs9fBnX/i/4jAeHRrZpI4c4M87YWKIe7yFV9gc9q/n6+B3iOb4GfCLxL/wU1+Oypq3jzxndT2nhK2uBwZptyvcBeoQBSq4xthjwPvivluIs0lSthKM+WUk5Sl/JBbtebekfM/R+BOHaeIcszxVL2kISUKdP/n7WlrGL/uxXvT8u6uizc6b8I/+CbmmRePPi9HD8Q/j7rifbktLmTz7bSGm582ZzktITyX++5+5tX5z5L8LPgv+0X/wVH12+8ZfEbxneW/2SRxB9o02eTSosdY4XRlgjYZGV++e5Jr8xvF3i7xN4+8UX/jXxney6jquqTNcXVzMcvJI5ySfQdgBwBgDgV+sP/BPX9jj9oL9oXw9H4tuvG2s+DfAVlM8VsthcyJLdSBsyCBNwREDZ3SEHLcBTgkfmuX4j+0MXHB06LdBXagna/8AenK6u+7el9Ej9+zvA/2Jls81r4uMcY7J1px5lHf93ShaVo9FFJNq8pMyPil/wRs/aj8D6dLq3gq70vxdHECxgtZGtrogf3UmAQn2EmT2r8rPEPh3xB4R1u58M+KrGfTdRsnMdxa3MbRTROOzKwBBr+674VfCnTfhNoI0DTtX1jWFAGZtYvpL2U4934X/AICAK8V/aW/Yf/Z9/areHUvibpkkerWsRhg1KxkMFyqHkBiMrIAeQJFYDJxjJr6bNPD+nOkqmAfLP+WTuvk7XX4/I/PeHvG6tSxDo5zH2lLpUhHll6uN7NelmvPY/iir2T4KftA/GD9njxMvir4Q65caTPkGWFTutrgD+GaE/I4+oyOxB5r2n9tb9jTxj+x18RYvD+pTtqmgaqrS6VqezZ5qpjfHIBwsseRuA4IIYdcD4yr80q0sRgcQ4SvCpB+jT8mvzP6Bw2JwOcYGNany1aFRdVdNdmn22aauno9T9tLO0+D/APwUr02Txd8NI4Ph38ftGT7b5NtIYLXWGh58yJwQVkBGd/8ArEP3i6fMP0R/4J7ftw6/8YZ7z9nb9oGJtN+JPhoPFIJ18p75IPlclegnj/5aKOGHzrxnH8rPhXxT4j8D+JbHxj4QvZdO1TTJkuLW5hba8UqHIIP8x0I4PFfsZ8bPEdx+0N8FvDv/AAUl+CgXSfiJ4EuYLXxZBbDALw4CXO0clMEZznMLlSf3Zr7PJM+nzyxUF+9ir1IrRVILeVtlOO+lrr5n5RxfwbRdKGXVXfDzfLRm9ZUKr+GDlq3RqP3UnfllZfy2/ptorw/9m/43aF+0X8FNA+L+gARpq1uGnhByYLlCUmiP+44IHqMHvXuFfsNGtCrTjVpu8ZJNPyZ/LWKwtXDVp4evG04Nxa7NOzX3hRRRWhzhRRRQBBdf8e0n+6f5Vx1djdf8e0n+6f5Vx1AH/9H+/iiiigAooooAKKKKAPw9/wCCvXiPWviH4q+F/wCyN4XlKT+K9TS6uQvoXFvAT7AvI3/AQe1fnF/wVO+IOnXfxx034AeDj5Xhv4ZaXb6TawKfkE7Ro0rY6bgvlofdT61+h3xNj/4Tv/gtd4Q0W/8Anh8P6THLGp6Ax21xOD/324Nfg3+0T4kufGH7QHjjxRdtukvte1GXJ9PPcKPwAAr8a4pxUpLEz6zq8n/btOK0+cpX9Uf1d4c5bCDy+lbSlh3W/wC38RNq/qoQcV5M8fjiaeRYEOGchR9TxX9svw9+GHijSvgB4I+Gnwr1ceGbGztYY728gijluhbohLLAJVeJZJJCN0jo+0Zwu4gj+JgO8REsf3l+YfUV/bf8DNVm+Mv7KtkNF1CTTZ9Z0d4Ir2D/AFls9zF8sidPmj3hhz1Fel4YyhGtiHpzWjur6e9f9Dw/H9VXQwFvgvUv62hb8Oa3zPoDwfp6aPoiaONXuNaa1Zo3ubp43nLDqrmJEXI/3QfWukmjMsTRBihYEbl6jPcZ7ivxk/4JMf8ABOv9ob9hBvFdr8ZvGOma9Yak22wttLiYGV2kMkl1dzSIkkkzcKisX8tSwDYNfs/X7Bj6NOlXlCjUU4/zJWv8j+ZsNUnOmpThyvtufj/+1Z8Hf2bPi58PviF8Avh/4wl1j4iaBZjXG0m71qfU7i3u4FMqt5VxLL5LzR70Kx7AVfJXAXH8sysGUMOh5r+vzwl+wD+y78KP2wPEX7bGn6xqFv4g8QmWa70+fUFGlrdTRmGS4EGATIY2dRvdlXe+0DPH83Nh+x58bPFev3kljpSaVYPcymGS+kEX7oudp2DL/dx/DX4Z4xZxkmCxGHxdTGRTlG0ueUU7q3S93a7S69Oh/SngTnNSjgcZhMc1CnCSlC70966dr/4U7Lq79T5Kr9MP+CWfxHsNH+P138EPF2JvDfxL0640a9gc/I0vls0Rx6kb4x/v1x3iz9hmHwV4KuPFHiLxlaWkltGzt5sBSAsBkIHL7iT0GFJJ7V8qfAnxLc+D/jd4N8V2bFJdP1vT5wR/szoT+YyK/NeD+Lcvx+Ijisuq88ackpPlklruveSvdX2ufsmavC5zlWKw9CV7xaTs1aSV4tXS1Ukmrdj9/P8Agkfrus/DD4ifFP8AY/8AEkrPJ4Z1F7y1DeiSG3mI9m2wv/wI1+5Ffhd4Ki/4Qf8A4Lb+INM0/wCSHxDpDySqOhL2cMx/8fizX7o1/RnC7ccLPDP/AJdTnBeid1+DP5M8RkqmZUselZ4ijSqv1lG0vvcWwooor6Q+BCiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/S/v4ooooAKKKKACiiigD8LfiNIfBP/BbLwpq9/wDJDr2kJHGTwCZLS4gH/j0eK/Bj9oPw7c+Evj3428M3ilZLHXtRiIPoJ3x+Ywa/fL/grnoWsfDPx98K/wBrzw5EzyeGNSS0uSvokguYQfZtsy/8CFfnB/wVP+HNho/7QFp8bvCeJvDnxK0231mznQfI0vlqsoz6kbJD/v1+M8U4WUViYW1hV5/+3akVr/4FG3qz+r/DnMYTeX1b6VcP7L/t/Dzenq4Tcl5I/M2v6yP+CR3j4eLP2XbLRZZN0uku9sRnp5bMB/45sr+Tev3u/wCCJXj7yNW8T/DyZ+C6XUak9pUw36xD865uAcV7LNFTf24tfd736Hd405d9Y4cddLWlOMvk7wf/AKUvuP6Kq/P/APaa+InjJfF8vge3lez06KONgIyVM+8ZJYjkgHIx045r9AK/Gr/gsB8UPHXwg8N+AvFfgV4oWmv7u3uTJEsiyL5SsiNkZxkMeCDmvU8bsgzPN+Fa+FyrEujUUot6tKcdnBtapO6fny2ejZ/OnAOFWJzqjheVOU+ZK+yaTlfr2t8z85td/b18H6D4n1DQLrw5fSLY3Elv5okRWcxsVJKMAVyR0yTivEPHf7f3jjVFe18BaXb6PGeBPcH7RN9QMBAfqGrFP7UPwj8c3f2/4y/DuzvbxgA93ZNtd8dyGwT+Lmuvh/aP/ZT8IxC58EfD0y3Y5UzwxKAf99mlP5Cv49wvCeBwUoc3D9Sday3qRlTb73c7Wf8Aej8j+rKWVUKLV8vlKf8AiTj/AOlW+9Hw74w8ceNvHl8NX8bajc6jK2SjTsSo/wBxeFUf7orovgf4dufF3xp8H+F7NS0uoa3p8Cgf7c6A/pW98avjx4q+NmoW0mswW9jY2G/7LaWy4WPfjJLHlicD0HoBX13/AMEtPhrZeI/2jH+L3inEPh34cWE+t31w/wBxJFRliBPqPmkH/XOv3fhXCVa/1ahUoRoybV4RacYq/dKK0jq7Ky1s3uezm+PeByeviqkFBxhK0U767RirJattLTqz9H/CMg8af8Futd1DT/ni8P6OySsOxSyiiP8A49Niv3Qr8NP+CS+j6t8V/iv8V/2wdfiZD4i1B7K0LDtLJ9olUf7imFfwr9y6/oLhe88LUxPSrUnNejdl+CP5G8RWqeY0cAnd4ejSpP8AxRjd/c5NBRRRX0h8CFFFFAEF1/x7Sf7p/lXHV2N1/wAe0n+6f5Vx1AH/0/7+KKKKACiiigAooooA8M/aT+B+iftGfBLxB8INcIjGrWxFvORnyLmMh4ZB/uSAE46jI71+AfwU8N3H7SXwL8Qf8E5fjFt0r4kfD65nuvCstycbmhz5ltuPVcE4x1idWHEdf031+UX/AAUL/Yj8T/FG/sv2mP2c5H074keGtkoFufLe+jg5Taennx9Ezw6/Ie2PleI8slUtjKUOZpOM4/zwe6X96L1j5/cfpPAXEMKF8rxNX2cZSU6VR7Uq0dE3/cmvcn5dldn8r/iXw3r/AIN8Q3vhPxXZy6fqemzPb3VtMNskUsZwysPY/n1HFfe3/BL3x/8A8IP+1bptvK+2HVbeSBvdoyso/RWH419SX8fwg/4Kc6QmleIpLfwB8f8ASI/ssiXCGC11kwfLtZSNwkGMbceZH0w6Dj88tM+HvxW/ZK/aO8OQ/FvR7nQ7uw1OElpV/czQs+x2ilGUkUqTypPvivy3DYWWX46hjaT56HOrSXa+ql/LK26fy0P6LzDMYZ3lGMynEx9ni/ZyvTfV2bjKD+3BtJqS9HZn9gnxB/aM+Cvwp8XWXgj4ja/Bo+o6hB9ogW5DrG0ZYoCZNvlr8wI+Zh0r48/4KkfDey+NP7GOqeIPDUsV7L4elh1u0khYOskcOVl2MCQcwu5GDyRXwx/wVBnbVPH3gjxGeVvPDwUt2LxzOW/9Cr87tO8PfFXVdPisbDS9avNImbzLNILa4mtXfo5j2KULZwDjmvqs+4srKvi8rqYfnjays2nqlq9JX3v0P4FwfiDisjzqNanQU3RnGUbNq9rOz0ej207nxZovhrV9enMNhHwpwztwq/U+vt1qrrWlT6JqUumXBDNHj5l6EEZr7U+IHhHxF8JvEUHhL4j2Umiald2sV/Hb3Q8t2hnztbB75BDKfmVgQQCK8e0f4N/E349/FRvBvwh0a41y+YRq/kD91ECPvSyHCRqPVmFfl8aNZ1vYcj59rWd79rbn9T+HPjFnnEPE1WhmmEWEwKw8qkVJNbSppTdSSimmpO1ko2a3aueH+H/D+ueLNds/DHhi0lv9R1CZLe2toV3SSyyHCqoHUk1+yfxl8N3X7Ln7P+h/8E9/hOF1X4nfEm4gufFDWp3FBMR5dqGHRTgLzx5au5wJKtaZZ/B7/gmFpBhsJLbx78fdVi+zwQWyma00UzjbgAfMZDnGMCSToAiElvv/AP4J7fsS+LPh5q15+1H+0q76h8R/Em+ZUuSHksI5/vFj0E8g4YDiNPkH8VfeZJkVTnlhYfxpK02tqUHur7c8trdFfzt9dxdxjQ9lDMKi/wBlpvmpRejxFVfDK26o03713bmla2yv90/sw/ArRv2bvgboHwh0crK2mQZup1GPPu5Tvmk9fmcnGei4HavfKKK/YaFGFGnGlTVoxSSXkj+WMXi6uKr1MTXlec25N923dsKKKK1OcKKKKAILr/j2k/3T/KuOrsbr/j2k/wB0/wAq46gD/9T+/iiiigAooooAKKKKACiiigD87P2wf+Ccnwm/ahmbxvosh8K+NY8NHq1onyzOn3ftEYK7yMcSKVkX1IAFfnT4m8f/ALdv7L+gyfDn9rjwFb/GLwFD8q3ssf2srGOjfaAjspA6GeMMOzV/RTRXz+N4eo1akq+Hm6VR7uNrS/xRekvzPuMo45xOGoQweOpRxFCPwqd1KH/XuorSh8m0uiPwz0L/AIKEf8E3vi6miH4saHd6Xc6B5gs4tWs3vYIPNILAGFpA65UcSLxjgCvtS1/4KT/sLWVlHFZePrCGCJAqRJa3K7VHQBRFxj0xXv8A48/Zc/Zx+J0z3Xj3wPoupzyHLTS2cfnE+8iqH/WvGP8Ah23+w953n/8ACu9PznOPMn2/98+bj9K5oYTOqMpSpyoyb3k4yjJ2015Xqac/BNSbrPD4mlKW6hKlJf8AgUkpP5n5zfta/tof8Ex/jPq+k+IPHelan491HQlljtI7KGWyikWUqSkryNCzJlcgc4JPHNcZ4V+Iv7c37TGgJ8N/2Ovh7bfB7wHN8pvoo/shMZ4LfaSiMxx1MERf/ar9sPAn7LH7N3wxmS68B+BtF02eM5WaOzjMwI9JGBf9a98AAGBWSyDF16kquKrqPN8Xso8rfrN3lY9SXG+WYPDww2W4SdRQ+B4io5xjre6pRtTvfW+up+cv7H//AATg+FX7MdynjzxHMfFnjeTLvqt2vyQO/wB77OjFtpOeZGLSH1AOK/Rqiivo8FgaGEpKjh4KMV/V33fmz4LNs5xuZ4h4rHVXOb6vouyWyS6JJIKKKK6zzAooooAKKKKAILr/AI9pP90/yrjq7G6/49pP90/yrjqAP//Z"), itemEdited: true), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) } + .environment(\.revealed, false) .previewLayout(.fixed(width: 360, height: 200)) } } @@ -415,17 +422,18 @@ struct FramedItemView_Edited_Previews: PreviewProvider { struct FramedItemView_Deleted_Previews: PreviewProvider { static var previews: some View { Group { - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello", quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directSnd), itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directRcv), itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "👍", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "Hello too", chatDir: .directRcv), itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this covers -", .rcvRead, itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this text has the time on the same line ", .rcvRead, itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "https://simplex.chat", .rcvRead, itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "chaT@simplex.chat", .rcvRead, itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello", quotedItem: CIQuote.getSample(1, .now, "hi there hello hello hello ther hello hello", chatDir: .directSnd, image: "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAuKADAAQAAAABAAAAYAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAYAC4AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAgEBAgMCAgIDBAMDAwMEBgQEBAQEBgcGBgYGBgYHBwcHBwcHBwgICAgICAkJCQkJCwsLCwsLCwsLC//bAEMBAgICAwMDBQMDBQsIBggLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLC//dAAQADP/aAAwDAQACEQMRAD8A/v4ooooAKKKKACiiigAooooAKK+CP2vP+ChXwZ/ZPibw7dMfEHi2VAYdGs3G9N33TO/IiU9hgu3ZSOa/NzXNL/4KJ/td6JJ49+NXiq2+Cvw7kG/ZNKbDMLcjKblmfI/57SRqewrwMdxBRo1HQoRdWqt1HaP+KT0j838j7XKOCMXiqEcbjKkcPh5bSne8/wDr3BXlN+is+5+43jb45/Bf4bs0fj/xZpGjSL1jvL2KF/8AvlmDfpXjH/DfH7GQuPsv/CydD35x/wAfIx+fT9a/AO58D/8ABJj4UzvF4v8AFfif4l6mp/evpkfkWzP3w2Isg+omb61X/wCF0/8ABJr/AI9f+FQeJPL6ed9vbzPrj7ZivnavFuIT+KhHyc5Sf3wjY+7w/hlgZQv7PF1P70aUKa+SqTUvwP6afBXx2+CnxIZYvAHi3R9ZkfpHZ3sUz/8AfKsW/SvVq/lItvBf/BJX4rTLF4V8UeJ/hpqTH91JqUfn2yv2y2JcD3MqfUV9OaFon/BRH9krQ4vH3wI8XW3xq+HkY3+XDKb/ABCvJxHuaZMDr5Ergd1ruwvFNVrmq0VOK3lSkp29Y6SS+R5GY+HGGi1DD4qVKo9oYmm6XN5RqK9Nvsro/obor4A/ZC/4KH/Bv9qxV8MLnw54vjU+bo9443SFPvG3k4EoHdcB17rjmvv+vqcHjaGKpKth5qUX1X9aPyZ+b5rlOMy3ESwmOpOFRdH+aezT6NXTCiiiuo84KKKKACiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/Q/v4ooooAKKKKACiiigAr8tf+ChP7cWs/BEWfwD+A8R1P4k+JQkUCQr5rWUc52o+zndNIf9Up4H324wD9x/tDfGjw/wDs9fBnX/i/4jAeHRrZpI4c4M87YWKIe7yFV9gc9q/n6+B3iOb4GfCLxL/wU1+Oypq3jzxndT2nhK2uBwZptyvcBeoQBSq4xthjwPvivluIs0lSthKM+WUk5Sl/JBbtebekfM/R+BOHaeIcszxVL2kISUKdP/n7WlrGL/uxXvT8u6uizc6b8I/+CbmmRePPi9HD8Q/j7rifbktLmTz7bSGm582ZzktITyX++5+5tX5z5L8LPgv+0X/wVH12+8ZfEbxneW/2SRxB9o02eTSosdY4XRlgjYZGV++e5Jr8xvF3i7xN4+8UX/jXxney6jquqTNcXVzMcvJI5ySfQdgBwBgDgV+sP/BPX9jj9oL9oXw9H4tuvG2s+DfAVlM8VsthcyJLdSBsyCBNwREDZ3SEHLcBTgkfmuX4j+0MXHB06LdBXagna/8AenK6u+7el9Ej9+zvA/2Jls81r4uMcY7J1px5lHf93ShaVo9FFJNq8pMyPil/wRs/aj8D6dLq3gq70vxdHECxgtZGtrogf3UmAQn2EmT2r8rPEPh3xB4R1u58M+KrGfTdRsnMdxa3MbRTROOzKwBBr+674VfCnTfhNoI0DTtX1jWFAGZtYvpL2U4934X/AICAK8V/aW/Yf/Z9/areHUvibpkkerWsRhg1KxkMFyqHkBiMrIAeQJFYDJxjJr6bNPD+nOkqmAfLP+WTuvk7XX4/I/PeHvG6tSxDo5zH2lLpUhHll6uN7NelmvPY/iir2T4KftA/GD9njxMvir4Q65caTPkGWFTutrgD+GaE/I4+oyOxB5r2n9tb9jTxj+x18RYvD+pTtqmgaqrS6VqezZ5qpjfHIBwsseRuA4IIYdcD4yr80q0sRgcQ4SvCpB+jT8mvzP6Bw2JwOcYGNany1aFRdVdNdmn22aauno9T9tLO0+D/APwUr02Txd8NI4Ph38ftGT7b5NtIYLXWGh58yJwQVkBGd/8ArEP3i6fMP0R/4J7ftw6/8YZ7z9nb9oGJtN+JPhoPFIJ18p75IPlclegnj/5aKOGHzrxnH8rPhXxT4j8D+JbHxj4QvZdO1TTJkuLW5hba8UqHIIP8x0I4PFfsZ8bPEdx+0N8FvDv/AAUl+CgXSfiJ4EuYLXxZBbDALw4CXO0clMEZznMLlSf3Zr7PJM+nzyxUF+9ir1IrRVILeVtlOO+lrr5n5RxfwbRdKGXVXfDzfLRm9ZUKr+GDlq3RqP3UnfllZfy2/ptorw/9m/43aF+0X8FNA+L+gARpq1uGnhByYLlCUmiP+44IHqMHvXuFfsNGtCrTjVpu8ZJNPyZ/LWKwtXDVp4evG04Nxa7NOzX3hRRRWhzhRRRQBBdf8e0n+6f5Vx1djdf8e0n+6f5Vx1AH/9H+/iiiigAooooAKKKKAPw9/wCCvXiPWviH4q+F/wCyN4XlKT+K9TS6uQvoXFvAT7AvI3/AQe1fnF/wVO+IOnXfxx034AeDj5Xhv4ZaXb6TawKfkE7Ro0rY6bgvlofdT61+h3xNj/4Tv/gtd4Q0W/8Anh8P6THLGp6Ax21xOD/324Nfg3+0T4kufGH7QHjjxRdtukvte1GXJ9PPcKPwAAr8a4pxUpLEz6zq8n/btOK0+cpX9Uf1d4c5bCDy+lbSlh3W/wC38RNq/qoQcV5M8fjiaeRYEOGchR9TxX9svw9+GHijSvgB4I+Gnwr1ceGbGztYY728gijluhbohLLAJVeJZJJCN0jo+0Zwu4gj+JgO8REsf3l+YfUV/bf8DNVm+Mv7KtkNF1CTTZ9Z0d4Ir2D/AFls9zF8sidPmj3hhz1Fel4YyhGtiHpzWjur6e9f9Dw/H9VXQwFvgvUv62hb8Oa3zPoDwfp6aPoiaONXuNaa1Zo3ubp43nLDqrmJEXI/3QfWukmjMsTRBihYEbl6jPcZ7ivxk/4JMf8ABOv9ob9hBvFdr8ZvGOma9Yak22wttLiYGV2kMkl1dzSIkkkzcKisX8tSwDYNfs/X7Bj6NOlXlCjUU4/zJWv8j+ZsNUnOmpThyvtufj/+1Z8Hf2bPi58PviF8Avh/4wl1j4iaBZjXG0m71qfU7i3u4FMqt5VxLL5LzR70Kx7AVfJXAXH8sysGUMOh5r+vzwl+wD+y78KP2wPEX7bGn6xqFv4g8QmWa70+fUFGlrdTRmGS4EGATIY2dRvdlXe+0DPH83Nh+x58bPFev3kljpSaVYPcymGS+kEX7oudp2DL/dx/DX4Z4xZxkmCxGHxdTGRTlG0ueUU7q3S93a7S69Oh/SngTnNSjgcZhMc1CnCSlC70966dr/4U7Lq79T5Kr9MP+CWfxHsNH+P138EPF2JvDfxL0640a9gc/I0vls0Rx6kb4x/v1x3iz9hmHwV4KuPFHiLxlaWkltGzt5sBSAsBkIHL7iT0GFJJ7V8qfAnxLc+D/jd4N8V2bFJdP1vT5wR/szoT+YyK/NeD+Lcvx+Ijisuq88ackpPlklruveSvdX2ufsmavC5zlWKw9CV7xaTs1aSV4tXS1Ukmrdj9/P8Agkfrus/DD4ifFP8AY/8AEkrPJ4Z1F7y1DeiSG3mI9m2wv/wI1+5Ffhd4Ki/4Qf8A4Lb+INM0/wCSHxDpDySqOhL2cMx/8fizX7o1/RnC7ccLPDP/AJdTnBeid1+DP5M8RkqmZUselZ4ijSqv1lG0vvcWwooor6Q+BCiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/S/v4ooooAKKKKACiiigD8LfiNIfBP/BbLwpq9/wDJDr2kJHGTwCZLS4gH/j0eK/Bj9oPw7c+Evj3428M3ilZLHXtRiIPoJ3x+Ywa/fL/grnoWsfDPx98K/wBrzw5EzyeGNSS0uSvokguYQfZtsy/8CFfnB/wVP+HNho/7QFp8bvCeJvDnxK0231mznQfI0vlqsoz6kbJD/v1+M8U4WUViYW1hV5/+3akVr/4FG3qz+r/DnMYTeX1b6VcP7L/t/Dzenq4Tcl5I/M2v6yP+CR3j4eLP2XbLRZZN0uku9sRnp5bMB/45sr+Tev3u/wCCJXj7yNW8T/DyZ+C6XUak9pUw36xD865uAcV7LNFTf24tfd736Hd405d9Y4cddLWlOMvk7wf/AKUvuP6Kq/P/APaa+InjJfF8vge3lez06KONgIyVM+8ZJYjkgHIx045r9AK/Gr/gsB8UPHXwg8N+AvFfgV4oWmv7u3uTJEsiyL5SsiNkZxkMeCDmvU8bsgzPN+Fa+FyrEujUUot6tKcdnBtapO6fny2ejZ/OnAOFWJzqjheVOU+ZK+yaTlfr2t8z85td/b18H6D4n1DQLrw5fSLY3Elv5okRWcxsVJKMAVyR0yTivEPHf7f3jjVFe18BaXb6PGeBPcH7RN9QMBAfqGrFP7UPwj8c3f2/4y/DuzvbxgA93ZNtd8dyGwT+Lmuvh/aP/ZT8IxC58EfD0y3Y5UzwxKAf99mlP5Cv49wvCeBwUoc3D9Sday3qRlTb73c7Wf8Aej8j+rKWVUKLV8vlKf8AiTj/AOlW+9Hw74w8ceNvHl8NX8bajc6jK2SjTsSo/wBxeFUf7orovgf4dufF3xp8H+F7NS0uoa3p8Cgf7c6A/pW98avjx4q+NmoW0mswW9jY2G/7LaWy4WPfjJLHlicD0HoBX13/AMEtPhrZeI/2jH+L3inEPh34cWE+t31w/wBxJFRliBPqPmkH/XOv3fhXCVa/1ahUoRoybV4RacYq/dKK0jq7Ky1s3uezm+PeByeviqkFBxhK0U767RirJattLTqz9H/CMg8af8Futd1DT/ni8P6OySsOxSyiiP8A49Niv3Qr8NP+CS+j6t8V/iv8V/2wdfiZD4i1B7K0LDtLJ9olUf7imFfwr9y6/oLhe88LUxPSrUnNejdl+CP5G8RWqeY0cAnd4ejSpP8AxRjd/c5NBRRRX0h8CFFFFAEF1/x7Sf7p/lXHV2N1/wAe0n+6f5Vx1AH/0/7+KKKKACiiigAooooA8M/aT+B+iftGfBLxB8INcIjGrWxFvORnyLmMh4ZB/uSAE46jI71+AfwU8N3H7SXwL8Qf8E5fjFt0r4kfD65nuvCstycbmhz5ltuPVcE4x1idWHEdf031+UX/AAUL/Yj8T/FG/sv2mP2c5H074keGtkoFufLe+jg5Taennx9Ezw6/Ie2PleI8slUtjKUOZpOM4/zwe6X96L1j5/cfpPAXEMKF8rxNX2cZSU6VR7Uq0dE3/cmvcn5dldn8r/iXw3r/AIN8Q3vhPxXZy6fqemzPb3VtMNskUsZwysPY/n1HFfe3/BL3x/8A8IP+1bptvK+2HVbeSBvdoyso/RWH419SX8fwg/4Kc6QmleIpLfwB8f8ASI/ssiXCGC11kwfLtZSNwkGMbceZH0w6Dj88tM+HvxW/ZK/aO8OQ/FvR7nQ7uw1OElpV/czQs+x2ilGUkUqTypPvivy3DYWWX46hjaT56HOrSXa+ql/LK26fy0P6LzDMYZ3lGMynEx9ni/ZyvTfV2bjKD+3BtJqS9HZn9gnxB/aM+Cvwp8XWXgj4ja/Bo+o6hB9ogW5DrG0ZYoCZNvlr8wI+Zh0r48/4KkfDey+NP7GOqeIPDUsV7L4elh1u0khYOskcOVl2MCQcwu5GDyRXwx/wVBnbVPH3gjxGeVvPDwUt2LxzOW/9Cr87tO8PfFXVdPisbDS9avNImbzLNILa4mtXfo5j2KULZwDjmvqs+4srKvi8rqYfnjays2nqlq9JX3v0P4FwfiDisjzqNanQU3RnGUbNq9rOz0ej207nxZovhrV9enMNhHwpwztwq/U+vt1qrrWlT6JqUumXBDNHj5l6EEZr7U+IHhHxF8JvEUHhL4j2Umiald2sV/Hb3Q8t2hnztbB75BDKfmVgQQCK8e0f4N/E349/FRvBvwh0a41y+YRq/kD91ECPvSyHCRqPVmFfl8aNZ1vYcj59rWd79rbn9T+HPjFnnEPE1WhmmEWEwKw8qkVJNbSppTdSSimmpO1ko2a3aueH+H/D+ueLNds/DHhi0lv9R1CZLe2toV3SSyyHCqoHUk1+yfxl8N3X7Ln7P+h/8E9/hOF1X4nfEm4gufFDWp3FBMR5dqGHRTgLzx5au5wJKtaZZ/B7/gmFpBhsJLbx78fdVi+zwQWyma00UzjbgAfMZDnGMCSToAiElvv/AP4J7fsS+LPh5q15+1H+0q76h8R/Em+ZUuSHksI5/vFj0E8g4YDiNPkH8VfeZJkVTnlhYfxpK02tqUHur7c8trdFfzt9dxdxjQ9lDMKi/wBlpvmpRejxFVfDK26o03713bmla2yv90/sw/ArRv2bvgboHwh0crK2mQZup1GPPu5Tvmk9fmcnGei4HavfKKK/YaFGFGnGlTVoxSSXkj+WMXi6uKr1MTXlec25N923dsKKKK1OcKKKKAILr/j2k/3T/KuOrsbr/j2k/wB0/wAq46gD/9T+/iiiigAooooAKKKKACiiigD87P2wf+Ccnwm/ahmbxvosh8K+NY8NHq1onyzOn3ftEYK7yMcSKVkX1IAFfnT4m8f/ALdv7L+gyfDn9rjwFb/GLwFD8q3ssf2srGOjfaAjspA6GeMMOzV/RTRXz+N4eo1akq+Hm6VR7uNrS/xRekvzPuMo45xOGoQweOpRxFCPwqd1KH/XuorSh8m0uiPwz0L/AIKEf8E3vi6miH4saHd6Xc6B5gs4tWs3vYIPNILAGFpA65UcSLxjgCvtS1/4KT/sLWVlHFZePrCGCJAqRJa3K7VHQBRFxj0xXv8A48/Zc/Zx+J0z3Xj3wPoupzyHLTS2cfnE+8iqH/WvGP8Ah23+w953n/8ACu9PznOPMn2/98+bj9K5oYTOqMpSpyoyb3k4yjJ2015Xqac/BNSbrPD4mlKW6hKlJf8AgUkpP5n5zfta/tof8Ex/jPq+k+IPHelan491HQlljtI7KGWyikWUqSkryNCzJlcgc4JPHNcZ4V+Iv7c37TGgJ8N/2Ovh7bfB7wHN8pvoo/shMZ4LfaSiMxx1MERf/ar9sPAn7LH7N3wxmS68B+BtF02eM5WaOzjMwI9JGBf9a98AAGBWSyDF16kquKrqPN8Xso8rfrN3lY9SXG+WYPDww2W4SdRQ+B4io5xjre6pRtTvfW+up+cv7H//AATg+FX7MdynjzxHMfFnjeTLvqt2vyQO/wB77OjFtpOeZGLSH1AOK/Rqiivo8FgaGEpKjh4KMV/V33fmz4LNs5xuZ4h4rHVXOb6vouyWyS6JJIKKKK6zzAooooAKKKKAILr/AI9pP90/yrjq7G6/49pP90/yrjqAP//Z"), itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) - FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello there this is a long text", quotedItem: CIQuote.getSample(1, .now, "hi there", chatDir: .directSnd, image: "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAuKADAAQAAAABAAAAYAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAYAC4AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAgEBAgMCAgIDBAMDAwMEBgQEBAQEBgcGBgYGBgYHBwcHBwcHBwgICAgICAkJCQkJCwsLCwsLCwsLC//bAEMBAgICAwMDBQMDBQsIBggLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLC//dAAQADP/aAAwDAQACEQMRAD8A/v4ooooAKKKKACiiigAooooAKK+CP2vP+ChXwZ/ZPibw7dMfEHi2VAYdGs3G9N33TO/IiU9hgu3ZSOa/NzXNL/4KJ/td6JJ49+NXiq2+Cvw7kG/ZNKbDMLcjKblmfI/57SRqewrwMdxBRo1HQoRdWqt1HaP+KT0j838j7XKOCMXiqEcbjKkcPh5bSne8/wDr3BXlN+is+5+43jb45/Bf4bs0fj/xZpGjSL1jvL2KF/8AvlmDfpXjH/DfH7GQuPsv/CydD35x/wAfIx+fT9a/AO58D/8ABJj4UzvF4v8AFfif4l6mp/evpkfkWzP3w2Isg+omb61X/wCF0/8ABJr/AI9f+FQeJPL6ed9vbzPrj7ZivnavFuIT+KhHyc5Sf3wjY+7w/hlgZQv7PF1P70aUKa+SqTUvwP6afBXx2+CnxIZYvAHi3R9ZkfpHZ3sUz/8AfKsW/SvVq/lItvBf/BJX4rTLF4V8UeJ/hpqTH91JqUfn2yv2y2JcD3MqfUV9OaFon/BRH9krQ4vH3wI8XW3xq+HkY3+XDKb/ABCvJxHuaZMDr5Ergd1ruwvFNVrmq0VOK3lSkp29Y6SS+R5GY+HGGi1DD4qVKo9oYmm6XN5RqK9Nvsro/obor4A/ZC/4KH/Bv9qxV8MLnw54vjU+bo9443SFPvG3k4EoHdcB17rjmvv+vqcHjaGKpKth5qUX1X9aPyZ+b5rlOMy3ESwmOpOFRdH+aezT6NXTCiiiuo84KKKKACiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/Q/v4ooooAKKKKACiiigAr8tf+ChP7cWs/BEWfwD+A8R1P4k+JQkUCQr5rWUc52o+zndNIf9Up4H324wD9x/tDfGjw/wDs9fBnX/i/4jAeHRrZpI4c4M87YWKIe7yFV9gc9q/n6+B3iOb4GfCLxL/wU1+Oypq3jzxndT2nhK2uBwZptyvcBeoQBSq4xthjwPvivluIs0lSthKM+WUk5Sl/JBbtebekfM/R+BOHaeIcszxVL2kISUKdP/n7WlrGL/uxXvT8u6uizc6b8I/+CbmmRePPi9HD8Q/j7rifbktLmTz7bSGm582ZzktITyX++5+5tX5z5L8LPgv+0X/wVH12+8ZfEbxneW/2SRxB9o02eTSosdY4XRlgjYZGV++e5Jr8xvF3i7xN4+8UX/jXxney6jquqTNcXVzMcvJI5ySfQdgBwBgDgV+sP/BPX9jj9oL9oXw9H4tuvG2s+DfAVlM8VsthcyJLdSBsyCBNwREDZ3SEHLcBTgkfmuX4j+0MXHB06LdBXagna/8AenK6u+7el9Ej9+zvA/2Jls81r4uMcY7J1px5lHf93ShaVo9FFJNq8pMyPil/wRs/aj8D6dLq3gq70vxdHECxgtZGtrogf3UmAQn2EmT2r8rPEPh3xB4R1u58M+KrGfTdRsnMdxa3MbRTROOzKwBBr+674VfCnTfhNoI0DTtX1jWFAGZtYvpL2U4934X/AICAK8V/aW/Yf/Z9/areHUvibpkkerWsRhg1KxkMFyqHkBiMrIAeQJFYDJxjJr6bNPD+nOkqmAfLP+WTuvk7XX4/I/PeHvG6tSxDo5zH2lLpUhHll6uN7NelmvPY/iir2T4KftA/GD9njxMvir4Q65caTPkGWFTutrgD+GaE/I4+oyOxB5r2n9tb9jTxj+x18RYvD+pTtqmgaqrS6VqezZ5qpjfHIBwsseRuA4IIYdcD4yr80q0sRgcQ4SvCpB+jT8mvzP6Bw2JwOcYGNany1aFRdVdNdmn22aauno9T9tLO0+D/APwUr02Txd8NI4Ph38ftGT7b5NtIYLXWGh58yJwQVkBGd/8ArEP3i6fMP0R/4J7ftw6/8YZ7z9nb9oGJtN+JPhoPFIJ18p75IPlclegnj/5aKOGHzrxnH8rPhXxT4j8D+JbHxj4QvZdO1TTJkuLW5hba8UqHIIP8x0I4PFfsZ8bPEdx+0N8FvDv/AAUl+CgXSfiJ4EuYLXxZBbDALw4CXO0clMEZznMLlSf3Zr7PJM+nzyxUF+9ir1IrRVILeVtlOO+lrr5n5RxfwbRdKGXVXfDzfLRm9ZUKr+GDlq3RqP3UnfllZfy2/ptorw/9m/43aF+0X8FNA+L+gARpq1uGnhByYLlCUmiP+44IHqMHvXuFfsNGtCrTjVpu8ZJNPyZ/LWKwtXDVp4evG04Nxa7NOzX3hRRRWhzhRRRQBBdf8e0n+6f5Vx1djdf8e0n+6f5Vx1AH/9H+/iiiigAooooAKKKKAPw9/wCCvXiPWviH4q+F/wCyN4XlKT+K9TS6uQvoXFvAT7AvI3/AQe1fnF/wVO+IOnXfxx034AeDj5Xhv4ZaXb6TawKfkE7Ro0rY6bgvlofdT61+h3xNj/4Tv/gtd4Q0W/8Anh8P6THLGp6Ax21xOD/324Nfg3+0T4kufGH7QHjjxRdtukvte1GXJ9PPcKPwAAr8a4pxUpLEz6zq8n/btOK0+cpX9Uf1d4c5bCDy+lbSlh3W/wC38RNq/qoQcV5M8fjiaeRYEOGchR9TxX9svw9+GHijSvgB4I+Gnwr1ceGbGztYY728gijluhbohLLAJVeJZJJCN0jo+0Zwu4gj+JgO8REsf3l+YfUV/bf8DNVm+Mv7KtkNF1CTTZ9Z0d4Ir2D/AFls9zF8sidPmj3hhz1Fel4YyhGtiHpzWjur6e9f9Dw/H9VXQwFvgvUv62hb8Oa3zPoDwfp6aPoiaONXuNaa1Zo3ubp43nLDqrmJEXI/3QfWukmjMsTRBihYEbl6jPcZ7ivxk/4JMf8ABOv9ob9hBvFdr8ZvGOma9Yak22wttLiYGV2kMkl1dzSIkkkzcKisX8tSwDYNfs/X7Bj6NOlXlCjUU4/zJWv8j+ZsNUnOmpThyvtufj/+1Z8Hf2bPi58PviF8Avh/4wl1j4iaBZjXG0m71qfU7i3u4FMqt5VxLL5LzR70Kx7AVfJXAXH8sysGUMOh5r+vzwl+wD+y78KP2wPEX7bGn6xqFv4g8QmWa70+fUFGlrdTRmGS4EGATIY2dRvdlXe+0DPH83Nh+x58bPFev3kljpSaVYPcymGS+kEX7oudp2DL/dx/DX4Z4xZxkmCxGHxdTGRTlG0ueUU7q3S93a7S69Oh/SngTnNSjgcZhMc1CnCSlC70966dr/4U7Lq79T5Kr9MP+CWfxHsNH+P138EPF2JvDfxL0640a9gc/I0vls0Rx6kb4x/v1x3iz9hmHwV4KuPFHiLxlaWkltGzt5sBSAsBkIHL7iT0GFJJ7V8qfAnxLc+D/jd4N8V2bFJdP1vT5wR/szoT+YyK/NeD+Lcvx+Ijisuq88ackpPlklruveSvdX2ufsmavC5zlWKw9CV7xaTs1aSV4tXS1Ukmrdj9/P8Agkfrus/DD4ifFP8AY/8AEkrPJ4Z1F7y1DeiSG3mI9m2wv/wI1+5Ffhd4Ki/4Qf8A4Lb+INM0/wCSHxDpDySqOhL2cMx/8fizX7o1/RnC7ccLPDP/AJdTnBeid1+DP5M8RkqmZUselZ4ijSqv1lG0vvcWwooor6Q+BCiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/S/v4ooooAKKKKACiiigD8LfiNIfBP/BbLwpq9/wDJDr2kJHGTwCZLS4gH/j0eK/Bj9oPw7c+Evj3428M3ilZLHXtRiIPoJ3x+Ywa/fL/grnoWsfDPx98K/wBrzw5EzyeGNSS0uSvokguYQfZtsy/8CFfnB/wVP+HNho/7QFp8bvCeJvDnxK0231mznQfI0vlqsoz6kbJD/v1+M8U4WUViYW1hV5/+3akVr/4FG3qz+r/DnMYTeX1b6VcP7L/t/Dzenq4Tcl5I/M2v6yP+CR3j4eLP2XbLRZZN0uku9sRnp5bMB/45sr+Tev3u/wCCJXj7yNW8T/DyZ+C6XUak9pUw36xD865uAcV7LNFTf24tfd736Hd405d9Y4cddLWlOMvk7wf/AKUvuP6Kq/P/APaa+InjJfF8vge3lez06KONgIyVM+8ZJYjkgHIx045r9AK/Gr/gsB8UPHXwg8N+AvFfgV4oWmv7u3uTJEsiyL5SsiNkZxkMeCDmvU8bsgzPN+Fa+FyrEujUUot6tKcdnBtapO6fny2ejZ/OnAOFWJzqjheVOU+ZK+yaTlfr2t8z85td/b18H6D4n1DQLrw5fSLY3Elv5okRWcxsVJKMAVyR0yTivEPHf7f3jjVFe18BaXb6PGeBPcH7RN9QMBAfqGrFP7UPwj8c3f2/4y/DuzvbxgA93ZNtd8dyGwT+Lmuvh/aP/ZT8IxC58EfD0y3Y5UzwxKAf99mlP5Cv49wvCeBwUoc3D9Sday3qRlTb73c7Wf8Aej8j+rKWVUKLV8vlKf8AiTj/AOlW+9Hw74w8ceNvHl8NX8bajc6jK2SjTsSo/wBxeFUf7orovgf4dufF3xp8H+F7NS0uoa3p8Cgf7c6A/pW98avjx4q+NmoW0mswW9jY2G/7LaWy4WPfjJLHlicD0HoBX13/AMEtPhrZeI/2jH+L3inEPh34cWE+t31w/wBxJFRliBPqPmkH/XOv3fhXCVa/1ahUoRoybV4RacYq/dKK0jq7Ky1s3uezm+PeByeviqkFBxhK0U767RirJattLTqz9H/CMg8af8Futd1DT/ni8P6OySsOxSyiiP8A49Niv3Qr8NP+CS+j6t8V/iv8V/2wdfiZD4i1B7K0LDtLJ9olUf7imFfwr9y6/oLhe88LUxPSrUnNejdl+CP5G8RWqeY0cAnd4ejSpP8AxRjd/c5NBRRRX0h8CFFFFAEF1/x7Sf7p/lXHV2N1/wAe0n+6f5Vx1AH/0/7+KKKKACiiigAooooA8M/aT+B+iftGfBLxB8INcIjGrWxFvORnyLmMh4ZB/uSAE46jI71+AfwU8N3H7SXwL8Qf8E5fjFt0r4kfD65nuvCstycbmhz5ltuPVcE4x1idWHEdf031+UX/AAUL/Yj8T/FG/sv2mP2c5H074keGtkoFufLe+jg5Taennx9Ezw6/Ie2PleI8slUtjKUOZpOM4/zwe6X96L1j5/cfpPAXEMKF8rxNX2cZSU6VR7Uq0dE3/cmvcn5dldn8r/iXw3r/AIN8Q3vhPxXZy6fqemzPb3VtMNskUsZwysPY/n1HFfe3/BL3x/8A8IP+1bptvK+2HVbeSBvdoyso/RWH419SX8fwg/4Kc6QmleIpLfwB8f8ASI/ssiXCGC11kwfLtZSNwkGMbceZH0w6Dj88tM+HvxW/ZK/aO8OQ/FvR7nQ7uw1OElpV/czQs+x2ilGUkUqTypPvivy3DYWWX46hjaT56HOrSXa+ql/LK26fy0P6LzDMYZ3lGMynEx9ni/ZyvTfV2bjKD+3BtJqS9HZn9gnxB/aM+Cvwp8XWXgj4ja/Bo+o6hB9ogW5DrG0ZYoCZNvlr8wI+Zh0r48/4KkfDey+NP7GOqeIPDUsV7L4elh1u0khYOskcOVl2MCQcwu5GDyRXwx/wVBnbVPH3gjxGeVvPDwUt2LxzOW/9Cr87tO8PfFXVdPisbDS9avNImbzLNILa4mtXfo5j2KULZwDjmvqs+4srKvi8rqYfnjays2nqlq9JX3v0P4FwfiDisjzqNanQU3RnGUbNq9rOz0ej207nxZovhrV9enMNhHwpwztwq/U+vt1qrrWlT6JqUumXBDNHj5l6EEZr7U+IHhHxF8JvEUHhL4j2Umiald2sV/Hb3Q8t2hnztbB75BDKfmVgQQCK8e0f4N/E349/FRvBvwh0a41y+YRq/kD91ECPvSyHCRqPVmFfl8aNZ1vYcj59rWd79rbn9T+HPjFnnEPE1WhmmEWEwKw8qkVJNbSppTdSSimmpO1ko2a3aueH+H/D+ueLNds/DHhi0lv9R1CZLe2toV3SSyyHCqoHUk1+yfxl8N3X7Ln7P+h/8E9/hOF1X4nfEm4gufFDWp3FBMR5dqGHRTgLzx5au5wJKtaZZ/B7/gmFpBhsJLbx78fdVi+zwQWyma00UzjbgAfMZDnGMCSToAiElvv/AP4J7fsS+LPh5q15+1H+0q76h8R/Em+ZUuSHksI5/vFj0E8g4YDiNPkH8VfeZJkVTnlhYfxpK02tqUHur7c8trdFfzt9dxdxjQ9lDMKi/wBlpvmpRejxFVfDK26o03713bmla2yv90/sw/ArRv2bvgboHwh0crK2mQZup1GPPu5Tvmk9fmcnGei4HavfKKK/YaFGFGnGlTVoxSSXkj+WMXi6uKr1MTXlec25N923dsKKKK1OcKKKKAILr/j2k/3T/KuOrsbr/j2k/wB0/wAq46gD/9T+/iiiigAooooAKKKKACiiigD87P2wf+Ccnwm/ahmbxvosh8K+NY8NHq1onyzOn3ftEYK7yMcSKVkX1IAFfnT4m8f/ALdv7L+gyfDn9rjwFb/GLwFD8q3ssf2srGOjfaAjspA6GeMMOzV/RTRXz+N4eo1akq+Hm6VR7uNrS/xRekvzPuMo45xOGoQweOpRxFCPwqd1KH/XuorSh8m0uiPwz0L/AIKEf8E3vi6miH4saHd6Xc6B5gs4tWs3vYIPNILAGFpA65UcSLxjgCvtS1/4KT/sLWVlHFZePrCGCJAqRJa3K7VHQBRFxj0xXv8A48/Zc/Zx+J0z3Xj3wPoupzyHLTS2cfnE+8iqH/WvGP8Ah23+w953n/8ACu9PznOPMn2/98+bj9K5oYTOqMpSpyoyb3k4yjJ2015Xqac/BNSbrPD4mlKW6hKlJf8AgUkpP5n5zfta/tof8Ex/jPq+k+IPHelan491HQlljtI7KGWyikWUqSkryNCzJlcgc4JPHNcZ4V+Iv7c37TGgJ8N/2Ovh7bfB7wHN8pvoo/shMZ4LfaSiMxx1MERf/ar9sPAn7LH7N3wxmS68B+BtF02eM5WaOzjMwI9JGBf9a98AAGBWSyDF16kquKrqPN8Xso8rfrN3lY9SXG+WYPDww2W4SdRQ+B4io5xjre6pRtTvfW+up+cv7H//AATg+FX7MdynjzxHMfFnjeTLvqt2vyQO/wB77OjFtpOeZGLSH1AOK/Rqiivo8FgaGEpKjh4KMV/V33fmz4LNs5xuZ4h4rHVXOb6vouyWyS6JJIKKKK6zzAooooAKKKKAILr/AI9pP90/yrjq7G6/49pP90/yrjqAP//Z"), itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(true), allowMenu: Binding.constant(true), audioPlayer: .constant(nil), playbackState: .constant(.noPlayback), playbackTime: .constant(nil)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello", quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directSnd), itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "hi", chatDir: .directRcv), itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "👍", .sndSent(sndProgress: .complete), quotedItem: CIQuote.getSample(1, .now, "Hello too", chatDir: .directRcv), itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this covers -", .rcvRead, itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too!!! this text has the time on the same line ", .rcvRead, itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "https://simplex.chat", .rcvRead, itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "chaT@simplex.chat", .rcvRead, itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello", quotedItem: CIQuote.getSample(1, .now, "hi there hello hello hello ther hello hello", chatDir: .directSnd, image: "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAuKADAAQAAAABAAAAYAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAYAC4AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAgEBAgMCAgIDBAMDAwMEBgQEBAQEBgcGBgYGBgYHBwcHBwcHBwgICAgICAkJCQkJCwsLCwsLCwsLC//bAEMBAgICAwMDBQMDBQsIBggLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLC//dAAQADP/aAAwDAQACEQMRAD8A/v4ooooAKKKKACiiigAooooAKK+CP2vP+ChXwZ/ZPibw7dMfEHi2VAYdGs3G9N33TO/IiU9hgu3ZSOa/NzXNL/4KJ/td6JJ49+NXiq2+Cvw7kG/ZNKbDMLcjKblmfI/57SRqewrwMdxBRo1HQoRdWqt1HaP+KT0j838j7XKOCMXiqEcbjKkcPh5bSne8/wDr3BXlN+is+5+43jb45/Bf4bs0fj/xZpGjSL1jvL2KF/8AvlmDfpXjH/DfH7GQuPsv/CydD35x/wAfIx+fT9a/AO58D/8ABJj4UzvF4v8AFfif4l6mp/evpkfkWzP3w2Isg+omb61X/wCF0/8ABJr/AI9f+FQeJPL6ed9vbzPrj7ZivnavFuIT+KhHyc5Sf3wjY+7w/hlgZQv7PF1P70aUKa+SqTUvwP6afBXx2+CnxIZYvAHi3R9ZkfpHZ3sUz/8AfKsW/SvVq/lItvBf/BJX4rTLF4V8UeJ/hpqTH91JqUfn2yv2y2JcD3MqfUV9OaFon/BRH9krQ4vH3wI8XW3xq+HkY3+XDKb/ABCvJxHuaZMDr5Ergd1ruwvFNVrmq0VOK3lSkp29Y6SS+R5GY+HGGi1DD4qVKo9oYmm6XN5RqK9Nvsro/obor4A/ZC/4KH/Bv9qxV8MLnw54vjU+bo9443SFPvG3k4EoHdcB17rjmvv+vqcHjaGKpKth5qUX1X9aPyZ+b5rlOMy3ESwmOpOFRdH+aezT6NXTCiiiuo84KKKKACiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/Q/v4ooooAKKKKACiiigAr8tf+ChP7cWs/BEWfwD+A8R1P4k+JQkUCQr5rWUc52o+zndNIf9Up4H324wD9x/tDfGjw/wDs9fBnX/i/4jAeHRrZpI4c4M87YWKIe7yFV9gc9q/n6+B3iOb4GfCLxL/wU1+Oypq3jzxndT2nhK2uBwZptyvcBeoQBSq4xthjwPvivluIs0lSthKM+WUk5Sl/JBbtebekfM/R+BOHaeIcszxVL2kISUKdP/n7WlrGL/uxXvT8u6uizc6b8I/+CbmmRePPi9HD8Q/j7rifbktLmTz7bSGm582ZzktITyX++5+5tX5z5L8LPgv+0X/wVH12+8ZfEbxneW/2SRxB9o02eTSosdY4XRlgjYZGV++e5Jr8xvF3i7xN4+8UX/jXxney6jquqTNcXVzMcvJI5ySfQdgBwBgDgV+sP/BPX9jj9oL9oXw9H4tuvG2s+DfAVlM8VsthcyJLdSBsyCBNwREDZ3SEHLcBTgkfmuX4j+0MXHB06LdBXagna/8AenK6u+7el9Ej9+zvA/2Jls81r4uMcY7J1px5lHf93ShaVo9FFJNq8pMyPil/wRs/aj8D6dLq3gq70vxdHECxgtZGtrogf3UmAQn2EmT2r8rPEPh3xB4R1u58M+KrGfTdRsnMdxa3MbRTROOzKwBBr+674VfCnTfhNoI0DTtX1jWFAGZtYvpL2U4934X/AICAK8V/aW/Yf/Z9/areHUvibpkkerWsRhg1KxkMFyqHkBiMrIAeQJFYDJxjJr6bNPD+nOkqmAfLP+WTuvk7XX4/I/PeHvG6tSxDo5zH2lLpUhHll6uN7NelmvPY/iir2T4KftA/GD9njxMvir4Q65caTPkGWFTutrgD+GaE/I4+oyOxB5r2n9tb9jTxj+x18RYvD+pTtqmgaqrS6VqezZ5qpjfHIBwsseRuA4IIYdcD4yr80q0sRgcQ4SvCpB+jT8mvzP6Bw2JwOcYGNany1aFRdVdNdmn22aauno9T9tLO0+D/APwUr02Txd8NI4Ph38ftGT7b5NtIYLXWGh58yJwQVkBGd/8ArEP3i6fMP0R/4J7ftw6/8YZ7z9nb9oGJtN+JPhoPFIJ18p75IPlclegnj/5aKOGHzrxnH8rPhXxT4j8D+JbHxj4QvZdO1TTJkuLW5hba8UqHIIP8x0I4PFfsZ8bPEdx+0N8FvDv/AAUl+CgXSfiJ4EuYLXxZBbDALw4CXO0clMEZznMLlSf3Zr7PJM+nzyxUF+9ir1IrRVILeVtlOO+lrr5n5RxfwbRdKGXVXfDzfLRm9ZUKr+GDlq3RqP3UnfllZfy2/ptorw/9m/43aF+0X8FNA+L+gARpq1uGnhByYLlCUmiP+44IHqMHvXuFfsNGtCrTjVpu8ZJNPyZ/LWKwtXDVp4evG04Nxa7NOzX3hRRRWhzhRRRQBBdf8e0n+6f5Vx1djdf8e0n+6f5Vx1AH/9H+/iiiigAooooAKKKKAPw9/wCCvXiPWviH4q+F/wCyN4XlKT+K9TS6uQvoXFvAT7AvI3/AQe1fnF/wVO+IOnXfxx034AeDj5Xhv4ZaXb6TawKfkE7Ro0rY6bgvlofdT61+h3xNj/4Tv/gtd4Q0W/8Anh8P6THLGp6Ax21xOD/324Nfg3+0T4kufGH7QHjjxRdtukvte1GXJ9PPcKPwAAr8a4pxUpLEz6zq8n/btOK0+cpX9Uf1d4c5bCDy+lbSlh3W/wC38RNq/qoQcV5M8fjiaeRYEOGchR9TxX9svw9+GHijSvgB4I+Gnwr1ceGbGztYY728gijluhbohLLAJVeJZJJCN0jo+0Zwu4gj+JgO8REsf3l+YfUV/bf8DNVm+Mv7KtkNF1CTTZ9Z0d4Ir2D/AFls9zF8sidPmj3hhz1Fel4YyhGtiHpzWjur6e9f9Dw/H9VXQwFvgvUv62hb8Oa3zPoDwfp6aPoiaONXuNaa1Zo3ubp43nLDqrmJEXI/3QfWukmjMsTRBihYEbl6jPcZ7ivxk/4JMf8ABOv9ob9hBvFdr8ZvGOma9Yak22wttLiYGV2kMkl1dzSIkkkzcKisX8tSwDYNfs/X7Bj6NOlXlCjUU4/zJWv8j+ZsNUnOmpThyvtufj/+1Z8Hf2bPi58PviF8Avh/4wl1j4iaBZjXG0m71qfU7i3u4FMqt5VxLL5LzR70Kx7AVfJXAXH8sysGUMOh5r+vzwl+wD+y78KP2wPEX7bGn6xqFv4g8QmWa70+fUFGlrdTRmGS4EGATIY2dRvdlXe+0DPH83Nh+x58bPFev3kljpSaVYPcymGS+kEX7oudp2DL/dx/DX4Z4xZxkmCxGHxdTGRTlG0ueUU7q3S93a7S69Oh/SngTnNSjgcZhMc1CnCSlC70966dr/4U7Lq79T5Kr9MP+CWfxHsNH+P138EPF2JvDfxL0640a9gc/I0vls0Rx6kb4x/v1x3iz9hmHwV4KuPFHiLxlaWkltGzt5sBSAsBkIHL7iT0GFJJ7V8qfAnxLc+D/jd4N8V2bFJdP1vT5wR/szoT+YyK/NeD+Lcvx+Ijisuq88ackpPlklruveSvdX2ufsmavC5zlWKw9CV7xaTs1aSV4tXS1Ukmrdj9/P8Agkfrus/DD4ifFP8AY/8AEkrPJ4Z1F7y1DeiSG3mI9m2wv/wI1+5Ffhd4Ki/4Qf8A4Lb+INM0/wCSHxDpDySqOhL2cMx/8fizX7o1/RnC7ccLPDP/AJdTnBeid1+DP5M8RkqmZUselZ4ijSqv1lG0vvcWwooor6Q+BCiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/S/v4ooooAKKKKACiiigD8LfiNIfBP/BbLwpq9/wDJDr2kJHGTwCZLS4gH/j0eK/Bj9oPw7c+Evj3428M3ilZLHXtRiIPoJ3x+Ywa/fL/grnoWsfDPx98K/wBrzw5EzyeGNSS0uSvokguYQfZtsy/8CFfnB/wVP+HNho/7QFp8bvCeJvDnxK0231mznQfI0vlqsoz6kbJD/v1+M8U4WUViYW1hV5/+3akVr/4FG3qz+r/DnMYTeX1b6VcP7L/t/Dzenq4Tcl5I/M2v6yP+CR3j4eLP2XbLRZZN0uku9sRnp5bMB/45sr+Tev3u/wCCJXj7yNW8T/DyZ+C6XUak9pUw36xD865uAcV7LNFTf24tfd736Hd405d9Y4cddLWlOMvk7wf/AKUvuP6Kq/P/APaa+InjJfF8vge3lez06KONgIyVM+8ZJYjkgHIx045r9AK/Gr/gsB8UPHXwg8N+AvFfgV4oWmv7u3uTJEsiyL5SsiNkZxkMeCDmvU8bsgzPN+Fa+FyrEujUUot6tKcdnBtapO6fny2ejZ/OnAOFWJzqjheVOU+ZK+yaTlfr2t8z85td/b18H6D4n1DQLrw5fSLY3Elv5okRWcxsVJKMAVyR0yTivEPHf7f3jjVFe18BaXb6PGeBPcH7RN9QMBAfqGrFP7UPwj8c3f2/4y/DuzvbxgA93ZNtd8dyGwT+Lmuvh/aP/ZT8IxC58EfD0y3Y5UzwxKAf99mlP5Cv49wvCeBwUoc3D9Sday3qRlTb73c7Wf8Aej8j+rKWVUKLV8vlKf8AiTj/AOlW+9Hw74w8ceNvHl8NX8bajc6jK2SjTsSo/wBxeFUf7orovgf4dufF3xp8H+F7NS0uoa3p8Cgf7c6A/pW98avjx4q+NmoW0mswW9jY2G/7LaWy4WPfjJLHlicD0HoBX13/AMEtPhrZeI/2jH+L3inEPh34cWE+t31w/wBxJFRliBPqPmkH/XOv3fhXCVa/1ahUoRoybV4RacYq/dKK0jq7Ky1s3uezm+PeByeviqkFBxhK0U767RirJattLTqz9H/CMg8af8Futd1DT/ni8P6OySsOxSyiiP8A49Niv3Qr8NP+CS+j6t8V/iv8V/2wdfiZD4i1B7K0LDtLJ9olUf7imFfwr9y6/oLhe88LUxPSrUnNejdl+CP5G8RWqeY0cAnd4ejSpP8AxRjd/c5NBRRRX0h8CFFFFAEF1/x7Sf7p/lXHV2N1/wAe0n+6f5Vx1AH/0/7+KKKKACiiigAooooA8M/aT+B+iftGfBLxB8INcIjGrWxFvORnyLmMh4ZB/uSAE46jI71+AfwU8N3H7SXwL8Qf8E5fjFt0r4kfD65nuvCstycbmhz5ltuPVcE4x1idWHEdf031+UX/AAUL/Yj8T/FG/sv2mP2c5H074keGtkoFufLe+jg5Taennx9Ezw6/Ie2PleI8slUtjKUOZpOM4/zwe6X96L1j5/cfpPAXEMKF8rxNX2cZSU6VR7Uq0dE3/cmvcn5dldn8r/iXw3r/AIN8Q3vhPxXZy6fqemzPb3VtMNskUsZwysPY/n1HFfe3/BL3x/8A8IP+1bptvK+2HVbeSBvdoyso/RWH419SX8fwg/4Kc6QmleIpLfwB8f8ASI/ssiXCGC11kwfLtZSNwkGMbceZH0w6Dj88tM+HvxW/ZK/aO8OQ/FvR7nQ7uw1OElpV/czQs+x2ilGUkUqTypPvivy3DYWWX46hjaT56HOrSXa+ql/LK26fy0P6LzDMYZ3lGMynEx9ni/ZyvTfV2bjKD+3BtJqS9HZn9gnxB/aM+Cvwp8XWXgj4ja/Bo+o6hB9ogW5DrG0ZYoCZNvlr8wI+Zh0r48/4KkfDey+NP7GOqeIPDUsV7L4elh1u0khYOskcOVl2MCQcwu5GDyRXwx/wVBnbVPH3gjxGeVvPDwUt2LxzOW/9Cr87tO8PfFXVdPisbDS9avNImbzLNILa4mtXfo5j2KULZwDjmvqs+4srKvi8rqYfnjays2nqlq9JX3v0P4FwfiDisjzqNanQU3RnGUbNq9rOz0ej207nxZovhrV9enMNhHwpwztwq/U+vt1qrrWlT6JqUumXBDNHj5l6EEZr7U+IHhHxF8JvEUHhL4j2Umiald2sV/Hb3Q8t2hnztbB75BDKfmVgQQCK8e0f4N/E349/FRvBvwh0a41y+YRq/kD91ECPvSyHCRqPVmFfl8aNZ1vYcj59rWd79rbn9T+HPjFnnEPE1WhmmEWEwKw8qkVJNbSppTdSSimmpO1ko2a3aueH+H/D+ueLNds/DHhi0lv9R1CZLe2toV3SSyyHCqoHUk1+yfxl8N3X7Ln7P+h/8E9/hOF1X4nfEm4gufFDWp3FBMR5dqGHRTgLzx5au5wJKtaZZ/B7/gmFpBhsJLbx78fdVi+zwQWyma00UzjbgAfMZDnGMCSToAiElvv/AP4J7fsS+LPh5q15+1H+0q76h8R/Em+ZUuSHksI5/vFj0E8g4YDiNPkH8VfeZJkVTnlhYfxpK02tqUHur7c8trdFfzt9dxdxjQ9lDMKi/wBlpvmpRejxFVfDK26o03713bmla2yv90/sw/ArRv2bvgboHwh0crK2mQZup1GPPu5Tvmk9fmcnGei4HavfKKK/YaFGFGnGlTVoxSSXkj+WMXi6uKr1MTXlec25N923dsKKKK1OcKKKKAILr/j2k/3T/KuOrsbr/j2k/wB0/wAq46gD/9T+/iiiigAooooAKKKKACiiigD87P2wf+Ccnwm/ahmbxvosh8K+NY8NHq1onyzOn3ftEYK7yMcSKVkX1IAFfnT4m8f/ALdv7L+gyfDn9rjwFb/GLwFD8q3ssf2srGOjfaAjspA6GeMMOzV/RTRXz+N4eo1akq+Hm6VR7uNrS/xRekvzPuMo45xOGoQweOpRxFCPwqd1KH/XuorSh8m0uiPwz0L/AIKEf8E3vi6miH4saHd6Xc6B5gs4tWs3vYIPNILAGFpA65UcSLxjgCvtS1/4KT/sLWVlHFZePrCGCJAqRJa3K7VHQBRFxj0xXv8A48/Zc/Zx+J0z3Xj3wPoupzyHLTS2cfnE+8iqH/WvGP8Ah23+w953n/8ACu9PznOPMn2/98+bj9K5oYTOqMpSpyoyb3k4yjJ2015Xqac/BNSbrPD4mlKW6hKlJf8AgUkpP5n5zfta/tof8Ex/jPq+k+IPHelan491HQlljtI7KGWyikWUqSkryNCzJlcgc4JPHNcZ4V+Iv7c37TGgJ8N/2Ovh7bfB7wHN8pvoo/shMZ4LfaSiMxx1MERf/ar9sPAn7LH7N3wxmS68B+BtF02eM5WaOzjMwI9JGBf9a98AAGBWSyDF16kquKrqPN8Xso8rfrN3lY9SXG+WYPDww2W4SdRQ+B4io5xjre6pRtTvfW+up+cv7H//AATg+FX7MdynjzxHMfFnjeTLvqt2vyQO/wB77OjFtpOeZGLSH1AOK/Rqiivo8FgaGEpKjh4KMV/V33fmz4LNs5xuZ4h4rHVXOb6vouyWyS6JJIKKKK6zzAooooAKKKKAILr/AI9pP90/yrjq7G6/49pP90/yrjqAP//Z"), itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) + FramedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .groupRcv(groupMember: GroupMember.sampleData), .now, "hello there this is a long text", quotedItem: CIQuote.getSample(1, .now, "hi there", chatDir: .directSnd, image: "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAuKADAAQAAAABAAAAYAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAYAC4AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAgEBAgMCAgIDBAMDAwMEBgQEBAQEBgcGBgYGBgYHBwcHBwcHBwgICAgICAkJCQkJCwsLCwsLCwsLC//bAEMBAgICAwMDBQMDBQsIBggLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLC//dAAQADP/aAAwDAQACEQMRAD8A/v4ooooAKKKKACiiigAooooAKK+CP2vP+ChXwZ/ZPibw7dMfEHi2VAYdGs3G9N33TO/IiU9hgu3ZSOa/NzXNL/4KJ/td6JJ49+NXiq2+Cvw7kG/ZNKbDMLcjKblmfI/57SRqewrwMdxBRo1HQoRdWqt1HaP+KT0j838j7XKOCMXiqEcbjKkcPh5bSne8/wDr3BXlN+is+5+43jb45/Bf4bs0fj/xZpGjSL1jvL2KF/8AvlmDfpXjH/DfH7GQuPsv/CydD35x/wAfIx+fT9a/AO58D/8ABJj4UzvF4v8AFfif4l6mp/evpkfkWzP3w2Isg+omb61X/wCF0/8ABJr/AI9f+FQeJPL6ed9vbzPrj7ZivnavFuIT+KhHyc5Sf3wjY+7w/hlgZQv7PF1P70aUKa+SqTUvwP6afBXx2+CnxIZYvAHi3R9ZkfpHZ3sUz/8AfKsW/SvVq/lItvBf/BJX4rTLF4V8UeJ/hpqTH91JqUfn2yv2y2JcD3MqfUV9OaFon/BRH9krQ4vH3wI8XW3xq+HkY3+XDKb/ABCvJxHuaZMDr5Ergd1ruwvFNVrmq0VOK3lSkp29Y6SS+R5GY+HGGi1DD4qVKo9oYmm6XN5RqK9Nvsro/obor4A/ZC/4KH/Bv9qxV8MLnw54vjU+bo9443SFPvG3k4EoHdcB17rjmvv+vqcHjaGKpKth5qUX1X9aPyZ+b5rlOMy3ESwmOpOFRdH+aezT6NXTCiiiuo84KKKKACiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/Q/v4ooooAKKKKACiiigAr8tf+ChP7cWs/BEWfwD+A8R1P4k+JQkUCQr5rWUc52o+zndNIf9Up4H324wD9x/tDfGjw/wDs9fBnX/i/4jAeHRrZpI4c4M87YWKIe7yFV9gc9q/n6+B3iOb4GfCLxL/wU1+Oypq3jzxndT2nhK2uBwZptyvcBeoQBSq4xthjwPvivluIs0lSthKM+WUk5Sl/JBbtebekfM/R+BOHaeIcszxVL2kISUKdP/n7WlrGL/uxXvT8u6uizc6b8I/+CbmmRePPi9HD8Q/j7rifbktLmTz7bSGm582ZzktITyX++5+5tX5z5L8LPgv+0X/wVH12+8ZfEbxneW/2SRxB9o02eTSosdY4XRlgjYZGV++e5Jr8xvF3i7xN4+8UX/jXxney6jquqTNcXVzMcvJI5ySfQdgBwBgDgV+sP/BPX9jj9oL9oXw9H4tuvG2s+DfAVlM8VsthcyJLdSBsyCBNwREDZ3SEHLcBTgkfmuX4j+0MXHB06LdBXagna/8AenK6u+7el9Ej9+zvA/2Jls81r4uMcY7J1px5lHf93ShaVo9FFJNq8pMyPil/wRs/aj8D6dLq3gq70vxdHECxgtZGtrogf3UmAQn2EmT2r8rPEPh3xB4R1u58M+KrGfTdRsnMdxa3MbRTROOzKwBBr+674VfCnTfhNoI0DTtX1jWFAGZtYvpL2U4934X/AICAK8V/aW/Yf/Z9/areHUvibpkkerWsRhg1KxkMFyqHkBiMrIAeQJFYDJxjJr6bNPD+nOkqmAfLP+WTuvk7XX4/I/PeHvG6tSxDo5zH2lLpUhHll6uN7NelmvPY/iir2T4KftA/GD9njxMvir4Q65caTPkGWFTutrgD+GaE/I4+oyOxB5r2n9tb9jTxj+x18RYvD+pTtqmgaqrS6VqezZ5qpjfHIBwsseRuA4IIYdcD4yr80q0sRgcQ4SvCpB+jT8mvzP6Bw2JwOcYGNany1aFRdVdNdmn22aauno9T9tLO0+D/APwUr02Txd8NI4Ph38ftGT7b5NtIYLXWGh58yJwQVkBGd/8ArEP3i6fMP0R/4J7ftw6/8YZ7z9nb9oGJtN+JPhoPFIJ18p75IPlclegnj/5aKOGHzrxnH8rPhXxT4j8D+JbHxj4QvZdO1TTJkuLW5hba8UqHIIP8x0I4PFfsZ8bPEdx+0N8FvDv/AAUl+CgXSfiJ4EuYLXxZBbDALw4CXO0clMEZznMLlSf3Zr7PJM+nzyxUF+9ir1IrRVILeVtlOO+lrr5n5RxfwbRdKGXVXfDzfLRm9ZUKr+GDlq3RqP3UnfllZfy2/ptorw/9m/43aF+0X8FNA+L+gARpq1uGnhByYLlCUmiP+44IHqMHvXuFfsNGtCrTjVpu8ZJNPyZ/LWKwtXDVp4evG04Nxa7NOzX3hRRRWhzhRRRQBBdf8e0n+6f5Vx1djdf8e0n+6f5Vx1AH/9H+/iiiigAooooAKKKKAPw9/wCCvXiPWviH4q+F/wCyN4XlKT+K9TS6uQvoXFvAT7AvI3/AQe1fnF/wVO+IOnXfxx034AeDj5Xhv4ZaXb6TawKfkE7Ro0rY6bgvlofdT61+h3xNj/4Tv/gtd4Q0W/8Anh8P6THLGp6Ax21xOD/324Nfg3+0T4kufGH7QHjjxRdtukvte1GXJ9PPcKPwAAr8a4pxUpLEz6zq8n/btOK0+cpX9Uf1d4c5bCDy+lbSlh3W/wC38RNq/qoQcV5M8fjiaeRYEOGchR9TxX9svw9+GHijSvgB4I+Gnwr1ceGbGztYY728gijluhbohLLAJVeJZJJCN0jo+0Zwu4gj+JgO8REsf3l+YfUV/bf8DNVm+Mv7KtkNF1CTTZ9Z0d4Ir2D/AFls9zF8sidPmj3hhz1Fel4YyhGtiHpzWjur6e9f9Dw/H9VXQwFvgvUv62hb8Oa3zPoDwfp6aPoiaONXuNaa1Zo3ubp43nLDqrmJEXI/3QfWukmjMsTRBihYEbl6jPcZ7ivxk/4JMf8ABOv9ob9hBvFdr8ZvGOma9Yak22wttLiYGV2kMkl1dzSIkkkzcKisX8tSwDYNfs/X7Bj6NOlXlCjUU4/zJWv8j+ZsNUnOmpThyvtufj/+1Z8Hf2bPi58PviF8Avh/4wl1j4iaBZjXG0m71qfU7i3u4FMqt5VxLL5LzR70Kx7AVfJXAXH8sysGUMOh5r+vzwl+wD+y78KP2wPEX7bGn6xqFv4g8QmWa70+fUFGlrdTRmGS4EGATIY2dRvdlXe+0DPH83Nh+x58bPFev3kljpSaVYPcymGS+kEX7oudp2DL/dx/DX4Z4xZxkmCxGHxdTGRTlG0ueUU7q3S93a7S69Oh/SngTnNSjgcZhMc1CnCSlC70966dr/4U7Lq79T5Kr9MP+CWfxHsNH+P138EPF2JvDfxL0640a9gc/I0vls0Rx6kb4x/v1x3iz9hmHwV4KuPFHiLxlaWkltGzt5sBSAsBkIHL7iT0GFJJ7V8qfAnxLc+D/jd4N8V2bFJdP1vT5wR/szoT+YyK/NeD+Lcvx+Ijisuq88ackpPlklruveSvdX2ufsmavC5zlWKw9CV7xaTs1aSV4tXS1Ukmrdj9/P8Agkfrus/DD4ifFP8AY/8AEkrPJ4Z1F7y1DeiSG3mI9m2wv/wI1+5Ffhd4Ki/4Qf8A4Lb+INM0/wCSHxDpDySqOhL2cMx/8fizX7o1/RnC7ccLPDP/AJdTnBeid1+DP5M8RkqmZUselZ4ijSqv1lG0vvcWwooor6Q+BCiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/S/v4ooooAKKKKACiiigD8LfiNIfBP/BbLwpq9/wDJDr2kJHGTwCZLS4gH/j0eK/Bj9oPw7c+Evj3428M3ilZLHXtRiIPoJ3x+Ywa/fL/grnoWsfDPx98K/wBrzw5EzyeGNSS0uSvokguYQfZtsy/8CFfnB/wVP+HNho/7QFp8bvCeJvDnxK0231mznQfI0vlqsoz6kbJD/v1+M8U4WUViYW1hV5/+3akVr/4FG3qz+r/DnMYTeX1b6VcP7L/t/Dzenq4Tcl5I/M2v6yP+CR3j4eLP2XbLRZZN0uku9sRnp5bMB/45sr+Tev3u/wCCJXj7yNW8T/DyZ+C6XUak9pUw36xD865uAcV7LNFTf24tfd736Hd405d9Y4cddLWlOMvk7wf/AKUvuP6Kq/P/APaa+InjJfF8vge3lez06KONgIyVM+8ZJYjkgHIx045r9AK/Gr/gsB8UPHXwg8N+AvFfgV4oWmv7u3uTJEsiyL5SsiNkZxkMeCDmvU8bsgzPN+Fa+FyrEujUUot6tKcdnBtapO6fny2ejZ/OnAOFWJzqjheVOU+ZK+yaTlfr2t8z85td/b18H6D4n1DQLrw5fSLY3Elv5okRWcxsVJKMAVyR0yTivEPHf7f3jjVFe18BaXb6PGeBPcH7RN9QMBAfqGrFP7UPwj8c3f2/4y/DuzvbxgA93ZNtd8dyGwT+Lmuvh/aP/ZT8IxC58EfD0y3Y5UzwxKAf99mlP5Cv49wvCeBwUoc3D9Sday3qRlTb73c7Wf8Aej8j+rKWVUKLV8vlKf8AiTj/AOlW+9Hw74w8ceNvHl8NX8bajc6jK2SjTsSo/wBxeFUf7orovgf4dufF3xp8H+F7NS0uoa3p8Cgf7c6A/pW98avjx4q+NmoW0mswW9jY2G/7LaWy4WPfjJLHlicD0HoBX13/AMEtPhrZeI/2jH+L3inEPh34cWE+t31w/wBxJFRliBPqPmkH/XOv3fhXCVa/1ahUoRoybV4RacYq/dKK0jq7Ky1s3uezm+PeByeviqkFBxhK0U767RirJattLTqz9H/CMg8af8Futd1DT/ni8P6OySsOxSyiiP8A49Niv3Qr8NP+CS+j6t8V/iv8V/2wdfiZD4i1B7K0LDtLJ9olUf7imFfwr9y6/oLhe88LUxPSrUnNejdl+CP5G8RWqeY0cAnd4ejSpP8AxRjd/c5NBRRRX0h8CFFFFAEF1/x7Sf7p/lXHV2N1/wAe0n+6f5Vx1AH/0/7+KKKKACiiigAooooA8M/aT+B+iftGfBLxB8INcIjGrWxFvORnyLmMh4ZB/uSAE46jI71+AfwU8N3H7SXwL8Qf8E5fjFt0r4kfD65nuvCstycbmhz5ltuPVcE4x1idWHEdf031+UX/AAUL/Yj8T/FG/sv2mP2c5H074keGtkoFufLe+jg5Taennx9Ezw6/Ie2PleI8slUtjKUOZpOM4/zwe6X96L1j5/cfpPAXEMKF8rxNX2cZSU6VR7Uq0dE3/cmvcn5dldn8r/iXw3r/AIN8Q3vhPxXZy6fqemzPb3VtMNskUsZwysPY/n1HFfe3/BL3x/8A8IP+1bptvK+2HVbeSBvdoyso/RWH419SX8fwg/4Kc6QmleIpLfwB8f8ASI/ssiXCGC11kwfLtZSNwkGMbceZH0w6Dj88tM+HvxW/ZK/aO8OQ/FvR7nQ7uw1OElpV/czQs+x2ilGUkUqTypPvivy3DYWWX46hjaT56HOrSXa+ql/LK26fy0P6LzDMYZ3lGMynEx9ni/ZyvTfV2bjKD+3BtJqS9HZn9gnxB/aM+Cvwp8XWXgj4ja/Bo+o6hB9ogW5DrG0ZYoCZNvlr8wI+Zh0r48/4KkfDey+NP7GOqeIPDUsV7L4elh1u0khYOskcOVl2MCQcwu5GDyRXwx/wVBnbVPH3gjxGeVvPDwUt2LxzOW/9Cr87tO8PfFXVdPisbDS9avNImbzLNILa4mtXfo5j2KULZwDjmvqs+4srKvi8rqYfnjays2nqlq9JX3v0P4FwfiDisjzqNanQU3RnGUbNq9rOz0ej207nxZovhrV9enMNhHwpwztwq/U+vt1qrrWlT6JqUumXBDNHj5l6EEZr7U+IHhHxF8JvEUHhL4j2Umiald2sV/Hb3Q8t2hnztbB75BDKfmVgQQCK8e0f4N/E349/FRvBvwh0a41y+YRq/kD91ECPvSyHCRqPVmFfl8aNZ1vYcj59rWd79rbn9T+HPjFnnEPE1WhmmEWEwKw8qkVJNbSppTdSSimmpO1ko2a3aueH+H/D+ueLNds/DHhi0lv9R1CZLe2toV3SSyyHCqoHUk1+yfxl8N3X7Ln7P+h/8E9/hOF1X4nfEm4gufFDWp3FBMR5dqGHRTgLzx5au5wJKtaZZ/B7/gmFpBhsJLbx78fdVi+zwQWyma00UzjbgAfMZDnGMCSToAiElvv/AP4J7fsS+LPh5q15+1H+0q76h8R/Em+ZUuSHksI5/vFj0E8g4YDiNPkH8VfeZJkVTnlhYfxpK02tqUHur7c8trdFfzt9dxdxjQ9lDMKi/wBlpvmpRejxFVfDK26o03713bmla2yv90/sw/ArRv2bvgboHwh0crK2mQZup1GPPu5Tvmk9fmcnGei4HavfKKK/YaFGFGnGlTVoxSSXkj+WMXi6uKr1MTXlec25N923dsKKKK1OcKKKKAILr/j2k/3T/KuOrsbr/j2k/wB0/wAq46gD/9T+/iiiigAooooAKKKKACiiigD87P2wf+Ccnwm/ahmbxvosh8K+NY8NHq1onyzOn3ftEYK7yMcSKVkX1IAFfnT4m8f/ALdv7L+gyfDn9rjwFb/GLwFD8q3ssf2srGOjfaAjspA6GeMMOzV/RTRXz+N4eo1akq+Hm6VR7uNrS/xRekvzPuMo45xOGoQweOpRxFCPwqd1KH/XuorSh8m0uiPwz0L/AIKEf8E3vi6miH4saHd6Xc6B5gs4tWs3vYIPNILAGFpA65UcSLxjgCvtS1/4KT/sLWVlHFZePrCGCJAqRJa3K7VHQBRFxj0xXv8A48/Zc/Zx+J0z3Xj3wPoupzyHLTS2cfnE+8iqH/WvGP8Ah23+w953n/8ACu9PznOPMn2/98+bj9K5oYTOqMpSpyoyb3k4yjJ2015Xqac/BNSbrPD4mlKW6hKlJf8AgUkpP5n5zfta/tof8Ex/jPq+k+IPHelan491HQlljtI7KGWyikWUqSkryNCzJlcgc4JPHNcZ4V+Iv7c37TGgJ8N/2Ovh7bfB7wHN8pvoo/shMZ4LfaSiMxx1MERf/ar9sPAn7LH7N3wxmS68B+BtF02eM5WaOzjMwI9JGBf9a98AAGBWSyDF16kquKrqPN8Xso8rfrN3lY9SXG+WYPDww2W4SdRQ+B4io5xjre6pRtTvfW+up+cv7H//AATg+FX7MdynjzxHMfFnjeTLvqt2vyQO/wB77OjFtpOeZGLSH1AOK/Rqiivo8FgaGEpKjh4KMV/V33fmz4LNs5xuZ4h4rHVXOb6vouyWyS6JJIKKKK6zzAooooAKKKKAILr/AI9pP90/yrjq7G6/49pP90/yrjqAP//Z"), itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in }, allowMenu: Binding.constant(true)) } + .environment(\.revealed, false) .previewLayout(.fixed(width: 360, height: 200)) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/FullScreenMediaView.swift b/apps/ios/Shared/Views/Chat/ChatItem/FullScreenMediaView.swift index 0e721acdcb..10e5efa298 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/FullScreenMediaView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/FullScreenMediaView.swift @@ -14,11 +14,11 @@ import AVKit struct FullScreenMediaView: View { @EnvironmentObject var m: ChatModel @State var chatItem: ChatItem + var scrollToItemId: ((ChatItem.ID) -> Void)? @State var image: UIImage? @State var player: AVPlayer? = nil @State var url: URL? = nil @Binding var showView: Bool - @State var scrollProxy: ScrollViewProxy? @State private var showNext = false @State private var nextImage: UIImage? @State private var nextPlayer: AVPlayer? @@ -71,9 +71,7 @@ struct FullScreenMediaView: View { let w = abs(t.width) if t.height > 60 && t.height > w * 2 { showView = false - if let proxy = scrollProxy { - proxy.scrollTo(chatItem.viewId) - } + scrollToItemId?(chatItem.id) } else if w > 60 && w > abs(t.height) * 2 && !scrolling { let previous = t.width > 0 scrolling = true @@ -128,7 +126,7 @@ struct FullScreenMediaView: View { .scaledToFit() } } - .onTapGesture { showView = false } + .onTapGesture { showView = false } // this is used in full screen view, onTapGesture works } private func videoView( _ player: AVPlayer, _ url: URL) -> some View { diff --git a/apps/ios/Shared/Views/Chat/ChatItem/IntegrityErrorItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/IntegrityErrorItemView.swift index 1aa0093c9a..47a30f6cf3 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/IntegrityErrorItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/IntegrityErrorItemView.swift @@ -11,6 +11,7 @@ import SimpleXChat struct IntegrityErrorItemView: View { @ObservedObject var chat: Chat + @EnvironmentObject var theme: AppTheme var msgError: MsgErrorType var chatItem: ChatItem @@ -30,8 +31,8 @@ struct IntegrityErrorItemView: View { case .msgBadHash: AlertManager.shared.showAlert(Alert( title: Text("Bad message hash"), - message: Text("The hash of the previous message is different.") + Text("\n") + - Text(decryptErrorReason) + Text("\n") + + message: Text("The hash of the previous message is different.") + textNewLine + + Text(decryptErrorReason) + textNewLine + Text("Please report it to the developers.") )) case .msgBadId: msgBadIdAlert() @@ -46,7 +47,7 @@ struct IntegrityErrorItemView: View { message: Text(""" The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. - """) + Text("\n") + + """) + textNewLine + Text("Please report it to the developers.") )) } @@ -54,6 +55,7 @@ struct IntegrityErrorItemView: View { struct CIMsgError: View { @ObservedObject var chat: Chat + @EnvironmentObject var theme: AppTheme var chatItem: ChatItem var onTap: () -> Void @@ -62,15 +64,14 @@ struct CIMsgError: View { Text(chatItem.content.text) .foregroundColor(.red) .italic() - CIMetaView(chat: chat, chatItem: chatItem) + CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary) .padding(.horizontal, 12) } .padding(.leading, 12) .padding(.vertical, 6) - .background(Color(uiColor: .tertiarySystemGroupedBackground)) - .cornerRadius(18) + .background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) } .textSelection(.disabled) - .onTapGesture(perform: onTap) + .simultaneousGesture(TapGesture().onEnded(onTap)) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift index cb0b61f537..87a9b2ce61 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift @@ -11,19 +11,18 @@ import SimpleXChat struct MarkedDeletedItemView: View { @EnvironmentObject var m: ChatModel - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme + @Environment(\.revealed) var revealed: Bool @ObservedObject var chat: Chat var chatItem: ChatItem - @Binding var revealed: Bool var body: some View { - (Text(mergedMarkedDeletedText).italic() + Text(" ") + chatItem.timestampText) + (Text(mergedMarkedDeletedText).italic() + textSpace + chatItem.timestampText) .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .padding(.horizontal, 12) .padding(.vertical, 6) - .background(chatItemFrameColor(chatItem, colorScheme)) - .cornerRadius(18) + .background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) } .textSelection(.disabled) } @@ -36,8 +35,8 @@ struct MarkedDeletedItemView: View { var blockedByAdmin = 0 var deleted = 0 var moderatedBy: Set = [] - while i < m.reversedChatItems.count, - let ci = .some(m.reversedChatItems[i]), + while i < ItemsModel.shared.reversedChatItems.count, + let ci = .some(ItemsModel.shared.reversedChatItems[i]), ci.mergeCategory == ciCategory, let itemDeleted = ci.meta.itemDeleted { switch itemDeleted { @@ -68,11 +67,15 @@ struct MarkedDeletedItemView: View { // same texts are in markedDeletedText in ChatPreviewView, but it returns String; // can be refactored into a single function if functions calling these are changed to return same type var markedDeletedText: LocalizedStringKey { - switch chatItem.meta.itemDeleted { - case let .moderated(_, byGroupMember): "moderated by \(byGroupMember.displayName)" - case .blocked: "blocked" - case .blockedByAdmin: "blocked by admin" - case .deleted, nil: "marked deleted" + if chatItem.meta.itemDeleted != nil, chatItem.isReport { + "archived report" + } else { + switch chatItem.meta.itemDeleted { + case let .moderated(_, byGroupMember): "moderated by \(byGroupMember.displayName)" + case .blocked: "blocked" + case .blockedByAdmin: "blocked by admin" + case .deleted, nil: "marked deleted" + } } } } @@ -80,7 +83,10 @@ struct MarkedDeletedItemView: View { struct MarkedDeletedItemView_Previews: PreviewProvider { static var previews: some View { Group { - MarkedDeletedItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(true)) + MarkedDeletedItemView( + chat: Chat.sampleData, + chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemDeleted: .deleted(deletedTs: .now)) + ).environment(\.revealed, true) } .previewLayout(.fixed(width: 360, height: 200)) } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift b/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift index ccd7ac0a12..e04584dfff 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift @@ -11,46 +11,74 @@ import SimpleXChat let uiLinkColor = UIColor(red: 0, green: 0.533, blue: 1, alpha: 1) -private let noTyping = Text(" ") - -private let typingIndicators: [Text] = [ - (typing(.black) + typing() + typing()), - (typing(.bold) + typing(.black) + typing()), - (typing() + typing(.bold) + typing(.black)), - (typing() + typing() + typing(.bold)) -] - -private func typing(_ w: Font.Weight = .light) -> Text { - Text(".").fontWeight(w) +private func typing(_ theme: AppTheme, _ descr: UIFontDescriptor, _ ws: [UIFont.Weight]) -> NSMutableAttributedString { + let res = NSMutableAttributedString() + for w in ws { + res.append(NSAttributedString(string: ".", attributes: [ + .font: UIFont.monospacedSystemFont(ofSize: descr.pointSize, weight: w), + .kern: -2 as NSNumber, + .foregroundColor: UIColor(theme.colors.secondary) + ])) + } + return res } struct MsgContentView: View { @ObservedObject var chat: Chat + @Environment(\.showTimestamp) var showTimestamp: Bool + @Environment(\.containerBackground) var containerBackground: UIColor + @EnvironmentObject var theme: AppTheme var text: String var formattedText: [FormattedText]? = nil + var textStyle: UIFont.TextStyle var sender: String? = nil var meta: CIMeta? = nil + var mentions: [String: CIMention]? = nil + var userMemberId: String? = nil var rightToLeft = false - var showSecrets: Bool + var prefix: NSAttributedString? = nil + @State private var showSecrets: Set = [] @State private var typingIdx = 0 @State private var timer: Timer? + @State private var typingIndicators: [NSAttributedString] = [] + @State private var noTyping = NSAttributedString(string: " ") + @State private var phase: CGFloat = 0 + + @AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false var body: some View { + let v = msgContentView() if meta?.isLive == true { - msgContentView() - .onAppear { switchTyping() } + v.onAppear { + let descr = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) + noTyping = NSAttributedString(string: " ", attributes: [ + .font: UIFont.monospacedSystemFont(ofSize: descr.pointSize, weight: .regular), + .kern: -2 as NSNumber, + .foregroundColor: UIColor(theme.colors.secondary) + ]) + switchTyping() + } .onDisappear(perform: stopTyping) .onChange(of: meta?.isLive, perform: switchTyping) .onChange(of: meta?.recent, perform: switchTyping) } else { - msgContentView() + v } } private func switchTyping(_: Bool? = nil) { if let meta = meta, meta.isLive && meta.recent { + if typingIndicators.isEmpty { + let descr = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) + typingIndicators = [ + typing(theme, descr, [.black, .light, .light]), + typing(theme, descr, [.bold, .black, .light]), + typing(theme, descr, [.light, .bold, .black]), + typing(theme, descr, [.light, .light, .bold]) + ] + } timer = timer ?? Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { _ in - typingIdx = (typingIdx + 1) % typingIndicators.count + typingIdx = typingIdx + 1 } } else { stopTyping() @@ -60,95 +88,276 @@ struct MsgContentView: View { private func stopTyping() { timer?.invalidate() timer = nil + typingIdx = 0 } - private func msgContentView() -> Text { - var v = messageText(text, formattedText, sender, showSecrets: showSecrets) + @inline(__always) + private func msgContentView() -> some View { + let r = messageText(text, formattedText, textStyle: textStyle, sender: sender, mentions: mentions, userMemberId: userMemberId, showSecrets: showSecrets, backgroundColor: containerBackground, prefix: prefix) + let s = r.string + let t: Text if let mt = meta { if mt.isLive { - v = v + typingIndicator(mt.recent) + s.append(typingIndicator(mt.recent)) } - v = v + reserveSpaceForMeta(mt) + t = Text(AttributedString(s)) + reserveSpaceForMeta(mt) + } else { + t = Text(AttributedString(s)) } - return v + return msgTextResultView(r, t, showSecrets: $showSecrets) } - private func typingIndicator(_ recent: Bool) -> Text { - return (recent ? typingIndicators[typingIdx] : noTyping) - .font(.body.monospaced()) - .kerning(-2) - .foregroundColor(.secondary) + @inline(__always) + private func typingIndicator(_ recent: Bool) -> NSAttributedString { + recent && !typingIndicators.isEmpty + ? typingIndicators[typingIdx % 4] + : noTyping } + @inline(__always) private func reserveSpaceForMeta(_ mt: CIMeta) -> Text { - (rightToLeft ? Text("\n") : Text(" ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, encrypted: nil, transparent: true) + (rightToLeft ? textNewLine : Text(verbatim: " ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp) } } -func messageText(_ text: String, _ formattedText: [FormattedText]?, _ sender: String?, icon: String? = nil, preview: Bool = false, showSecrets: Bool) -> Text { - let s = text - var res: Text - if let ft = formattedText, ft.count > 0 && ft.count <= 200 { - res = formatText(ft[0], preview, showSecret: showSecrets) - var i = 1 - while i < ft.count { - res = res + formatText(ft[i], preview, showSecret: showSecrets) - i = i + 1 - } - } else { - res = Text(s) - } - - if let i = icon { - res = Text(Image(systemName: i)).foregroundColor(Color(uiColor: .tertiaryLabel)) + Text(" ") + res - } - - if let s = sender { - let t = Text(s) - return (preview ? t : t.fontWeight(.medium)) + Text(": ") + res - } else { - return res - } +func msgTextResultView(_ r: MsgTextResult, _ t: Text, showSecrets: Binding>? = nil) -> some View { + t.if(r.hasSecrets, transform: hiddenSecretsView) + .if(r.handleTaps) { $0.overlay(handleTextTaps(r.string, showSecrets: showSecrets)) } } -private func formatText(_ ft: FormattedText, _ preview: Bool, showSecret: Bool) -> Text { - let t = ft.text - if let f = ft.format { - switch (f) { - case .bold: return Text(t).bold() - case .italic: return Text(t).italic() - case .strikeThrough: return Text(t).strikethrough() - case .snippet: return Text(t).font(.body.monospaced()) - case .secret: return - showSecret - ? Text(t) - : Text(AttributedString(t, attributes: AttributeContainer([ - .foregroundColor: UIColor.clear as Any, - .backgroundColor: UIColor.secondarySystemFill as Any - ]))) - case let .colored(color): return Text(t).foregroundColor(color.uiColor) - case .uri: return linkText(t, t, preview, prefix: "") - case let .simplexLink(linkType, simplexUri, smpHosts): - switch privacySimplexLinkModeDefault.get() { - case .description: return linkText(simplexLinkText(linkType, smpHosts), simplexUri, preview, prefix: "") - case .full: return linkText(t, simplexUri, preview, prefix: "") - case .browser: return linkText(t, simplexUri, preview, prefix: "") +@inline(__always) +private func handleTextTaps(_ s: NSAttributedString, showSecrets: Binding>? = nil) -> some View { + return GeometryReader { g in + Rectangle() + .fill(Color.clear) + .contentShape(Rectangle()) + .simultaneousGesture(DragGesture(minimumDistance: 0).onEnded { event in + let t = event.translation + if t.width * t.width + t.height * t.height > 100 { return } + let framesetter = CTFramesetterCreateWithAttributedString(s as CFAttributedString) + let path = CGPath(rect: CGRect(origin: .zero, size: g.size), transform: nil) + let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, s.length), path, nil) + let point = CGPoint(x: event.location.x, y: g.size.height - event.location.y) // Flip y for UIKit + var index: CFIndex? + if let lines = CTFrameGetLines(frame) as? [CTLine] { + var origins = [CGPoint](repeating: .zero, count: lines.count) + CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), &origins) + for i in 0 ..< lines.count { + let bounds = CTLineGetBoundsWithOptions(lines[i], .useOpticalBounds) + if bounds.offsetBy(dx: origins[i].x, dy: origins[i].y).contains(point) { + index = CTLineGetStringIndexForPosition(lines[i], point) + break + } + } + } + if let index, let (url, browser) = attributedStringLink(s, for: index) { + if browser { + openBrowserAlert(uri: url) + } else { + UIApplication.shared.open(url) + } + } + }) + } + + func attributedStringLink(_ s: NSAttributedString, for index: CFIndex) -> (URL, Bool)? { + var linkURL: URL? + var browser: Bool = false + s.enumerateAttributes(in: NSRange(location: 0, length: s.length)) { attrs, range, stop in + if index >= range.location && index < range.location + range.length { + if let url = attrs[linkAttrKey] as? NSURL { + linkURL = url.absoluteURL + browser = attrs[webLinkAttrKey] != nil + } else if let showSecrets, let i = attrs[secretAttrKey] as? Int { + if showSecrets.wrappedValue.contains(i) { + showSecrets.wrappedValue.remove(i) + } else { + showSecrets.wrappedValue.insert(i) + } + } + stop.pointee = true } - case .email: return linkText(t, t, preview, prefix: "mailto:") - case .phone: return linkText(t, t.replacingOccurrences(of: " ", with: ""), preview, prefix: "tel:") } - } else { - return Text(t) + return if let linkURL { (linkURL, browser) } else { nil } } } -private func linkText(_ s: String, _ link: String, _ preview: Bool, prefix: String, color: Color = Color(uiColor: uiLinkColor), uiColor: UIColor = uiLinkColor) -> Text { - preview - ? Text(s).foregroundColor(color).underline(color: color) - : Text(AttributedString(s, attributes: AttributeContainer([ - .link: NSURL(string: prefix + link) as Any, - .foregroundColor: uiColor as Any - ]))).underline() +func hiddenSecretsView(_ v: V) -> some View { + v.overlay( + GeometryReader { g in + let size = (g.size.width + g.size.height) / 1.4142 + Image("vertical_logo") + .resizable(resizingMode: .tile) + .frame(width: size, height: size) + .rotationEffect(.degrees(45), anchor: .center) + .position(x: g.size.width / 2, y: g.size.height / 2) + .clipped() + .saturation(0.65) + .opacity(0.35) + } + .mask(v) + ) +} + +private let linkAttrKey = NSAttributedString.Key("chat.simplex.app.link") + +private let webLinkAttrKey = NSAttributedString.Key("chat.simplex.app.webLink") + +private let secretAttrKey = NSAttributedString.Key("chat.simplex.app.secret") + +typealias MsgTextResult = (string: NSMutableAttributedString, hasSecrets: Bool, handleTaps: Bool) + +func messageText( + _ text: String, + _ formattedText: [FormattedText]?, + textStyle: UIFont.TextStyle = .body, + sender: String?, + preview: Bool = false, + mentions: [String: CIMention]?, + userMemberId: String?, + showSecrets: Set?, + backgroundColor: UIColor, + prefix: NSAttributedString? = nil +) -> MsgTextResult { + let res = NSMutableAttributedString() + let descr = UIFontDescriptor.preferredFontDescriptor(withTextStyle: textStyle) + let font = UIFont.preferredFont(forTextStyle: textStyle) + let plain: [NSAttributedString.Key: Any] = [ + .font: font, + .foregroundColor: UIColor.label + ] + let secretColor = backgroundColor.withAlphaComponent(1) + var link: [NSAttributedString.Key: Any]? + var hasSecrets = false + var handleTaps = false + + if let sender { + if preview { + res.append(NSAttributedString(string: sender + ": ", attributes: plain)) + } else { + var attrs = plain + attrs[.font] = UIFont(descriptor: descr.addingAttributes([.traits: [UIFontDescriptor.TraitKey.weight: UIFont.Weight.medium]]), size: descr.pointSize) + res.append(NSAttributedString(string: sender, attributes: attrs)) + res.append(NSAttributedString(string: ": ", attributes: plain)) + } + } + + if let prefix { + res.append(prefix) + } + + if let fts = formattedText, fts.count > 0 { + var bold: UIFont? + var italic: UIFont? + var snippet: UIFont? + var mention: UIFont? + var secretIdx: Int = 0 + for ft in fts { + var t = ft.text + var attrs = plain + switch (ft.format) { + case .bold: + bold = bold ?? UIFont(descriptor: descr.addingAttributes([.traits: [UIFontDescriptor.TraitKey.weight: UIFont.Weight.bold]]), size: descr.pointSize) + attrs[.font] = bold + case .italic: + italic = italic ?? UIFont(descriptor: descr.withSymbolicTraits(.traitItalic) ?? descr, size: descr.pointSize) + attrs[.font] = italic + case .strikeThrough: + attrs[.strikethroughStyle] = NSUnderlineStyle.single.rawValue + case .snippet: + snippet = snippet ?? UIFont.monospacedSystemFont(ofSize: descr.pointSize, weight: .regular) + attrs[.font] = snippet + case .secret: + if let showSecrets { + if !showSecrets.contains(secretIdx) { + attrs[.foregroundColor] = UIColor.clear + attrs[.backgroundColor] = secretColor + } + attrs[secretAttrKey] = secretIdx + secretIdx += 1 + handleTaps = true + } else { + attrs[.foregroundColor] = UIColor.clear + attrs[.backgroundColor] = secretColor + } + hasSecrets = true + case let .colored(color): + if let c = color.uiColor { + attrs[.foregroundColor] = UIColor(c) + } + case .uri: + attrs = linkAttrs() + if !preview { + let s = t.lowercased() + let link = s.hasPrefix("http://") || s.hasPrefix("https://") + ? t + : "https://" + t + attrs[linkAttrKey] = NSURL(string: link) + attrs[webLinkAttrKey] = true + handleTaps = true + } + case let .simplexLink(linkType, simplexUri, smpHosts): + attrs = linkAttrs() + if !preview { + attrs[linkAttrKey] = NSURL(string: simplexUri) + handleTaps = true + } + if case .description = privacySimplexLinkModeDefault.get() { + t = simplexLinkText(linkType, smpHosts) + } + case let .mention(memberName): + if let m = mentions?[memberName] { + mention = mention ?? UIFont(descriptor: descr.addingAttributes([.traits: [UIFontDescriptor.TraitKey.weight: UIFont.Weight.semibold]]), size: descr.pointSize) + attrs[.font] = mention + if let ref = m.memberRef { + let name: String = if let alias = ref.localAlias, alias != "" { + "\(alias) (\(ref.displayName))" + } else { + ref.displayName + } + if m.memberId == userMemberId { + attrs[.foregroundColor] = UIColor.tintColor + } + t = mentionText(name) + } else { + t = mentionText(memberName) + } + } + case .email: + attrs = linkAttrs() + if !preview { + attrs[linkAttrKey] = NSURL(string: "mailto:" + ft.text) + handleTaps = true + } + case .phone: + attrs = linkAttrs() + if !preview { + attrs[linkAttrKey] = NSURL(string: "tel:" + t.replacingOccurrences(of: " ", with: "")) + handleTaps = true + } + case .none: () + } + res.append(NSAttributedString(string: t, attributes: attrs)) + } + } else { + res.append(NSMutableAttributedString(string: text, attributes: plain)) + } + + return (string: res, hasSecrets: hasSecrets, handleTaps: handleTaps) + + func linkAttrs() -> [NSAttributedString.Key: Any] { + link = link ?? [ + .font: font, + .foregroundColor: uiLinkColor, + .underlineStyle: NSUnderlineStyle.single.rawValue + ] + return link! + } +} + +@inline(__always) +private func mentionText(_ name: String) -> String { + name.contains(" @") ? "@'\(name)'" : "@\(name)" } func simplexLinkText(_ linkType: SimplexLinkType, _ smpHosts: [String]) -> String { @@ -162,9 +371,9 @@ struct MsgContentView_Previews: PreviewProvider { chat: Chat.sampleData, text: chatItem.text, formattedText: chatItem.formattedText, + textStyle: .body, sender: chatItem.memberDisplayName, - meta: chatItem.meta, - showSecrets: false + meta: chatItem.meta ) .environmentObject(Chat.sampleData) } diff --git a/apps/ios/Shared/Views/Chat/ChatItemForwardingView.swift b/apps/ios/Shared/Views/Chat/ChatItemForwardingView.swift new file mode 100644 index 0000000000..dfc620c402 --- /dev/null +++ b/apps/ios/Shared/Views/Chat/ChatItemForwardingView.swift @@ -0,0 +1,132 @@ +// +// ChatItemForwardingView.swift +// SimpleX (iOS) +// +// Created by spaced4ndy on 12.04.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct ChatItemForwardingView: View { + @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme + @Environment(\.dismiss) var dismiss + + var chatItems: [ChatItem] + var fromChatInfo: ChatInfo + @Binding var composeState: ComposeState + + @State private var searchText: String = "" + @State private var alert: SomeAlert? + private let chatsToForwardTo = filterChatsToForwardTo(chats: ChatModel.shared.chats) + + var body: some View { + NavigationView { + forwardListView() + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button("Cancel") { + dismiss() + } + } + ToolbarItem(placement: .principal) { + Text("Forward") + .bold() + } + } + } + .modifier(ThemedBackground()) + .alert(item: $alert) { $0.alert } + } + + private func forwardListView() -> some View { + VStack(alignment: .leading) { + if !chatsToForwardTo.isEmpty { + List { + let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase + let chats = s == "" ? chatsToForwardTo : chatsToForwardTo.filter { foundChat($0, s) } + ForEach(chats) { chat in + forwardListChatView(chat) + .disabled(chatModel.deletedChats.contains(chat.chatInfo.id)) + } + } + .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always)) + .modifier(ThemedBackground(grouped: true)) + } else { + ZStack { + emptyList() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .modifier(ThemedBackground()) + } + } + } + + private func emptyList() -> some View { + Text("No filtered chats") + .foregroundColor(theme.colors.secondary) + .frame(maxWidth: .infinity) + } + + @ViewBuilder private func forwardListChatView(_ chat: Chat) -> some View { + let prohibited = chatItems.map { ci in + chat.prohibitedByPref( + hasSimplexLink: hasSimplexLink(ci.content.msgContent?.text), + isMediaOrFileAttachment: ci.content.msgContent?.isMediaOrFileAttachment ?? false, + isVoice: ci.content.msgContent?.isVoice ?? false + ) + }.contains(true) + + Button { + if prohibited { + alert = SomeAlert( + alert: mkAlert( + title: "Cannot forward message", + message: "Selected chat preferences prohibit this message." + ), + id: "forward prohibited by preferences" + ) + } else { + dismiss() + if chat.id == fromChatInfo.id { + composeState = ComposeState( + message: composeState.message, + preview: composeState.linkPreview != nil ? composeState.preview : .noPreview, + contextItem: .forwardingItems(chatItems: chatItems, fromChatInfo: fromChatInfo) + ) + } else { + composeState = ComposeState.init(forwardingItems: chatItems, fromChatInfo: fromChatInfo) + ItemsModel.shared.loadOpenChat(chat.id) + } + } + } label: { + HStack { + ChatInfoImage(chat: chat, size: 30) + .padding(.trailing, 2) + Text(chat.chatInfo.chatViewName) + .foregroundColor(prohibited ? theme.colors.secondary : theme.colors.onBackground) + .lineLimit(1) + if chat.chatInfo.incognito { + Spacer() + Image(systemName: "theatermasks") + .resizable() + .scaledToFit() + .frame(width: 22, height: 22) + .foregroundColor(theme.colors.secondary) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + } + } +} + +#Preview { + ChatItemForwardingView( + chatItems: [ChatItem.getSample(1, .directSnd, .now, "hello")], + fromChatInfo: .direct(contact: Contact.sampleData), + composeState: Binding.constant(ComposeState(message: "hello")) + ).environmentObject(CurrentColors.toAppTheme()) +} + diff --git a/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift b/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift index 8dd43cc01b..cd75d1b0cd 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift @@ -11,16 +11,21 @@ import SimpleXChat struct ChatItemInfoView: View { @EnvironmentObject var chatModel: ChatModel - @Environment(\.colorScheme) var colorScheme + @Environment(\.dismiss) var dismiss + @EnvironmentObject var theme: AppTheme var ci: ChatItem + var userMemberId: String? @Binding var chatItemInfo: ChatItemInfo? @State private var selection: CIInfoTab = .history @State private var alert: CIInfoViewAlert? = nil + @State private var messageStatusLimited: Bool = true + @State private var fileStatusLimited: Bool = true @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false enum CIInfoTab { case history case quote + case forwarded case delivery } @@ -68,9 +73,20 @@ struct ChatItemInfoView: View { if ci.quotedItem != nil { numTabs += 1 } + if chatItemInfo?.forwardedFromChatItem != nil { + numTabs += 1 + } return numTabs } + private var local: Bool { + switch ci.chatDir { + case .localSnd: true + case .localRcv: true + default: false + } + } + @ViewBuilder private func itemInfoView() -> some View { if numTabs > 1 { TabView(selection: $selection) { @@ -86,12 +102,22 @@ struct ChatItemInfoView: View { Label("History", systemImage: "clock") } .tag(CIInfoTab.history) + .modifier(ThemedBackground()) if let qi = ci.quotedItem { quoteTab(qi) .tabItem { Label("In reply to", systemImage: "arrowshape.turn.up.left") } .tag(CIInfoTab.quote) + .modifier(ThemedBackground()) + } + if let forwardedFromItem = chatItemInfo?.forwardedFromChatItem { + forwardedFromTab(forwardedFromItem) + .tabItem { + Label(local ? "Saved" : "Forwarded", systemImage: "arrowshape.turn.up.forward") + } + .tag(CIInfoTab.forwarded) + .modifier(ThemedBackground()) } } .onAppear { @@ -101,12 +127,13 @@ struct ChatItemInfoView: View { } } else { historyTab() + .modifier(ThemedBackground()) } } - @ViewBuilder private func details() -> some View { + private func details() -> some View { let meta = ci.meta - VStack(alignment: .leading, spacing: 16) { + return VStack(alignment: .leading, spacing: 16) { Text(title) .font(.largeTitle) .bold() @@ -137,11 +164,40 @@ struct ChatItemInfoView: View { if developerTools { infoRow("Database ID", "\(meta.itemId)") infoRow("Record updated at", localTimestamp(meta.updatedAt)) + let msv = infoRow("Message status", ci.meta.itemStatus.id) + Group { + if messageStatusLimited { + msv.lineLimit(1) + } else { + msv + } + } + .onTapGesture { + withAnimation { + messageStatusLimited.toggle() + } + } + + if let file = ci.file { + let fsv = infoRow("File status", file.fileStatus.id) + Group { + if fileStatusLimited { + fsv.lineLimit(1) + } else { + fsv + } + } + .onTapGesture { + withAnimation { + fileStatusLimited.toggle() + } + } + } } } } - @ViewBuilder private func historyTab() -> some View { + private func historyTab() -> some View { GeometryReader { g in let maxWidth = (g.size.width - 32) * 0.84 ScrollView { @@ -161,7 +217,7 @@ struct ChatItemInfoView: View { } else { Text("No history") - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .frame(maxWidth: .infinity) } } @@ -171,13 +227,14 @@ struct ChatItemInfoView: View { } } - @ViewBuilder private func itemVersionView(_ itemVersion: ChatItemVersion, _ maxWidth: CGFloat, current: Bool) -> some View { - VStack(alignment: .leading, spacing: 4) { - textBubble(itemVersion.msgContent.text, itemVersion.formattedText, nil) + private func itemVersionView(_ itemVersion: ChatItemVersion, _ maxWidth: CGFloat, current: Bool) -> some View { + let backgroundColor = chatItemFrameColor(ci, theme) + return VStack(alignment: .leading, spacing: 4) { + textBubble(itemVersion.msgContent.text, itemVersion.formattedText, nil, backgroundColor: UIColor(backgroundColor)) .padding(.horizontal, 12) .padding(.vertical, 6) - .background(chatItemFrameColor(ci, colorScheme)) - .cornerRadius(18) + .background(backgroundColor) + .modifier(ChatItemClipped()) .contextMenu { if itemVersion.msgContent.text != "" { Button { @@ -201,28 +258,33 @@ struct ChatItemInfoView: View { .frame(maxWidth: maxWidth, alignment: .leading) } - @ViewBuilder private func textBubble(_ text: String, _ formattedText: [FormattedText]?, _ sender: String? = nil) -> some View { + @ViewBuilder private func textBubble(_ text: String, _ formattedText: [FormattedText]?, _ sender: String? = nil, backgroundColor: UIColor) -> some View { if text != "" { - TextBubble(text: text, formattedText: formattedText, sender: sender) + TextBubble(text: text, formattedText: formattedText, sender: sender, mentions: ci.mentions, userMemberId: userMemberId, backgroundColor: backgroundColor) } else { Text("no text") .italic() - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } } private struct TextBubble: View { + @EnvironmentObject var theme: AppTheme var text: String var formattedText: [FormattedText]? var sender: String? = nil - @State private var showSecrets = false + var mentions: [String: CIMention]? + var userMemberId: String? + var backgroundColor: UIColor + @State private var showSecrets: Set = [] var body: some View { - toggleSecrets(formattedText, $showSecrets, messageText(text, formattedText, sender, showSecrets: showSecrets)) + let r = messageText(text, formattedText, sender: sender, mentions: mentions, userMemberId: userMemberId, showSecrets: showSecrets, backgroundColor: backgroundColor) + return msgTextResultView(r, Text(AttributedString(r.string)), showSecrets: $showSecrets) } } - @ViewBuilder private func quoteTab(_ qi: CIQuote) -> some View { + private func quoteTab(_ qi: CIQuote) -> some View { GeometryReader { g in let maxWidth = (g.size.width - 32) * 0.84 ScrollView { @@ -240,13 +302,14 @@ struct ChatItemInfoView: View { } } - @ViewBuilder private func quotedMsgView(_ qi: CIQuote, _ maxWidth: CGFloat) -> some View { - VStack(alignment: .leading, spacing: 4) { - textBubble(qi.text, qi.formattedText, qi.getSender(nil)) + private func quotedMsgView(_ qi: CIQuote, _ maxWidth: CGFloat) -> some View { + let backgroundColor = quotedMsgFrameColor(qi, theme) + return VStack(alignment: .leading, spacing: 4) { + textBubble(qi.text, qi.formattedText, qi.getSender(nil), backgroundColor: UIColor(backgroundColor)) .padding(.horizontal, 12) .padding(.vertical, 6) - .background(quotedMsgFrameColor(qi, colorScheme)) - .cornerRadius(18) + .background(quotedMsgFrameColor(qi, theme)) + .modifier(ChatItemClipped()) .contextMenu { if qi.text != "" { Button { @@ -269,13 +332,82 @@ struct ChatItemInfoView: View { .frame(maxWidth: maxWidth, alignment: .leading) } - func quotedMsgFrameColor(_ qi: CIQuote, _ colorScheme: ColorScheme) -> Color { + func quotedMsgFrameColor(_ qi: CIQuote, _ theme: AppTheme) -> Color { (qi.chatDir?.sent ?? false) - ? (colorScheme == .light ? sentColorLight : sentColorDark) - : Color(uiColor: .tertiarySystemGroupedBackground) + ? theme.appColors.sentMessage + : theme.appColors.receivedMessage } - @ViewBuilder private func deliveryTab(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> some View { + private func forwardedFromTab(_ forwardedFromItem: AChatItem) -> some View { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + details() + Divider().padding(.vertical) + Text(local ? "Saved from" : "Forwarded from") + .font(.title2) + .padding(.bottom, 4) + forwardedFromView(forwardedFromItem) + } + .padding() + } + .frame(maxHeight: .infinity, alignment: .top) + } + + private func forwardedFromView(_ forwardedFromItem: AChatItem) -> some View { + VStack(alignment: .leading, spacing: 8) { + Button { + Task { + await MainActor.run { + ItemsModel.shared.loadOpenChat(forwardedFromItem.chatInfo.id) { + dismiss() + } + } + } + } label: { + forwardedFromSender(forwardedFromItem) + } + + if !local { + Divider().padding(.top, 32) + Text("Recipient(s) can't see who this message is from.") + .font(.caption) + .foregroundColor(theme.colors.secondary) + } + } + } + + private func forwardedFromSender(_ forwardedFromItem: AChatItem) -> some View { + HStack { + ChatInfoImage(chat: Chat(chatInfo: forwardedFromItem.chatInfo), size: 48) + .padding(.trailing, 6) + + if forwardedFromItem.chatItem.chatDir.sent { + VStack(alignment: .leading) { + Text("you") + .italic() + .foregroundColor(theme.colors.onBackground) + Text(forwardedFromItem.chatInfo.chatViewName) + .foregroundColor(theme.colors.secondary) + .lineLimit(1) + } + } else if case let .groupRcv(groupMember) = forwardedFromItem.chatItem.chatDir { + VStack(alignment: .leading) { + Text(groupMember.chatViewName) + .foregroundColor(theme.colors.onBackground) + .lineLimit(1) + Text(forwardedFromItem.chatInfo.chatViewName) + .foregroundColor(theme.colors.secondary) + .lineLimit(1) + } + } else { + Text(forwardedFromItem.chatInfo.chatViewName) + .foregroundColor(theme.colors.onBackground) + .lineLimit(1) + } + } + } + + private func deliveryTab(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> some View { ScrollView { VStack(alignment: .leading, spacing: 16) { details() @@ -290,57 +422,44 @@ struct ChatItemInfoView: View { .frame(maxHeight: .infinity, alignment: .top) } - @ViewBuilder private func memberDeliveryStatusesView(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> some View { - VStack(alignment: .leading, spacing: 12) { + private func memberDeliveryStatusesView(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> some View { + LazyVStack(alignment: .leading, spacing: 12) { let mss = membersStatuses(memberDeliveryStatuses) if !mss.isEmpty { ForEach(mss, id: \.0.groupMemberId) { memberStatus in - memberDeliveryStatusView(memberStatus.0, memberStatus.1) + memberDeliveryStatusView(memberStatus.0, memberStatus.1, memberStatus.2) } } else { Text("No delivery information") - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } } } - private func membersStatuses(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> [(GroupMember, CIStatus)] { + private func membersStatuses(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> [(GroupMember, GroupSndStatus, Bool?)] { memberDeliveryStatuses.compactMap({ mds in if let mem = chatModel.getGroupMember(mds.groupMemberId) { - return (mem.wrapped, mds.memberDeliveryStatus) + return (mem.wrapped, mds.memberDeliveryStatus, mds.sentViaProxy) } else { return nil } }) } - private func memberDeliveryStatusView(_ member: GroupMember, _ status: CIStatus) -> some View { + private func memberDeliveryStatusView(_ member: GroupMember, _ status: GroupSndStatus, _ sentViaProxy: Bool?) -> some View { HStack{ - ProfileImage(imageStr: member.image) - .frame(width: 30, height: 30) + MemberProfileImage(member, size: 30) .padding(.trailing, 2) Text(member.chatViewName) .lineLimit(1) Spacer() + if sentViaProxy == true { + Image(systemName: "arrow.forward") + .foregroundColor(theme.colors.secondary).opacity(0.67) + } let v = Group { - if let (icon, statusColor) = status.statusIcon(Color.secondary) { - switch status { - case .sndRcvd: - ZStack(alignment: .trailing) { - Image(systemName: icon) - .foregroundColor(statusColor.opacity(0.67)) - .padding(.trailing, 6) - Image(systemName: icon) - .foregroundColor(statusColor.opacity(0.67)) - } - default: - Image(systemName: icon) - .foregroundColor(statusColor) - } - } else { - Image(systemName: "ellipsis") - .foregroundColor(Color.secondary) - } + let (image, statusColor) = status.statusIcon(theme.colors.secondary, theme.colors.primary) + image.foregroundColor(statusColor) } if let (title, text) = status.statusInfo { @@ -382,8 +501,12 @@ struct ChatItemInfoView: View { if developerTools { shareText += [ String.localizedStringWithFormat(NSLocalizedString("Database ID: %d", comment: "copied message info"), meta.itemId), - String.localizedStringWithFormat(NSLocalizedString("Record updated at: %@", comment: "copied message info"), localTimestamp(meta.updatedAt)) + String.localizedStringWithFormat(NSLocalizedString("Record updated at: %@", comment: "copied message info"), localTimestamp(meta.updatedAt)), + String.localizedStringWithFormat(NSLocalizedString("Message status: %@", comment: "copied message info"), meta.itemStatus.id) ] + if let file = ci.file { + shareText += [String.localizedStringWithFormat(NSLocalizedString("File status: %@", comment: "copied message info"), file.fileStatus.id)] + } } if let qi = ci.quotedItem { shareText += ["", NSLocalizedString("## In reply to", comment: "copied message info")] @@ -433,6 +556,6 @@ func localTimestamp(_ date: Date) -> String { struct ChatItemInfoView_Previews: PreviewProvider { static var previews: some View { - ChatItemInfoView(ci: ChatItem.getSample(1, .directSnd, .now, "hello"), chatItemInfo: Binding.constant(nil)) + ChatItemInfoView(ci: ChatItem.getSample(1, .directSnd, .now, "hello"), userMemberId: Chat.sampleData.chatInfo.groupInfo?.membership.memberId, chatItemInfo: Binding.constant(nil)) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItemView.swift b/apps/ios/Shared/Views/Chat/ChatItemView.swift index da9dc523e1..f5558bcd93 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemView.swift @@ -9,50 +9,71 @@ import SwiftUI import SimpleXChat +extension EnvironmentValues { + struct ShowTimestamp: EnvironmentKey { + static let defaultValue: Bool = true + } + + struct Revealed: EnvironmentKey { + static let defaultValue: Bool = true + } + + struct ContainerBackground: EnvironmentKey { + static let defaultValue: UIColor = .clear + } + + var showTimestamp: Bool { + get { self[ShowTimestamp.self] } + set { self[ShowTimestamp.self] = newValue } + } + + var revealed: Bool { + get { self[Revealed.self] } + set { self[Revealed.self] = newValue } + } + + var containerBackground: UIColor { + get { self[ContainerBackground.self] } + set { self[ContainerBackground.self] = newValue } + } +} + struct ChatItemView: View { @ObservedObject var chat: Chat + @EnvironmentObject var theme: AppTheme + @Environment(\.showTimestamp) var showTimestamp: Bool + @Environment(\.revealed) var revealed: Bool var chatItem: ChatItem + var scrollToItemId: (ChatItem.ID) -> Void var maxWidth: CGFloat = .infinity - @State var scrollProxy: ScrollViewProxy? = nil - @Binding var revealed: Bool @Binding var allowMenu: Bool - @Binding var audioPlayer: AudioPlayer? - @Binding var playbackState: VoiceMessagePlaybackState - @Binding var playbackTime: TimeInterval? + init( chat: Chat, chatItem: ChatItem, + scrollToItemId: @escaping (ChatItem.ID) -> Void, showMember: Bool = false, maxWidth: CGFloat = .infinity, - scrollProxy: ScrollViewProxy? = nil, - revealed: Binding, - allowMenu: Binding = .constant(false), - audioPlayer: Binding = .constant(nil), - playbackState: Binding = .constant(.noPlayback), - playbackTime: Binding = .constant(nil) + allowMenu: Binding = .constant(false) ) { self.chat = chat self.chatItem = chatItem + self.scrollToItemId = scrollToItemId self.maxWidth = maxWidth - _scrollProxy = .init(initialValue: scrollProxy) - _revealed = revealed _allowMenu = allowMenu - _audioPlayer = audioPlayer - _playbackState = playbackState - _playbackTime = playbackTime } var body: some View { let ci = chatItem if chatItem.meta.itemDeleted != nil && (!revealed || chatItem.isDeletedContent) { - MarkedDeletedItemView(chat: chat, chatItem: chatItem, revealed: $revealed) - } else if ci.quotedItem == nil && ci.meta.itemDeleted == nil && !ci.meta.isLive { + MarkedDeletedItemView(chat: chat, chatItem: chatItem) + } else if ci.quotedItem == nil && ci.meta.itemForwarded == nil && ci.meta.itemDeleted == nil && !ci.meta.isLive { if let mc = ci.content.msgContent, mc.isText && isShortEmoji(ci.content.text) { EmojiItemView(chat: chat, chatItem: ci) } else if ci.content.text.isEmpty, case let .voice(_, duration) = ci.content.msgContent { - CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, audioPlayer: $audioPlayer, playbackState: $playbackState, playbackTime: $playbackTime, allowMenu: $allowMenu) + CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, allowMenu: $allowMenu) } else if ci.content.msgContent == nil { - ChatItemContentView(chat: chat, chatItem: chatItem, revealed: $revealed, msgContentView: { Text(ci.text) }) // msgContent is unreachable branch in this case + ChatItemContentView(chat: chat, chatItem: chatItem, msgContentView: { Text(ci.text) }) // msgContent is unreachable branch in this case } else { framedItemView() } @@ -62,15 +83,41 @@ struct ChatItemView: View { } private func framedItemView() -> some View { - FramedItemView(chat: chat, chatItem: chatItem, revealed: $revealed, maxWidth: maxWidth, scrollProxy: scrollProxy, allowMenu: $allowMenu, audioPlayer: $audioPlayer, playbackState: $playbackState, playbackTime: $playbackTime) + let preview = chatItem.content.msgContent + .flatMap { + switch $0 { + case let .image(_, image): image + case let .video(_, image, _): image + default: nil + } + } + .flatMap { imageFromBase64($0) } + let adjustedMaxWidth = { + if let preview, preview.size.width <= preview.size.height { + maxWidth * 0.75 + } else { + maxWidth + } + }() + return FramedItemView( + chat: chat, + chatItem: chatItem, + scrollToItemId: scrollToItemId, + preview: preview, + maxWidth: maxWidth, + imgWidth: adjustedMaxWidth, + videoWidth: adjustedMaxWidth, + allowMenu: $allowMenu + ) } } struct ChatItemContentView: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme + @Environment(\.revealed) var revealed: Bool @ObservedObject var chat: Chat var chatItem: ChatItem - @Binding var revealed: Bool var msgContentView: () -> Content @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false @@ -97,14 +144,14 @@ struct ChatItemContentView: View { case .sndGroupEvent: eventItemView() case .rcvConnEvent: eventItemView() case .sndConnEvent: eventItemView() - case let .rcvChatFeature(feature, enabled, _): chatFeatureView(feature, enabled.iconColor) - case let .sndChatFeature(feature, enabled, _): chatFeatureView(feature, enabled.iconColor) + case let .rcvChatFeature(feature, enabled, _): chatFeatureView(feature, enabled.iconColor(theme.colors.secondary)) + case let .sndChatFeature(feature, enabled, _): chatFeatureView(feature, enabled.iconColor(theme.colors.secondary)) case let .rcvChatPreference(feature, allowed, param): CIFeaturePreferenceView(chat: chat, chatItem: chatItem, feature: feature, allowed: allowed, param: param) case let .sndChatPreference(feature, _, _): - CIChatFeatureView(chatItem: chatItem, revealed: $revealed, feature: feature, icon: feature.icon, iconColor: .secondary) - case let .rcvGroupFeature(feature, preference, _): chatFeatureView(feature, preference.enable.iconColor) - case let .sndGroupFeature(feature, preference, _): chatFeatureView(feature, preference.enable.iconColor) + CIChatFeatureView(chat: chat, chatItem: chatItem, feature: feature, icon: feature.icon, iconColor: theme.colors.secondary) + case let .rcvGroupFeature(feature, preference, _, role): chatFeatureView(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor(theme.colors.secondary)) + case let .sndGroupFeature(feature, preference, _, role): chatFeatureView(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor(theme.colors.secondary)) case let .rcvChatFeatureRejected(feature): chatFeatureView(feature, .red) case let .rcvGroupFeatureRejected(feature): chatFeatureView(feature, .red) case .sndModerated: deletedItemView() @@ -127,29 +174,29 @@ struct ChatItemContentView: View { } private func groupInvitationItemView(_ groupInvitation: CIGroupInvitation, _ memberRole: GroupMemberRole) -> some View { - CIGroupInvitationView(chatItem: chatItem, groupInvitation: groupInvitation, memberRole: memberRole, chatIncognito: chat.chatInfo.incognito) + CIGroupInvitationView(chat: chat, chatItem: chatItem, groupInvitation: groupInvitation, memberRole: memberRole, chatIncognito: chat.chatInfo.incognito) } private func eventItemView() -> some View { - return CIEventView(eventText: eventItemViewText()) + CIEventView(eventText: eventItemViewText(theme.colors.secondary)) } - private func eventItemViewText() -> Text { + private func eventItemViewText(_ secondaryColor: Color) -> Text { if !revealed, let t = mergedGroupEventText { - return chatEventText(t + Text(" ") + chatItem.timestampText) + return chatEventText(t + textSpace + chatItem.timestampText, secondaryColor) } else if let member = chatItem.memberDisplayName { return Text(member + " ") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(secondaryColor) .fontWeight(.light) - + chatEventText(chatItem) + + chatEventText(chatItem, secondaryColor) } else { - return chatEventText(chatItem) + return chatEventText(chatItem, secondaryColor) } } private func chatFeatureView(_ feature: Feature, _ iconColor: Color) -> some View { - CIChatFeatureView(chatItem: chatItem, revealed: $revealed, feature: feature, iconColor: iconColor) + CIChatFeatureView(chat: chat, chatItem: chatItem, feature: feature, iconColor: iconColor) } private var mergedGroupEventText: Text? { @@ -169,7 +216,7 @@ struct ChatItemContentView: View { } else if ns.count == 0 { Text("\(count) group events") } else if count > ns.count { - Text(members) + Text(" ") + Text("and \(count - ns.count) other events") + Text(members) + textSpace + Text("and \(count - ns.count) other events") } else { Text(members) } @@ -179,7 +226,7 @@ struct ChatItemContentView: View { info.pqEnabled ? Text("Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery.") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .fontWeight(.light) : e2eeInfoNoPQText() } @@ -187,39 +234,40 @@ struct ChatItemContentView: View { private func e2eeInfoNoPQText() -> Text { Text("Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery.") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .fontWeight(.light) } } -func chatEventText(_ text: Text) -> Text { +func chatEventText(_ text: Text, _ secondaryColor: Color) -> Text { text .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(secondaryColor) .fontWeight(.light) } -func chatEventText(_ eventText: LocalizedStringKey, _ ts: Text) -> Text { - chatEventText(Text(eventText) + Text(" ") + ts) +func chatEventText(_ eventText: LocalizedStringKey, _ ts: Text, _ secondaryColor: Color) -> Text { + chatEventText(Text(eventText) + textSpace + ts, secondaryColor) } -func chatEventText(_ ci: ChatItem) -> Text { - chatEventText("\(ci.content.text)", ci.timestampText) +func chatEventText(_ ci: ChatItem, _ secondaryColor: Color) -> Text { + chatEventText("\(ci.content.text)", ci.timestampText, secondaryColor) } struct ChatItemView_Previews: PreviewProvider { static var previews: some View { Group{ - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello"), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too"), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂"), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂"), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂🙂"), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getDeletedContentSample(), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemDeleted: .deleted(deletedTs: .now)), revealed: Binding.constant(false)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂", .sndSent(sndProgress: .complete), itemLive: true), revealed: Binding.constant(true)) - ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemLive: true), revealed: Binding.constant(true)) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello"), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "hello there too"), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂"), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂"), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directRcv, .now, "🙂🙂🙂🙂🙂🙂"), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getDeletedContentSample(), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemDeleted: .deleted(deletedTs: .now)), scrollToItemId: { _ in }) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "🙂", .sndSent(sndProgress: .complete), itemLive: true), scrollToItemId: { _ in }).environment(\.revealed, true) + ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getSample(1, .directSnd, .now, "hello", .sndSent(sndProgress: .complete), itemLive: true), scrollToItemId: { _ in }).environment(\.revealed, true) } + .environment(\.revealed, false) .previewLayout(.fixed(width: 360, height: 70)) .environmentObject(Chat.sampleData) } @@ -238,7 +286,7 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider { quotedItem: nil, file: nil ), - revealed: Binding.constant(true) + scrollToItemId: { _ in } ) ChatItemView( chat: Chat.sampleData, @@ -249,7 +297,7 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider { quotedItem: nil, file: nil ), - revealed: Binding.constant(true) + scrollToItemId: { _ in } ) ChatItemView( chat: Chat.sampleData, @@ -260,7 +308,7 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider { quotedItem: nil, file: nil ), - revealed: Binding.constant(true) + scrollToItemId: { _ in } ) ChatItemView( chat: Chat.sampleData, @@ -271,7 +319,7 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider { quotedItem: nil, file: nil ), - revealed: Binding.constant(true) + scrollToItemId: { _ in } ) ChatItemView( chat: Chat.sampleData, @@ -282,9 +330,10 @@ struct ChatItemView_NonMsgContentDeleted_Previews: PreviewProvider { quotedItem: nil, file: nil ), - revealed: Binding.constant(true) + scrollToItemId: { _ in } ) } + .environment(\.revealed, true) .previewLayout(.fixed(width: 360, height: 70)) .environmentObject(Chat.sampleData) } diff --git a/apps/ios/Shared/Views/Chat/ChatItemsLoader.swift b/apps/ios/Shared/Views/Chat/ChatItemsLoader.swift new file mode 100644 index 0000000000..07034cf8ec --- /dev/null +++ b/apps/ios/Shared/Views/Chat/ChatItemsLoader.swift @@ -0,0 +1,511 @@ +// +// ChatItemsLoader.swift +// SimpleX (iOS) +// +// Created by Stanislav Dmitrenko on 17.12.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SimpleXChat +import SwiftUI + +let TRIM_KEEP_COUNT = 200 + +func apiLoadMessages( + _ chatId: ChatId, + _ pagination: ChatPagination, + _ chatState: ActiveChatState, + _ search: String = "", + _ openAroundItemId: ChatItem.ID? = nil, + _ visibleItemIndexesNonReversed: @MainActor () -> ClosedRange = { 0 ... 0 } +) async { + let chat: Chat + let navInfo: NavigationInfo + do { + (chat, navInfo) = try await apiGetChat(chatId: chatId, pagination: pagination, search: search) + } catch let error { + logger.error("apiLoadMessages error: \(responseError(error))") + return + } + + let chatModel = ChatModel.shared + + // For .initial allow the chatItems to be empty as well as chatModel.chatId to not match this chat because these values become set after .initial finishes + let paginationIsInitial = switch pagination { case .initial: true; default: false } + let paginationIsLast = switch pagination { case .last: true; default: false } + // When openAroundItemId is provided, chatId can be different too + if ((chatModel.chatId != chat.id || chat.chatItems.isEmpty) && !paginationIsInitial && !paginationIsLast && openAroundItemId == nil) || Task.isCancelled { + return + } + + let unreadAfterItemId = chatState.unreadAfterItemId + + let oldItems = Array(ItemsModel.shared.reversedChatItems.reversed()) + var newItems: [ChatItem] = [] + switch pagination { + case .initial: + let newSplits: [Int64] = if !chat.chatItems.isEmpty && navInfo.afterTotal > 0 { [chat.chatItems.last!.id] } else { [] } + if chatModel.getChat(chat.id) == nil { + chatModel.addChat(chat) + } + await MainActor.run { + chatModel.chatItemStatuses.removeAll() + ItemsModel.shared.reversedChatItems = chat.chatItems.reversed() + chatModel.updateChatInfo(chat.chatInfo) + chatState.splits = newSplits + if !chat.chatItems.isEmpty { + chatState.unreadAfterItemId = chat.chatItems.last!.id + } + chatState.totalAfter = navInfo.afterTotal + chatState.unreadTotal = chat.chatStats.unreadCount + chatState.unreadAfter = navInfo.afterUnread + chatState.unreadAfterNewestLoaded = navInfo.afterUnread + + PreloadState.shared.clear() + } + case let .before(paginationChatItemId, _): + newItems.append(contentsOf: oldItems) + let indexInCurrentItems = oldItems.firstIndex(where: { $0.id == paginationChatItemId }) + guard let indexInCurrentItems else { return } + let (newIds, _) = mapItemsToIds(chat.chatItems) + let wasSize = newItems.count + let visibleItemIndexes = await MainActor.run { visibleItemIndexesNonReversed() } + let modifiedSplits = removeDuplicatesAndModifySplitsOnBeforePagination( + unreadAfterItemId, &newItems, newIds, chatState.splits, visibleItemIndexes + ) + let insertAt = max((indexInCurrentItems - (wasSize - newItems.count) + modifiedSplits.trimmedIds.count), 0) + newItems.insert(contentsOf: chat.chatItems, at: insertAt) + let newReversed: [ChatItem] = newItems.reversed() + await MainActor.run { + ItemsModel.shared.reversedChatItems = newReversed + chatState.splits = modifiedSplits.newSplits + chatState.moveUnreadAfterItem(modifiedSplits.oldUnreadSplitIndex, modifiedSplits.newUnreadSplitIndex, oldItems) + } + case let .after(paginationChatItemId, _): + newItems.append(contentsOf: oldItems) + let indexInCurrentItems = oldItems.firstIndex(where: { $0.id == paginationChatItemId }) + guard let indexInCurrentItems else { return } + + let mappedItems = mapItemsToIds(chat.chatItems) + let newIds = mappedItems.0 + let (newSplits, unreadInLoaded) = removeDuplicatesAndModifySplitsOnAfterPagination( + mappedItems.1, paginationChatItemId, &newItems, newIds, chat, chatState.splits + ) + let indexToAdd = min(indexInCurrentItems + 1, newItems.count) + let indexToAddIsLast = indexToAdd == newItems.count + newItems.insert(contentsOf: chat.chatItems, at: indexToAdd) + let new: [ChatItem] = newItems + let newReversed: [ChatItem] = newItems.reversed() + await MainActor.run { + ItemsModel.shared.reversedChatItems = newReversed + chatState.splits = newSplits + chatState.moveUnreadAfterItem(chatState.splits.first ?? new.last!.id, new) + // loading clear bottom area, updating number of unread items after the newest loaded item + if indexToAddIsLast { + chatState.unreadAfterNewestLoaded -= unreadInLoaded + } + } + case .around: + var newSplits: [Int64] + if openAroundItemId == nil { + newItems.append(contentsOf: oldItems) + newSplits = await removeDuplicatesAndUpperSplits(&newItems, chat, chatState.splits, visibleItemIndexesNonReversed) + } else { + newSplits = [] + } + let (itemIndex, splitIndex) = indexToInsertAround(chat.chatInfo.chatType, chat.chatItems.last, to: newItems, Set(newSplits)) + //indexToInsertAroundTest() + newItems.insert(contentsOf: chat.chatItems, at: itemIndex) + newSplits.insert(chat.chatItems.last!.id, at: splitIndex) + let newReversed: [ChatItem] = newItems.reversed() + let orderedSplits = newSplits + await MainActor.run { + ItemsModel.shared.reversedChatItems = newReversed + chatState.splits = orderedSplits + chatState.unreadAfterItemId = chat.chatItems.last!.id + chatState.totalAfter = navInfo.afterTotal + chatState.unreadTotal = chat.chatStats.unreadCount + chatState.unreadAfter = navInfo.afterUnread + + if let openAroundItemId { + chatState.unreadAfterNewestLoaded = navInfo.afterUnread + ChatModel.shared.openAroundItemId = openAroundItemId + ChatModel.shared.chatId = chatId + } else { + // no need to set it, count will be wrong + // chatState.unreadAfterNewestLoaded = navInfo.afterUnread + } + PreloadState.shared.clear() + } + case .last: + newItems.append(contentsOf: oldItems) + let newSplits = await removeDuplicatesAndUnusedSplits(&newItems, chat, chatState.splits) + newItems.append(contentsOf: chat.chatItems) + let items = newItems + await MainActor.run { + ItemsModel.shared.reversedChatItems = items.reversed() + chatState.splits = newSplits + chatModel.updateChatInfo(chat.chatInfo) + chatState.unreadAfterNewestLoaded = 0 + } + } +} + + +private class ModifiedSplits { + let oldUnreadSplitIndex: Int + let newUnreadSplitIndex: Int + let trimmedIds: Set + let newSplits: [Int64] + + init(oldUnreadSplitIndex: Int, newUnreadSplitIndex: Int, trimmedIds: Set, newSplits: [Int64]) { + self.oldUnreadSplitIndex = oldUnreadSplitIndex + self.newUnreadSplitIndex = newUnreadSplitIndex + self.trimmedIds = trimmedIds + self.newSplits = newSplits + } +} + +private func removeDuplicatesAndModifySplitsOnBeforePagination( + _ unreadAfterItemId: Int64, + _ newItems: inout [ChatItem], + _ newIds: Set, + _ splits: [Int64], + _ visibleItemIndexes: ClosedRange +) -> ModifiedSplits { + var oldUnreadSplitIndex: Int = -1 + var newUnreadSplitIndex: Int = -1 + var lastSplitIndexTrimmed: Int? = nil + var allowedTrimming = true + var index = 0 + /** keep the newest [TRIM_KEEP_COUNT] items (bottom area) and oldest [TRIM_KEEP_COUNT] items, trim others */ + let trimLowerBound = visibleItemIndexes.upperBound + TRIM_KEEP_COUNT + let trimUpperBound = newItems.count - TRIM_KEEP_COUNT + let trimRange = trimUpperBound >= trimLowerBound ? trimLowerBound ... trimUpperBound : -1 ... -1 + var trimmedIds = Set() + let prevTrimLowerBound = visibleItemIndexes.upperBound + TRIM_KEEP_COUNT + 1 + let prevTrimUpperBound = newItems.count - TRIM_KEEP_COUNT + let prevItemTrimRange = prevTrimUpperBound >= prevTrimLowerBound ? prevTrimLowerBound ... prevTrimUpperBound : -1 ... -1 + var newSplits = splits + + newItems.removeAll(where: { + let invisibleItemToTrim = trimRange.contains(index) && allowedTrimming + let prevItemWasTrimmed = prevItemTrimRange.contains(index) && allowedTrimming + // may disable it after clearing the whole split range + if !splits.isEmpty && $0.id == splits.first { + // trim only in one split range + allowedTrimming = false + } + let indexInSplits = splits.firstIndex(of: $0.id) + if let indexInSplits { + lastSplitIndexTrimmed = indexInSplits + } + if invisibleItemToTrim { + if prevItemWasTrimmed { + trimmedIds.insert($0.id) + } else { + newUnreadSplitIndex = index + // prev item is not supposed to be trimmed, so exclude current one from trimming and set a split here instead. + // this allows to define splitRange of the oldest items and to start loading trimmed items when user scrolls in the opposite direction + if let lastSplitIndexTrimmed { + var new = newSplits + new[lastSplitIndexTrimmed] = $0.id + newSplits = new + } else { + newSplits = [$0.id] + newSplits + } + } + } + if unreadAfterItemId == $0.id { + oldUnreadSplitIndex = index + } + index += 1 + return (invisibleItemToTrim && prevItemWasTrimmed) || newIds.contains($0.id) + }) + // will remove any splits that now becomes obsolete because items were merged + newSplits = newSplits.filter { split in !newIds.contains(split) && !trimmedIds.contains(split) } + return ModifiedSplits(oldUnreadSplitIndex: oldUnreadSplitIndex, newUnreadSplitIndex: newUnreadSplitIndex, trimmedIds: trimmedIds, newSplits: newSplits) +} + +private func removeDuplicatesAndModifySplitsOnAfterPagination( + _ unreadInLoaded: Int, + _ paginationChatItemId: Int64, + _ newItems: inout [ChatItem], + _ newIds: Set, + _ chat: Chat, + _ splits: [Int64] +) -> ([Int64], Int) { + var unreadInLoaded = unreadInLoaded + var firstItemIdBelowAllSplits: Int64? = nil + var splitsToRemove: Set = [] + let indexInSplitRanges = splits.firstIndex(of: paginationChatItemId) + // Currently, it should always load from split range + let loadingFromSplitRange = indexInSplitRanges != nil + let topSplits: [Int64] + var splitsToMerge: [Int64] + if let indexInSplitRanges, loadingFromSplitRange && indexInSplitRanges + 1 <= splits.count { + splitsToMerge = Array(splits[indexInSplitRanges + 1 ..< splits.count]) + topSplits = Array(splits[0 ..< indexInSplitRanges + 1]) + } else { + splitsToMerge = [] + topSplits = [] + } + newItems.removeAll(where: { new in + let duplicate = newIds.contains(new.id) + if loadingFromSplitRange && duplicate { + if splitsToMerge.contains(new.id) { + splitsToMerge.removeAll(where: { $0 == new.id }) + splitsToRemove.insert(new.id) + } else if firstItemIdBelowAllSplits == nil && splitsToMerge.isEmpty { + // we passed all splits and found duplicated item below all of them, which means no splits anymore below the loaded items + firstItemIdBelowAllSplits = new.id + } + } + if duplicate && new.isRcvNew { + unreadInLoaded -= 1 + } + return duplicate + }) + var newSplits: [Int64] = [] + if firstItemIdBelowAllSplits != nil { + // no splits below anymore, all were merged with bottom items + newSplits = topSplits + } else { + if !splitsToRemove.isEmpty { + var new = splits + new.removeAll(where: { splitsToRemove.contains($0) }) + newSplits = new + } + let enlargedSplit = splits.firstIndex(of: paginationChatItemId) + if let enlargedSplit { + // move the split to the end of loaded items + var new = splits + new[enlargedSplit] = chat.chatItems.last!.id + newSplits = new + } + } + return (newSplits, unreadInLoaded) +} + +private func removeDuplicatesAndUpperSplits( + _ newItems: inout [ChatItem], + _ chat: Chat, + _ splits: [Int64], + _ visibleItemIndexesNonReversed: @MainActor () -> ClosedRange +) async -> [Int64] { + if splits.isEmpty { + removeDuplicates(&newItems, chat) + return splits + } + + var newSplits = splits + let visibleItemIndexes = await MainActor.run { visibleItemIndexesNonReversed() } + let (newIds, _) = mapItemsToIds(chat.chatItems) + var idsToTrim: [BoxedValue>] = [] + idsToTrim.append(BoxedValue(Set())) + var index = 0 + newItems.removeAll(where: { + let duplicate = newIds.contains($0.id) + if (!duplicate && visibleItemIndexes.lowerBound > index) { + idsToTrim.last?.boxedValue.insert($0.id) + } + if visibleItemIndexes.lowerBound > index, let firstIndex = newSplits.firstIndex(of: $0.id) { + newSplits.remove(at: firstIndex) + // closing previous range. All items in idsToTrim that ends with empty set should be deleted. + // Otherwise, the last set should be excluded from trimming because it is in currently visible split range + idsToTrim.append(BoxedValue(Set())) + } + + index += 1 + return duplicate + }) + if !idsToTrim.last!.boxedValue.isEmpty { + // it has some elements to trim from currently visible range which means the items shouldn't be trimmed + // Otherwise, the last set would be empty + idsToTrim.removeLast() + } + let allItemsToDelete = idsToTrim.compactMap { set in set.boxedValue }.joined() + if !allItemsToDelete.isEmpty { + newItems.removeAll(where: { allItemsToDelete.contains($0.id) }) + } + return newSplits +} + +private func removeDuplicatesAndUnusedSplits( + _ newItems: inout [ChatItem], + _ chat: Chat, + _ splits: [Int64] +) async -> [Int64] { + if splits.isEmpty { + removeDuplicates(&newItems, chat) + return splits + } + + var newSplits = splits + let (newIds, _) = mapItemsToIds(chat.chatItems) + newItems.removeAll(where: { + let duplicate = newIds.contains($0.id) + if duplicate, let firstIndex = newSplits.firstIndex(of: $0.id) { + newSplits.remove(at: firstIndex) + } + return duplicate + }) + return newSplits +} + +// ids, number of unread items +private func mapItemsToIds(_ items: [ChatItem]) -> (Set, Int) { + var unreadInLoaded = 0 + var ids: Set = Set() + var i = 0 + while i < items.count { + let item = items[i] + ids.insert(item.id) + if item.isRcvNew { + unreadInLoaded += 1 + } + i += 1 + } + return (ids, unreadInLoaded) +} + +private func removeDuplicates(_ newItems: inout [ChatItem], _ chat: Chat) { + let (newIds, _) = mapItemsToIds(chat.chatItems) + newItems.removeAll { newIds.contains($0.id) } +} + +private typealias SameTimeItem = (index: Int, item: ChatItem) + +// return (item index, split index) +private func indexToInsertAround(_ chatType: ChatType, _ lastNew: ChatItem?, to: [ChatItem], _ splits: Set) -> (Int, Int) { + guard to.count > 0, let lastNew = lastNew else { return (0, 0) } + // group sorting: item_ts, item_id + // everything else: created_at, item_id + let compareByTimeTs = chatType == .group + // in case several items have the same time as another item in the `to` array + var sameTime: [SameTimeItem] = [] + + // trying to find new split index for item looks difficult but allows to not use one more loop. + // The idea is to memorize how many splits were till any index (map number of splits until index) + // and use resulting itemIndex to decide new split index position. + // Because of the possibility to have many items with the same timestamp, it's possible to see `itemIndex < || == || > i`. + var splitsTillIndex: [Int] = [] + var splitsPerPrevIndex = 0 + + for i in 0 ..< to.count { + let item = to[i] + + splitsPerPrevIndex = splits.contains(item.id) ? splitsPerPrevIndex + 1 : splitsPerPrevIndex + splitsTillIndex.append(splitsPerPrevIndex) + + let itemIsNewer = (compareByTimeTs ? item.meta.itemTs > lastNew.meta.itemTs : item.meta.createdAt > lastNew.meta.createdAt) + if itemIsNewer || i + 1 == to.count { + if (compareByTimeTs ? lastNew.meta.itemTs == item.meta.itemTs : lastNew.meta.createdAt == item.meta.createdAt) { + sameTime.append((i, item)) + } + // time to stop the loop. Item is newer or it's the last item in `to` array, taking previous items and checking position inside them + let itemIndex: Int + if sameTime.count > 1, let first = sameTime.sorted(by: { prev, next in prev.item.meta.itemId < next.item.id }).first(where: { same in same.item.id > lastNew.id }) { + itemIndex = first.index + } else if sameTime.count == 1 { + itemIndex = sameTime[0].item.id > lastNew.id ? sameTime[0].index : sameTime[0].index + 1 + } else { + itemIndex = itemIsNewer ? i : i + 1 + } + let splitIndex = splitsTillIndex[min(itemIndex, splitsTillIndex.count - 1)] + let prevItemSplitIndex = itemIndex == 0 ? 0 : splitsTillIndex[min(itemIndex - 1, splitsTillIndex.count - 1)] + return (itemIndex, splitIndex == prevItemSplitIndex ? splitIndex : prevItemSplitIndex) + } + + if (compareByTimeTs ? lastNew.meta.itemTs == item.meta.itemTs : lastNew.meta.createdAt == item.meta.createdAt) { + sameTime.append(SameTimeItem(index: i, item: item)) + } else { + sameTime = [] + } + } + // shouldn't be here + return (to.count, splits.count) +} + +private func indexToInsertAroundTest() { + func assert(_ one: (Int, Int), _ two: (Int, Int)) { + if one != two { + logger.debug("\(String(describing: one)) != \(String(describing: two))") + fatalError() + } + } + + let itemsToInsert = [ChatItem.getSample(3, .groupSnd, Date.init(timeIntervalSince1970: 3), "")] + let items1 = [ + ChatItem.getSample(0, .groupSnd, Date.init(timeIntervalSince1970: 0), ""), + ChatItem.getSample(1, .groupSnd, Date.init(timeIntervalSince1970: 1), ""), + ChatItem.getSample(2, .groupSnd, Date.init(timeIntervalSince1970: 2), "") + ] + assert(indexToInsertAround(.group, itemsToInsert.last, to: items1, Set([1])), (3, 1)) + + let items2 = [ + ChatItem.getSample(0, .groupSnd, Date.init(timeIntervalSince1970: 0), ""), + ChatItem.getSample(1, .groupSnd, Date.init(timeIntervalSince1970: 1), ""), + ChatItem.getSample(2, .groupSnd, Date.init(timeIntervalSince1970: 3), "") + ] + assert(indexToInsertAround(.group, itemsToInsert.last, to: items2, Set([2])), (3, 1)) + + let items3 = [ + ChatItem.getSample(0, .groupSnd, Date.init(timeIntervalSince1970: 0), ""), + ChatItem.getSample(1, .groupSnd, Date.init(timeIntervalSince1970: 3), ""), + ChatItem.getSample(2, .groupSnd, Date.init(timeIntervalSince1970: 3), "") + ] + assert(indexToInsertAround(.group, itemsToInsert.last, to: items3, Set([1])), (3, 1)) + + let items4 = [ + ChatItem.getSample(0, .groupSnd, Date.init(timeIntervalSince1970: 0), ""), + ChatItem.getSample(4, .groupSnd, Date.init(timeIntervalSince1970: 3), ""), + ChatItem.getSample(5, .groupSnd, Date.init(timeIntervalSince1970: 3), "") + ] + assert(indexToInsertAround(.group, itemsToInsert.last, to: items4, Set([4])), (1, 0)) + + let items5 = [ + ChatItem.getSample(0, .groupSnd, Date.init(timeIntervalSince1970: 0), ""), + ChatItem.getSample(2, .groupSnd, Date.init(timeIntervalSince1970: 3), ""), + ChatItem.getSample(4, .groupSnd, Date.init(timeIntervalSince1970: 3), "") + ] + assert(indexToInsertAround(.group, itemsToInsert.last, to: items5, Set([2])), (2, 1)) + + let items6 = [ + ChatItem.getSample(4, .groupSnd, Date.init(timeIntervalSince1970: 4), ""), + ChatItem.getSample(5, .groupSnd, Date.init(timeIntervalSince1970: 4), ""), + ChatItem.getSample(6, .groupSnd, Date.init(timeIntervalSince1970: 4), "") + ] + assert(indexToInsertAround(.group, itemsToInsert.last, to: items6, Set([5])), (0, 0)) + + let items7 = [ + ChatItem.getSample(4, .groupSnd, Date.init(timeIntervalSince1970: 4), ""), + ChatItem.getSample(5, .groupSnd, Date.init(timeIntervalSince1970: 4), ""), + ChatItem.getSample(6, .groupSnd, Date.init(timeIntervalSince1970: 4), "") + ] + assert(indexToInsertAround(.group, nil, to: items7, Set([6])), (0, 0)) + + let items8 = [ + ChatItem.getSample(2, .groupSnd, Date.init(timeIntervalSince1970: 4), ""), + ChatItem.getSample(4, .groupSnd, Date.init(timeIntervalSince1970: 3), ""), + ChatItem.getSample(5, .groupSnd, Date.init(timeIntervalSince1970: 4), "") + ] + assert(indexToInsertAround(.group, itemsToInsert.last, to: items8, Set([2])), (0, 0)) + + let items9 = [ + ChatItem.getSample(2, .groupSnd, Date.init(timeIntervalSince1970: 3), ""), + ChatItem.getSample(4, .groupSnd, Date.init(timeIntervalSince1970: 3), ""), + ChatItem.getSample(5, .groupSnd, Date.init(timeIntervalSince1970: 4), "") + ] + assert(indexToInsertAround(.group, itemsToInsert.last, to: items9, Set([5])), (1, 0)) + + let items10 = [ + ChatItem.getSample(4, .groupSnd, Date.init(timeIntervalSince1970: 3), ""), + ChatItem.getSample(5, .groupSnd, Date.init(timeIntervalSince1970: 3), ""), + ChatItem.getSample(6, .groupSnd, Date.init(timeIntervalSince1970: 4), "") + ] + assert(indexToInsertAround(.group, itemsToInsert.last, to: items10, Set([4])), (0, 0)) + + let items11: [ChatItem] = [] + assert(indexToInsertAround(.group, itemsToInsert.last, to: items11, Set([])), (0, 0)) +} diff --git a/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift b/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift new file mode 100644 index 0000000000..0a55ed48cc --- /dev/null +++ b/apps/ios/Shared/Views/Chat/ChatItemsMerger.swift @@ -0,0 +1,456 @@ +// +// ChatItemsMerger.swift +// SimpleX (iOS) +// +// Created by Stanislav Dmitrenko on 02.12.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct MergedItems: Hashable, Equatable { + let items: [MergedItem] + let splits: [SplitRange] + // chat item id, index in list + let indexInParentItems: Dictionary + + static func == (lhs: Self, rhs: Self) -> Bool { + lhs.hashValue == rhs.hashValue + } + + func hash(into hasher: inout Hasher) { + hasher.combine("\(items.hashValue)") + } + + static func create(_ items: [ChatItem], _ revealedItems: Set, _ chatState: ActiveChatState) -> MergedItems { + if items.isEmpty { + return MergedItems(items: [], splits: [], indexInParentItems: [:]) + } + + let unreadCount = chatState.unreadTotal + + let unreadAfterItemId = chatState.unreadAfterItemId + let itemSplits = chatState.splits + var mergedItems: [MergedItem] = [] + // Indexes of splits here will be related to reversedChatItems, not chatModel.chatItems + var splitRanges: [SplitRange] = [] + var indexInParentItems = Dictionary() + var index = 0 + var unclosedSplitIndex: Int? = nil + var unclosedSplitIndexInParent: Int? = nil + var visibleItemIndexInParent = -1 + var unreadBefore = unreadCount - chatState.unreadAfterNewestLoaded + var lastRevealedIdsInMergedItems: BoxedValue<[Int64]>? = nil + var lastRangeInReversedForMergedItems: BoxedValue>? = nil + var recent: MergedItem? = nil + while index < items.count { + let item = items[index] + let prev = index >= 1 ? items[index - 1] : nil + let next = index + 1 < items.count ? items[index + 1] : nil + let category = item.mergeCategory + let itemIsSplit = itemSplits.contains(item.id) + + if item.id == unreadAfterItemId { + unreadBefore = unreadCount - chatState.unreadAfter + } + if item.isRcvNew { + unreadBefore -= 1 + } + + let revealed = item.mergeCategory == nil || revealedItems.contains(item.id) + if recent != nil, case let .grouped(items, _, _, _, mergeCategory, unreadIds, _, _) = recent, mergeCategory == category, let first = items.boxedValue.first, !revealedItems.contains(first.item.id) && !itemIsSplit { + let listItem = ListItem(item: item, prevItem: prev, nextItem: next, unreadBefore: unreadBefore) + items.boxedValue.append(listItem) + + if item.isRcvNew { + unreadIds.boxedValue.insert(item.id) + } + if let lastRevealedIdsInMergedItems, let lastRangeInReversedForMergedItems { + if revealed { + lastRevealedIdsInMergedItems.boxedValue.append(item.id) + } + lastRangeInReversedForMergedItems.boxedValue = lastRangeInReversedForMergedItems.boxedValue.lowerBound ... index + } + } else { + visibleItemIndexInParent += 1 + let listItem = ListItem(item: item, prevItem: prev, nextItem: next, unreadBefore: unreadBefore) + if item.mergeCategory != nil { + if item.mergeCategory != prev?.mergeCategory || lastRevealedIdsInMergedItems == nil { + lastRevealedIdsInMergedItems = BoxedValue(revealedItems.contains(item.id) ? [item.id] : []) + } else if revealed, let lastRevealedIdsInMergedItems { + lastRevealedIdsInMergedItems.boxedValue.append(item.id) + } + lastRangeInReversedForMergedItems = BoxedValue(index ... index) + recent = MergedItem.grouped( + items: BoxedValue([listItem]), + revealed: revealed, + revealedIdsWithinGroup: lastRevealedIdsInMergedItems!, + rangeInReversed: lastRangeInReversedForMergedItems!, + mergeCategory: item.mergeCategory, + unreadIds: BoxedValue(item.isRcvNew ? Set(arrayLiteral: item.id) : Set()), + startIndexInReversedItems: index, + hash: listItem.genHash(revealedItems.contains(prev?.id ?? -1), revealedItems.contains(next?.id ?? -1)) + ) + } else { + lastRangeInReversedForMergedItems = nil + recent = MergedItem.single( + item: listItem, + startIndexInReversedItems: index, + hash: listItem.genHash(revealedItems.contains(prev?.id ?? -1), revealedItems.contains(next?.id ?? -1)) + ) + } + mergedItems.append(recent!) + } + if itemIsSplit { + // found item that is considered as a split + if let unclosedSplitIndex, let unclosedSplitIndexInParent { + // it was at least second split in the list + splitRanges.append(SplitRange(itemId: items[unclosedSplitIndex].id, indexRangeInReversed: unclosedSplitIndex ... index - 1, indexRangeInParentItems: unclosedSplitIndexInParent ... visibleItemIndexInParent - 1)) + } + unclosedSplitIndex = index + unclosedSplitIndexInParent = visibleItemIndexInParent + } else if index + 1 == items.count, let unclosedSplitIndex, let unclosedSplitIndexInParent { + // just one split for the whole list, there will be no more, it's the end + splitRanges.append(SplitRange(itemId: items[unclosedSplitIndex].id, indexRangeInReversed: unclosedSplitIndex ... index, indexRangeInParentItems: unclosedSplitIndexInParent ... visibleItemIndexInParent)) + } + indexInParentItems[item.id] = visibleItemIndexInParent + index += 1 + } + return MergedItems( + items: mergedItems, + splits: splitRanges, + indexInParentItems: indexInParentItems + ) + } + + // Use this check to ensure that mergedItems state based on currently actual state of global + // splits and reversedChatItems + func isActualState() -> Bool { + let im = ItemsModel.shared + // do not load anything if global splits state is different than in merged items because it + // will produce undefined results in terms of loading and placement of items. + // Same applies to reversedChatItems + return indexInParentItems.count == im.reversedChatItems.count && + splits.count == im.chatState.splits.count && + // that's just an optimization because most of the time only 1 split exists + ((splits.count == 1 && splits[0].itemId == im.chatState.splits[0]) || splits.map({ split in split.itemId }).sorted() == im.chatState.splits.sorted()) + } +} + + +enum MergedItem: Identifiable, Hashable, Equatable { + // equatable and hashable implementations allows to see the difference and correctly scroll to items we want + static func == (lhs: Self, rhs: Self) -> Bool { + lhs.hash == rhs.hash + } + + var id: Int64 { newest().item.id } + + func hash(into hasher: inout Hasher) { + hasher.combine(hash) + } + + var hash: String { + switch self { + case .single(_, _, let hash): hash + " 1" + case .grouped(let items, _, _, _, _, _, _, let hash): hash + " \(items.boxedValue.count)" + } + } + + // the item that is always single, cannot be grouped and always revealed + case single( + item: ListItem, + startIndexInReversedItems: Int, + hash: String + ) + + /** The item that can contain multiple items or just one depending on revealed state. When the whole group of merged items is revealed, + * there will be multiple [Grouped] items with revealed flag set to true. When the whole group is collapsed, it will be just one instance + * of [Grouped] item with all grouped items inside [items]. In other words, number of [MergedItem] will always be equal to number of + * visible items in ChatView's EndlessScrollView */ + case grouped ( + items: BoxedValue<[ListItem]>, + revealed: Bool, + // it stores ids for all consecutive revealed items from the same group in order to hide them all on user's action + // it's the same list instance for all Grouped items within revealed group + /** @see reveal */ + revealedIdsWithinGroup: BoxedValue<[Int64]>, + rangeInReversed: BoxedValue>, + mergeCategory: CIMergeCategory?, + unreadIds: BoxedValue>, + startIndexInReversedItems: Int, + hash: String + ) + + func revealItems(_ reveal: Bool, _ revealedItems: Binding>) { + if case .grouped(let items, _, let revealedIdsWithinGroup, _, _, _, _, _) = self { + var newRevealed = revealedItems.wrappedValue + var i = 0 + if reveal { + while i < items.boxedValue.count { + newRevealed.insert(items.boxedValue[i].item.id) + i += 1 + } + } else { + while i < revealedIdsWithinGroup.boxedValue.count { + newRevealed.remove(revealedIdsWithinGroup.boxedValue[i]) + i += 1 + } + revealedIdsWithinGroup.boxedValue.removeAll() + } + revealedItems.wrappedValue = newRevealed + } + } + + var startIndexInReversedItems: Int { + get { + switch self { + case let .single(_, startIndexInReversedItems, _): startIndexInReversedItems + case let .grouped(_, _, _, _, _, _, startIndexInReversedItems, _): startIndexInReversedItems + } + } + } + + func hasUnread() -> Bool { + switch self { + case let .single(item, _, _): item.item.isRcvNew + case let .grouped(_, _, _, _, _, unreadIds, _, _): !unreadIds.boxedValue.isEmpty + } + } + + func newest() -> ListItem { + switch self { + case let .single(item, _, _): item + case let .grouped(items, _, _, _, _, _, _, _): items.boxedValue[0] + } + } + + func oldest() -> ListItem { + switch self { + case let .single(item, _, _): item + case let .grouped(items, _, _, _, _, _, _, _): items.boxedValue[items.boxedValue.count - 1] + } + } + + func lastIndexInReversed() -> Int { + switch self { + case .single: startIndexInReversedItems + case let .grouped(items, _, _, _, _, _, _, _): startIndexInReversedItems + items.boxedValue.count - 1 + } + } +} + +struct SplitRange { + let itemId: Int64 + /** range of indexes inside reversedChatItems where the first element is the split (it's index is [indexRangeInReversed.first]) + * so [0, 1, 2, -100-, 101] if the 3 is a split, SplitRange(indexRange = 3 .. 4) will be this SplitRange instance + * (3, 4 indexes of the splitRange with the split itself at index 3) + * */ + let indexRangeInReversed: ClosedRange + /** range of indexes inside LazyColumn where the first element is the split (it's index is [indexRangeInParentItems.first]) */ + let indexRangeInParentItems: ClosedRange +} + +struct ListItem: Hashable { + let item: ChatItem + let prevItem: ChatItem? + let nextItem: ChatItem? + // how many unread items before (older than) this one (excluding this one) + let unreadBefore: Int + + private func chatDirHash(_ chatDir: CIDirection?) -> Int { + guard let chatDir else { return 0 } + return switch chatDir { + case .directSnd: 0 + case .directRcv: 1 + case .groupSnd: 2 + case let .groupRcv(mem): "\(mem.groupMemberId) \(mem.displayName) \(mem.memberStatus.rawValue) \(mem.memberRole.rawValue) \(mem.image?.hash ?? 0)".hash + case .localSnd: 4 + case .localRcv: 5 + } + } + + // using meta.hashValue instead of parts takes much more time so better to use partial meta here + func genHash(_ prevRevealed: Bool, _ nextRevealed: Bool) -> String { + "\(item.meta.itemId) \(item.meta.updatedAt.hashValue) \(item.meta.itemEdited) \(item.meta.itemDeleted?.hashValue ?? 0) \(item.meta.itemTimed?.hashValue ?? 0) \(item.meta.itemStatus.hashValue) \(item.meta.sentViaProxy ?? false) \(item.mergeCategory?.hashValue ?? 0) \(chatDirHash(item.chatDir)) \(item.reactions.hashValue) \(item.meta.isRcvNew) \(item.text.hash) \(item.file?.hashValue ?? 0) \(item.quotedItem?.itemId ?? 0) \(unreadBefore) \(prevItem?.id ?? 0) \(chatDirHash(prevItem?.chatDir)) \(prevItem?.mergeCategory?.hashValue ?? 0) \(prevRevealed) \(nextItem?.id ?? 0) \(chatDirHash(nextItem?.chatDir)) \(nextItem?.mergeCategory?.hashValue ?? 0) \(nextRevealed)" + } +} + +class ActiveChatState { + var splits: [Int64] = [] + var unreadAfterItemId: Int64 = -1 + // total items after unread after item (exclusive) + var totalAfter: Int = 0 + var unreadTotal: Int = 0 + // exclusive + var unreadAfter: Int = 0 + // exclusive + var unreadAfterNewestLoaded: Int = 0 + + func moveUnreadAfterItem(_ toItemId: Int64?, _ nonReversedItems: [ChatItem]) { + guard let toItemId else { return } + let currentIndex = nonReversedItems.firstIndex(where: { $0.id == unreadAfterItemId }) + let newIndex = nonReversedItems.firstIndex(where: { $0.id == toItemId }) + guard let currentIndex, let newIndex else { + return + } + unreadAfterItemId = toItemId + let unreadDiff = newIndex > currentIndex + ? -nonReversedItems[currentIndex + 1.. fromIndex + ? -nonReversedItems[fromIndex + 1..?, _ newItems: [ChatItem]) { + guard let itemIds else { + // special case when the whole chat became read + unreadTotal = 0 + unreadAfter = 0 + return + } + var unreadAfterItemIndex: Int = -1 + // since it's more often that the newest items become read, it's logical to loop from the end of the list to finish it faster + var i = newItems.count - 1 + var ids = itemIds + // intermediate variables to prevent re-setting state value a lot of times without reason + var newUnreadTotal = unreadTotal + var newUnreadAfter = unreadAfter + while i >= 0 { + let item = newItems[i] + if item.id == unreadAfterItemId { + unreadAfterItemIndex = i + } + if ids.contains(item.id) { + // was unread, now this item is read + if (unreadAfterItemIndex == -1) { + newUnreadAfter -= 1 + } + newUnreadTotal -= 1 + ids.remove(item.id) + if ids.isEmpty { + break + } + } + i -= 1 + } + unreadTotal = newUnreadTotal + unreadAfter = newUnreadAfter + } + + func itemAdded(_ item: (Int64, Bool), _ index: Int) { + if item.1 { + unreadAfter += 1 + unreadTotal += 1 + } + } + + func itemsRemoved(_ itemIds: [(Int64, Int, Bool)], _ newItems: [ChatItem]) { + var newSplits: [Int64] = [] + for split in splits { + let index = itemIds.firstIndex(where: { (delId, _, _) in delId == split }) + // deleted the item that was right before the split between items, find newer item so it will act like the split + if let index { + let idx = itemIds[index].1 - itemIds.filter { (_, delIndex, _) in delIndex <= index }.count + let newSplit = newItems.count > idx && idx >= 0 ? newItems[idx].id : nil + // it the whole section is gone and splits overlap, don't add it at all + if let newSplit, !newSplits.contains(newSplit) { + newSplits.append(newSplit) + } + } else { + newSplits.append(split) + } + } + splits = newSplits + + let index = itemIds.firstIndex(where: { (delId, _, _) in delId == unreadAfterItemId }) + // unread after item was removed + if let index { + let idx = itemIds[index].1 - itemIds.filter { (_, delIndex, _) in delIndex <= index }.count + var newUnreadAfterItemId = newItems.count > idx && idx >= 0 ? newItems[idx].id : nil + let newUnreadAfterItemWasNull = newUnreadAfterItemId == nil + if newUnreadAfterItemId == nil { + // everything on top (including unread after item) were deleted, take top item as unread after id + newUnreadAfterItemId = newItems.first?.id + } + if let newUnreadAfterItemId { + unreadAfterItemId = newUnreadAfterItemId + totalAfter -= itemIds.filter { (_, delIndex, _) in delIndex > index }.count + unreadTotal -= itemIds.filter { (_, delIndex, isRcvNew) in delIndex <= index && isRcvNew }.count + unreadAfter -= itemIds.filter { (_, delIndex, isRcvNew) in delIndex > index && isRcvNew }.count + if newUnreadAfterItemWasNull { + // since the unread after item was moved one item after initial position, adjust counters accordingly + if newItems.first?.isRcvNew == true { + unreadTotal += 1 + unreadAfter -= 1 + } + } + } else { + // all items were deleted, 0 items in chatItems + unreadAfterItemId = -1 + totalAfter = 0 + unreadTotal = 0 + unreadAfter = 0 + } + } else { + totalAfter -= itemIds.count + } + } +} + +class BoxedValue: Equatable, Hashable { + static func == (lhs: BoxedValue, rhs: BoxedValue) -> Bool { + lhs.boxedValue == rhs.boxedValue + } + + func hash(into hasher: inout Hasher) { + hasher.combine("\(self)") + } + + var boxedValue : T + init(_ value: T) { + self.boxedValue = value + } +} + +@MainActor +func visibleItemIndexesNonReversed(_ listState: EndlessScrollView.ListState, _ mergedItems: MergedItems) -> ClosedRange { + let zero = 0 ... 0 + let items = mergedItems.items + if items.isEmpty { + return zero + } + let newest = items.count > listState.firstVisibleItemIndex ? items[listState.firstVisibleItemIndex].startIndexInReversedItems : nil + let oldest = items.count > listState.lastVisibleItemIndex ? items[listState.lastVisibleItemIndex].lastIndexInReversed() : nil + guard let newest, let oldest else { + return zero + } + let size = ItemsModel.shared.reversedChatItems.count + let range = size - oldest ... size - newest + if range.lowerBound < 0 || range.upperBound < 0 { + return zero + } + + // visible items mapped to their underlying data structure which is ItemsModel.shared.reversedChatItems.reversed() + return range +} diff --git a/apps/ios/Shared/Views/Chat/ChatScrollHelpers.swift b/apps/ios/Shared/Views/Chat/ChatScrollHelpers.swift new file mode 100644 index 0000000000..c1a1eec7d2 --- /dev/null +++ b/apps/ios/Shared/Views/Chat/ChatScrollHelpers.swift @@ -0,0 +1,185 @@ +// +// ChatScrollHelpers.swift +// SimpleX (iOS) +// +// Created by Stanislav Dmitrenko on 20.12.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +func loadLastItems(_ loadingMoreItems: Binding, loadingBottomItems: Binding, _ chat: Chat) async { + await MainActor.run { + loadingMoreItems.wrappedValue = true + loadingBottomItems.wrappedValue = true + } + try? await Task.sleep(nanoseconds: 500_000000) + if ChatModel.shared.chatId != chat.chatInfo.id { + await MainActor.run { + loadingMoreItems.wrappedValue = false + loadingBottomItems.wrappedValue = false + } + return + } + await apiLoadMessages(chat.chatInfo.id, ChatPagination.last(count: 50), ItemsModel.shared.chatState) + await MainActor.run { + loadingMoreItems.wrappedValue = false + loadingBottomItems.wrappedValue = false + } +} + +class PreloadState { + static let shared = PreloadState() + var prevFirstVisible: Int64 = Int64.min + var prevItemsCount: Int = 0 + var preloading: Bool = false + + func clear() { + prevFirstVisible = Int64.min + prevItemsCount = 0 + preloading = false + } +} + +func preloadIfNeeded( + _ allowLoadMoreItems: Binding, + _ ignoreLoadingRequests: Binding, + _ listState: EndlessScrollView.ListState, + _ mergedItems: BoxedValue, + loadItems: @escaping (Bool, ChatPagination) async -> Bool, + loadLastItems: @escaping () async -> Void +) { + let state = PreloadState.shared + guard !listState.isScrolling && !listState.isAnimatedScrolling, + !state.preloading, + listState.totalItemsCount > 0 + else { + return + } + if state.prevFirstVisible != listState.firstVisibleItemId as! Int64 || state.prevItemsCount != mergedItems.boxedValue.indexInParentItems.count { + state.preloading = true + let allowLoadMore = allowLoadMoreItems.wrappedValue + Task { + defer { state.preloading = false } + var triedToLoad = true + await preloadItems(mergedItems.boxedValue, allowLoadMore, listState, ignoreLoadingRequests) { pagination in + triedToLoad = await loadItems(false, pagination) + return triedToLoad + } + if triedToLoad { + state.prevFirstVisible = listState.firstVisibleItemId as! Int64 + state.prevItemsCount = mergedItems.boxedValue.indexInParentItems.count + } + // it's important to ask last items when the view is fully covered with items. Otherwise, visible items from one + // split will be merged with last items and position of scroll will change unexpectedly. + if listState.itemsCanCoverScreen && !ItemsModel.shared.lastItemsLoaded { + await loadLastItems() + } + } + } else if listState.itemsCanCoverScreen && !ItemsModel.shared.lastItemsLoaded { + state.preloading = true + Task { + defer { state.preloading = false } + await loadLastItems() + } + } +} + +func preloadItems( + _ mergedItems: MergedItems, + _ allowLoadMoreItems: Bool, + _ listState: EndlessScrollView.ListState, + _ ignoreLoadingRequests: Binding, + _ loadItems: @escaping (ChatPagination) async -> Bool) +async { + let allowLoad = allowLoadMoreItems || mergedItems.items.count == listState.lastVisibleItemIndex + 1 + let remaining = ChatPagination.UNTIL_PRELOAD_COUNT + let firstVisibleIndex = listState.firstVisibleItemIndex + + if !(await preloadItemsBefore()) { + await preloadItemsAfter() + } + + func preloadItemsBefore() async -> Bool { + let splits = mergedItems.splits + let lastVisibleIndex = listState.lastVisibleItemIndex + var lastIndexToLoadFrom: Int? = findLastIndexToLoadFromInSplits(firstVisibleIndex, lastVisibleIndex, remaining, splits) + let items: [ChatItem] = ItemsModel.shared.reversedChatItems.reversed() + if splits.isEmpty && !items.isEmpty && lastVisibleIndex > mergedItems.items.count - remaining { + lastIndexToLoadFrom = items.count - 1 + } + let loadFromItemId: Int64? + if allowLoad, let lastIndexToLoadFrom { + let index = items.count - 1 - lastIndexToLoadFrom + loadFromItemId = index >= 0 ? items[index].id : nil + } else { + loadFromItemId = nil + } + guard let loadFromItemId, ignoreLoadingRequests.wrappedValue != loadFromItemId else { + return false + } + let sizeWas = items.count + let firstItemIdWas = items.first?.id + let triedToLoad = await loadItems(ChatPagination.before(chatItemId: loadFromItemId, count: ChatPagination.PRELOAD_COUNT)) + if triedToLoad && sizeWas == ItemsModel.shared.reversedChatItems.count && firstItemIdWas == ItemsModel.shared.reversedChatItems.last?.id { + ignoreLoadingRequests.wrappedValue = loadFromItemId + return false + } + return triedToLoad + } + + func preloadItemsAfter() async { + let splits = mergedItems.splits + let split = splits.last(where: { $0.indexRangeInParentItems.contains(firstVisibleIndex) }) + // we're inside a splitRange (top --- [end of the splitRange --- we're here --- start of the splitRange] --- bottom) + let reversedItems: [ChatItem] = ItemsModel.shared.reversedChatItems + if let split, split.indexRangeInParentItems.lowerBound + remaining > firstVisibleIndex { + let index = split.indexRangeInReversed.lowerBound + if index >= 0 { + let loadFromItemId = reversedItems[index].id + _ = await loadItems(ChatPagination.after(chatItemId: loadFromItemId, count: ChatPagination.PRELOAD_COUNT)) + } + } + } +} + +func oldestPartiallyVisibleListItemInListStateOrNull(_ listState: EndlessScrollView.ListState) -> ListItem? { + if listState.lastVisibleItemIndex < listState.items.count { + return listState.items[listState.lastVisibleItemIndex].oldest() + } else { + return listState.items.last?.oldest() + } +} + +private func findLastIndexToLoadFromInSplits(_ firstVisibleIndex: Int, _ lastVisibleIndex: Int, _ remaining: Int, _ splits: [SplitRange]) -> Int? { + for split in splits { + // before any split + if split.indexRangeInParentItems.lowerBound > firstVisibleIndex { + if lastVisibleIndex > (split.indexRangeInParentItems.lowerBound - remaining) { + return split.indexRangeInReversed.lowerBound - 1 + } + break + } + let containsInRange = split.indexRangeInParentItems.contains(firstVisibleIndex) + if containsInRange { + if lastVisibleIndex > (split.indexRangeInParentItems.upperBound - remaining) { + return split.indexRangeInReversed.upperBound + } + break + } + } + return nil +} + +/// Disable animation on iOS 15 +func withConditionalAnimation( + _ animation: Animation? = .default, + _ body: () throws -> Result +) rethrows -> Result { + if #available(iOS 16.0, *) { + try withAnimation(animation, body) + } else { + try body() + } +} diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 3ada57ef5d..c136ebc01b 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -9,43 +9,63 @@ import SwiftUI import SimpleXChat import SwiftyGif +import Combine private let memberImageSize: CGFloat = 34 struct ChatView: View { @EnvironmentObject var chatModel: ChatModel - @Environment(\.colorScheme) var colorScheme + @ObservedObject var im = ItemsModel.shared + @State var mergedItems: BoxedValue = BoxedValue(MergedItems.create(ItemsModel.shared.reversedChatItems, [], ItemsModel.shared.chatState)) + @State var revealedItems: Set = Set() + @State var theme: AppTheme = buildTheme() @Environment(\.dismiss) var dismiss + @Environment(\.colorScheme) var colorScheme @Environment(\.presentationMode) var presentationMode + @Environment(\.scenePhase) var scenePhase @State @ObservedObject var chat: Chat @State private var showChatInfoSheet: Bool = false @State private var showAddMembersSheet: Bool = false @State private var composeState = ComposeState() + @State private var selectedRange = NSRange() @State private var keyboardVisible = false + @State private var keyboardHiddenDate = Date.now @State private var connectionStats: ConnectionStats? @State private var customUserProfile: Profile? @State private var connectionCode: String? - @State private var tableView: UITableView? - @State private var loadingItems = false - @State private var firstPage = false - @State private var itemsInView: Set = [] - @State private var scrollProxy: ScrollViewProxy? - @State private var searchMode = false + @State private var loadingMoreItems = false + @State private var loadingTopItems = false + @State private var requestedTopScroll = false + @State private var loadingBottomItems = false + @State private var requestedBottomScroll = false + @State private var showSearch = false @State private var searchText: String = "" @FocusState private var searchFocussed // opening GroupMemberInfoView on member icon - @State private var membersLoaded = false @State private var selectedMember: GMember? = nil // opening GroupLinkView on link button (incognito) @State private var showGroupLinkSheet: Bool = false - @State private var groupLink: String? + @State private var groupLink: CreatedConnLink? @State private var groupLinkMemberRole: GroupMemberRole = .member + @State private var forwardedChatItems: [ChatItem] = [] + @State private var selectedChatItems: Set? = nil + @State private var showDeleteSelectedMessages: Bool = false + @State private var showArchiveSelectedReports: Bool = false + @State private var allowToDeleteSelectedMessagesForAll: Bool = false + @State private var allowLoadMoreItems: Bool = false + @State private var ignoreLoadingRequests: Int64? = nil + @State private var animatedScrollingInProgress: Bool = false + @State private var floatingButtonModel: FloatingButtonModel = FloatingButtonModel() + + @State private var scrollView: EndlessScrollView = EndlessScrollView(frame: .zero) + + @AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial var body: some View { if #available(iOS 16.0, *) { viewBody - .scrollDismissesKeyboard(.immediately) - .keyboardPadding() + .scrollDismissesKeyboard(.immediately) + .toolbarBackground(.hidden, for: .navigationBar) } else { viewBody } @@ -53,93 +73,264 @@ struct ChatView: View { private var viewBody: some View { let cInfo = chat.chatInfo - return VStack(spacing: 0) { - if searchMode { - searchToolbar() - Divider() + return ZStack { + let wallpaperImage = theme.wallpaper.type.image + let wallpaperType = theme.wallpaper.type + let backgroundColor = theme.wallpaper.background ?? wallpaperType.defaultBackgroundColor(theme.base, theme.colors.background) + let tintColor = theme.wallpaper.tint ?? wallpaperType.defaultTintColor(theme.base) + Color.clear.ignoresSafeArea(.all) + .if(wallpaperImage != nil) { view in + view.modifier( + ChatViewBackground(image: wallpaperImage!, imageType: wallpaperType, background: backgroundColor, tint: tintColor) + ) } - ZStack(alignment: .trailing) { - chatItemsList() - if let proxy = scrollProxy { - floatingButtons(proxy) + VStack(spacing: 0) { + ZStack(alignment: .bottomTrailing) { + chatItemsList() + if let groupInfo = chat.chatInfo.groupInfo, !composeState.message.isEmpty { + GroupMentionsView(groupInfo: groupInfo, composeState: $composeState, selectedRange: $selectedRange, keyboardVisible: $keyboardVisible) + } + FloatingButtons(theme: theme, scrollView: scrollView, chat: chat, loadingMoreItems: $loadingMoreItems, loadingTopItems: $loadingTopItems, requestedTopScroll: $requestedTopScroll, loadingBottomItems: $loadingBottomItems, requestedBottomScroll: $requestedBottomScroll, animatedScrollingInProgress: $animatedScrollingInProgress, listState: scrollView.listState, model: floatingButtonModel, reloadItems: { + mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealedItems, im.chatState) + scrollView.updateItems(mergedItems.boxedValue.items) + } + ) + } + connectingText() + if selectedChatItems == nil { + let reason = chat.chatInfo.userCantSendReason + ComposeView( + chat: chat, + composeState: $composeState, + keyboardVisible: $keyboardVisible, + keyboardHiddenDate: $keyboardHiddenDate, + selectedRange: $selectedRange, + disabledText: reason?.composeLabel + ) + .disabled(!cInfo.sendMsgEnabled) + .if(!cInfo.sendMsgEnabled) { v in + v.disabled(true).onTapGesture { + AlertManager.shared.showAlertMsg( + title: "You can't send messages!", + message: reason?.alertMessage + ) + } + } + } else { + SelectedItemsBottomToolbar( + chatItems: ItemsModel.shared.reversedChatItems, + selectedChatItems: $selectedChatItems, + chatInfo: chat.chatInfo, + deleteItems: { forAll in + allowToDeleteSelectedMessagesForAll = forAll + showDeleteSelectedMessages = true + }, + archiveItems: { + showArchiveSelectedReports = true + }, + moderateItems: { + if case let .group(groupInfo) = chat.chatInfo { + showModerateSelectedMessagesAlert(groupInfo) + } + }, + forwardItems: forwardSelectedMessages + ) } } - - Spacer(minLength: 0) - - connectingText() - ComposeView( - chat: chat, - composeState: $composeState, - keyboardVisible: $keyboardVisible - ) - .disabled(!cInfo.sendMsgEnabled) + if im.showLoadingProgress == chat.id { + ProgressView().scaleEffect(2) + } + } + .safeAreaInset(edge: .top) { + VStack(spacing: .zero) { + if showSearch { searchToolbar() } + Divider() + } + .background(ToolbarMaterial.material(toolbarMaterial)) } - .padding(.top, 1) .navigationTitle(cInfo.chatViewName) + .background(theme.colors.background) .navigationBarTitleDisplayMode(.inline) + .environmentObject(theme) + .confirmationDialog(selectedChatItems?.count == 1 ? "Delete message?" : "Delete \((selectedChatItems?.count ?? 0)) messages?", isPresented: $showDeleteSelectedMessages, titleVisibility: .visible) { + Button("Delete for me", role: .destructive) { + if let selected = selectedChatItems { + deleteMessages(chat, selected.sorted(), .cidmInternal, moderate: false, deletedSelectedMessages) } + } + if allowToDeleteSelectedMessagesForAll { + Button(broadcastDeleteButtonText(chat), role: .destructive) { + if let selected = selectedChatItems { + allowToDeleteSelectedMessagesForAll = false + deleteMessages(chat, selected.sorted(), .cidmBroadcast, moderate: false, deletedSelectedMessages) + } + } + } + } + .confirmationDialog(selectedChatItems?.count == 1 ? "Archive report?" : "Archive \((selectedChatItems?.count ?? 0)) reports?", isPresented: $showArchiveSelectedReports, titleVisibility: .visible) { + Button("For me", role: .destructive) { + if let selected = selectedChatItems { + archiveReports(chat.chatInfo, selected.sorted(), false, deletedSelectedMessages) + } + } + if case let ChatInfo.group(groupInfo) = chat.chatInfo, groupInfo.membership.memberActive { + Button("For all moderators", role: .destructive) { + if let selected = selectedChatItems { + archiveReports(chat.chatInfo, selected.sorted(), true, deletedSelectedMessages) + } + } + } + } + .appSheet(item: $selectedMember) { member in + Group { + if case let .group(groupInfo) = chat.chatInfo { + GroupMemberInfoView( + groupInfo: groupInfo, + chat: chat, + groupMember: member, + navigation: true + ) + } + } + } + // it should be presented on top level in order to prevent a bug in SwiftUI on iOS 16 related to .focused() modifier in AddGroupMembersView's search field + .appSheet(isPresented: $showAddMembersSheet) { + Group { + if case let .group(groupInfo) = cInfo { + AddGroupMembersView(chat: chat, groupInfo: groupInfo) + } + } + } + .sheet(isPresented: Binding( + get: { !forwardedChatItems.isEmpty }, + set: { isPresented in + if !isPresented { + forwardedChatItems = [] + selectedChatItems = nil + } + } + )) { + if #available(iOS 16.0, *) { + ChatItemForwardingView(chatItems: forwardedChatItems, fromChatInfo: chat.chatInfo, composeState: $composeState) + .presentationDetents([.fraction(0.8)]) + } else { + ChatItemForwardingView(chatItems: forwardedChatItems, fromChatInfo: chat.chatInfo, composeState: $composeState) + } + } .onAppear { + scrollView.listState.onUpdateListener = onChatItemsUpdated + selectedChatItems = nil + revealedItems = Set() initChatView() + if im.isLoading { + Task { + try? await Task.sleep(nanoseconds: 500_000000) + await MainActor.run { + if im.isLoading { + im.showLoadingProgress = chat.id + } + } + } + } } .onChange(of: chatModel.chatId) { cId in - if cId != nil { + showChatInfoSheet = false + selectedChatItems = nil + revealedItems = Set() + stopAudioPlayer() + if let cId { + if let c = chatModel.getChat(cId) { + chat = c + } + scrollView.listState.onUpdateListener = onChatItemsUpdated initChatView() + theme = buildTheme() + closeSearch() + mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealedItems, im.chatState) + scrollView.updateItems(mergedItems.boxedValue.items) + + if let openAround = chatModel.openAroundItemId, let index = mergedItems.boxedValue.indexInParentItems[openAround] { + scrollView.scrollToItem(index) + } else if let unreadIndex = mergedItems.boxedValue.items.lastIndex(where: { $0.hasUnread() }) { + scrollView.scrollToItem(unreadIndex) + } else { + scrollView.scrollToBottom() + } + if chatModel.openAroundItemId != nil { + chatModel.openAroundItemId = nil + } } else { dismiss() } } - .onDisappear { - VideoPlayerView.players.removeAll() - if chatModel.chatId == cInfo.id && !presentationMode.wrappedValue.isPresented { - chatModel.chatId = nil - DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { - if chatModel.chatId == nil { - chatModel.chatItemStatuses = [:] - chatModel.reversedChatItems = [] - chatModel.groupMembers = [] - membersLoaded = false + .onChange(of: chatModel.openAroundItemId) { openAround in + if let openAround { + closeSearch() + mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealedItems, im.chatState) + scrollView.updateItems(mergedItems.boxedValue.items) + chatModel.openAroundItemId = nil + + if let index = mergedItems.boxedValue.indexInParentItems[openAround] { + scrollView.scrollToItem(index) + } + + // this may already being loading because of changed chat id (see .onChange(of: chat.id) + if !loadingBottomItems { + allowLoadMoreItems = false + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + allowLoadMoreItems = true } } } } + .onDisappear { + VideoPlayerView.players.removeAll() + stopAudioPlayer() + if chatModel.chatId == cInfo.id && !presentationMode.wrappedValue.isPresented { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { + if chatModel.chatId == nil { + chatModel.chatItemStatuses = [:] + ItemsModel.shared.reversedChatItems = [] + ItemsModel.shared.chatState.clear() + chatModel.groupMembers = [] + chatModel.groupMembersIndexes.removeAll() + chatModel.membersLoaded = false + } + } + } + } + .onChange(of: colorScheme) { _ in + theme = buildTheme() + } .toolbar { ToolbarItem(placement: .principal) { - if case let .direct(contact) = cInfo { + if selectedChatItems != nil { + SelectedItemsTopToolbar(selectedChatItems: $selectedChatItems) + } else if case let .direct(contact) = cInfo { Button { Task { - do { - let (stats, profile) = try await apiContactInfo(chat.chatInfo.apiId) - let (ct, code) = try await apiGetContactCode(chat.chatInfo.apiId) - await MainActor.run { - connectionStats = stats - customUserProfile = profile - connectionCode = code - if contact.activeConn?.connectionCode != ct.activeConn?.connectionCode { - chat.chatInfo = .direct(contact: ct) - } - } - } catch let error { - logger.error("apiContactInfo or apiGetContactCode error: \(responseError(error))") - } - await MainActor.run { showChatInfoSheet = true } + showChatInfoSheet = true } } label: { ChatInfoToolbar(chat: chat) } - .sheet(isPresented: $showChatInfoSheet, onDismiss: { - connectionStats = nil - customUserProfile = nil - connectionCode = nil - }) { - ChatInfoView(chat: chat, contact: contact, connectionStats: $connectionStats, customUserProfile: $customUserProfile, localAlias: chat.chatInfo.localAlias, connectionCode: $connectionCode) + .appSheet(isPresented: $showChatInfoSheet, onDismiss: { theme = buildTheme() }) { + ChatInfoView( + chat: chat, + contact: contact, + localAlias: chat.chatInfo.localAlias, + featuresAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences), + currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences), + onSearch: { focusSearch() } + ) } } else if case let .group(groupInfo) = cInfo { Button { - Task { await loadGroupMembers(groupInfo) { showChatInfoSheet = true } } + Task { await chatModel.loadGroupMembers(groupInfo) { showChatInfoSheet = true } } } label: { ChatInfoToolbar(chat: chat) + .tint(theme.colors.primary) } - .appSheet(isPresented: $showChatInfoSheet) { + .appSheet(isPresented: $showChatInfoSheet, onDismiss: { theme = buildTheme() }) { GroupChatInfoView( chat: chat, groupInfo: Binding( @@ -148,7 +339,9 @@ struct ChatView: View { chat.chatInfo = .group(groupInfo: gInfo) chat.created = Date.now } - ) + ), + onSearch: { focusSearch() }, + localAlias: groupInfo.localAlias ) } } else if case .local = cInfo { @@ -156,85 +349,83 @@ struct ChatView: View { } } ToolbarItem(placement: .navigationBarTrailing) { - switch cInfo { - case let .direct(contact): - HStack { - let callsPrefEnabled = contact.mergedPreferences.calls.enabled.forUser - if callsPrefEnabled { - if chatModel.activeCall == nil { - callButton(contact, .audio, imageName: "phone") - .disabled(!contact.ready || !contact.active) - } else if let call = chatModel.activeCall, call.contact.id == cInfo.id { - endCallButton(call) - } + if selectedChatItems != nil { + Button { + withAnimation { + selectedChatItems = nil } - Menu { - if callsPrefEnabled && chatModel.activeCall == nil { - Button { - CallController.shared.startCall(contact, .video) - } label: { - Label("Video call", systemImage: "video") + } label: { + Text("Cancel") + } + } else { + switch cInfo { + case let .direct(contact): + HStack { + let callsPrefEnabled = contact.mergedPreferences.calls.enabled.forUser + if callsPrefEnabled { + if chatModel.activeCall == nil { + callButton(contact, .audio, imageName: "phone") + .disabled(!contact.ready || !contact.active) + } else if let call = chatModel.activeCall, call.contact.id == cInfo.id { + endCallButton(call) } - .disabled(!contact.ready || !contact.active) } - searchButton() - toggleNtfsButton(chat) - .disabled(!contact.ready || !contact.active) - } label: { - Image(systemName: "ellipsis") - } - } - case let .group(groupInfo): - HStack { - if groupInfo.canAddMembers { - if (chat.chatInfo.incognito) { - groupLinkButton() - .appSheet(isPresented: $showGroupLinkSheet) { - GroupLinkView( - groupId: groupInfo.groupId, - groupLink: $groupLink, - groupLinkMemberRole: $groupLinkMemberRole, - showTitle: true, - creatingGroup: false - ) - } - } else { - addMembersButton() - .appSheet(isPresented: $showAddMembersSheet) { - AddGroupMembersView(chat: chat, groupInfo: groupInfo) + Menu { + if callsPrefEnabled && chatModel.activeCall == nil { + Button { + CallController.shared.startCall(contact, .video) + } label: { + Label("Video call", systemImage: "video") } + .disabled(!contact.ready || !contact.active) + } + searchButton() + ToggleNtfsButton(chat: chat) + .disabled(!contact.ready || !contact.active) + } label: { + Image(systemName: "ellipsis") } } - Menu { - searchButton() - toggleNtfsButton(chat) - } label: { - Image(systemName: "ellipsis") + case let .group(groupInfo): + HStack { + if groupInfo.canAddMembers { + if (chat.chatInfo.incognito) { + groupLinkButton() + .appSheet(isPresented: $showGroupLinkSheet) { + GroupLinkView( + groupId: groupInfo.groupId, + groupLink: $groupLink, + groupLinkMemberRole: $groupLinkMemberRole, + showTitle: true, + creatingGroup: false + ) + } + } else { + addMembersButton() + } + } + Menu { + searchButton() + ToggleNtfsButton(chat: chat) + } label: { + Image(systemName: "ellipsis") + } } + case .local: + searchButton() + default: + EmptyView() } - case .local: - searchButton() - default: - EmptyView() } } } } - private func loadGroupMembers(_ groupInfo: GroupInfo, updateView: @escaping () -> Void = {}) async { - let groupMembers = await apiListMembers(groupInfo.groupId) - await MainActor.run { - if chatModel.chatId == groupInfo.id { - chatModel.groupMembers = groupMembers.map { GMember.init($0) } - membersLoaded = true - updateView() - } - } - } - private func initChatView() { let cInfo = chat.chatInfo - if case let .direct(contact) = cInfo { + // This check prevents the call to apiContactInfo after the app is suspended, and the database is closed. + if case .active = scenePhase, + case let .direct(contact) = cInfo { Task { do { let (stats, _) = try await apiContactInfo(chat.chatInfo.apiId) @@ -248,7 +439,8 @@ struct ChatView: View { } } } - if chatModel.draftChatId == cInfo.id, let draft = chatModel.draft { + if chatModel.draftChatId == cInfo.id && !composeState.forwarding, + let draft = chatModel.draft { composeState = draft } if chat.chatStats.unreadChat { @@ -256,6 +448,40 @@ struct ChatView: View { await markChatUnread(chat, unreadChat: false) } } + floatingButtonModel.updateOnListChange(scrollView.listState) + } + + private func scrollToItemId(_ itemId: ChatItem.ID) { + Task { + do { + var index = mergedItems.boxedValue.indexInParentItems[itemId] + if index == nil { + let pagination = ChatPagination.around(chatItemId: itemId, count: ChatPagination.PRELOAD_COUNT * 2) + let oldSize = ItemsModel.shared.reversedChatItems.count + let triedToLoad = await loadChatItems(chat, pagination) + if !triedToLoad { + return + } + var repeatsLeft = 50 + while oldSize == ItemsModel.shared.reversedChatItems.count && repeatsLeft > 0 { + try await Task.sleep(nanoseconds: 20_000000) + repeatsLeft -= 1 + } + index = mergedItems.boxedValue.indexInParentItems[itemId] + } + if let index { + closeKeyboardAndRun { + Task { + await MainActor.run { animatedScrollingInProgress = true } + await scrollView.scrollToItemAnimated(min(ItemsModel.shared.reversedChatItems.count - 1, index)) + await MainActor.run { animatedScrollingInProgress = false } + } + } + } + } catch { + logger.error("Error scrolling to item: \(error)") + } + } } private func searchToolbar() -> some View { @@ -264,7 +490,7 @@ struct ChatView: View { Image(systemName: "magnifyingglass") TextField("Search", text: $searchText) .focused($searchFocussed) - .foregroundColor(.primary) + .foregroundColor(theme.colors.onBackground) .frame(maxWidth: .infinity) Button { @@ -274,151 +500,399 @@ struct ChatView: View { } } .padding(EdgeInsets(top: 7, leading: 7, bottom: 7, trailing: 7)) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .background(Color(.tertiarySystemFill)) .cornerRadius(10.0) Button ("Cancel") { - searchText = "" - searchMode = false - searchFocussed = false - DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { - chatModel.reversedChatItems = [] - loadChat(chat: chat) - } + closeSearch() + searchTextChanged("") } } .padding(.horizontal) .padding(.vertical, 8) } - + private func voiceWithoutFrame(_ ci: ChatItem) -> Bool { - ci.content.msgContent?.isVoice == true && ci.content.text.count == 0 && ci.quotedItem == nil + ci.content.msgContent?.isVoice == true && ci.content.text.count == 0 && ci.quotedItem == nil && ci.meta.itemForwarded == nil + } + + private func filtered(_ reversedChatItems: Array) -> Array { + reversedChatItems + .enumerated() + .filter { (index, chatItem) in + if let mergeCategory = chatItem.mergeCategory, index > 0 { + mergeCategory != reversedChatItems[index - 1].mergeCategory + } else { + true + } + } + .map { $0.element } } private func chatItemsList() -> some View { let cInfo = chat.chatInfo return GeometryReader { g in - ScrollViewReader { proxy in - ScrollView { - LazyVStack(spacing: 0) { - ForEach(chatModel.reversedChatItems, id: \.viewId) { ci in - let voiceNoFrame = voiceWithoutFrame(ci) - let maxWidth = cInfo.chatType == .group - ? voiceNoFrame - ? (g.size.width - 28) - 42 - : (g.size.width - 28) * 0.84 - 42 - : voiceNoFrame - ? (g.size.width - 32) - : (g.size.width - 32) * 0.84 - chatItemView(ci, maxWidth) - .scaleEffect(x: 1, y: -1, anchor: .center) - .onAppear { - itemsInView.insert(ci.viewId) - loadChatItems(cInfo, ci, proxy) - if ci.isRcvNew { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { - if chatModel.chatId == cInfo.id && itemsInView.contains(ci.viewId) { - Task { - await apiMarkChatItemRead(cInfo, ci) - } - } - } - } - } - .onDisappear { - itemsInView.remove(ci.viewId) - } - } - } + //let _ = logger.debug("Reloading chatItemsList with number of itmes: \(im.reversedChatItems.count)") + ScrollRepresentable(scrollView: scrollView) { (index: Int, mergedItem: MergedItem) in + let ci = switch mergedItem { + case let .single(item, _, _): item.item + case let .grouped(items, _, _, _, _, _, _, _): items.boxedValue.last!.item } - .onAppear { - scrollProxy = proxy + let voiceNoFrame = voiceWithoutFrame(ci) + let maxWidth = cInfo.chatType == .group + ? voiceNoFrame + ? (g.size.width - 28) - 42 + : (g.size.width - 28) * 0.84 - 42 + : voiceNoFrame + ? (g.size.width - 32) + : (g.size.width - 32) * 0.84 + return ChatItemWithMenu( + chat: $chat, + index: index, + isLastItem: index == mergedItems.boxedValue.items.count - 1, + chatItem: ci, + scrollToItemId: scrollToItemId, + merged: mergedItem, + maxWidth: maxWidth, + composeState: $composeState, + selectedMember: $selectedMember, + showChatInfoSheet: $showChatInfoSheet, + revealedItems: $revealedItems, + selectedChatItems: $selectedChatItems, + forwardedChatItems: $forwardedChatItems, + searchText: $searchText, + closeKeyboardAndRun: closeKeyboardAndRun + ) + // crashes on Cell size calculation without this line + .environmentObject(ChatModel.shared) + .environmentObject(theme) // crashes without this line when scrolling to the first unread in EndlessScrollVIew + .id(ci.id) // Required to trigger `onAppear` on iOS15 + } + .onAppear { + if !im.isLoading { + updateWithInitiallyLoadedItems() } - .onTapGesture { hideKeyboard() } - .onChange(of: searchText) { _ in - loadChat(chat: chat, search: searchText) + } + .onChange(of: im.isLoading) { loading in + if !loading { + updateWithInitiallyLoadedItems() } - .onChange(of: chatModel.chatId) { _ in - if let chatId = chatModel.chatId, let c = chatModel.getChat(chatId) { - chat = c - showChatInfoSheet = false - loadChat(chat: c) - DispatchQueue.main.async { - scrollToBottom(proxy) - } + } + .onChange(of: im.reversedChatItems) { items in + mergedItems.boxedValue = MergedItems.create(items, revealedItems, im.chatState) + scrollView.updateItems(mergedItems.boxedValue.items) + if im.itemAdded { + im.itemAdded = false + if scrollView.listState.firstVisibleItemIndex < 2 { + scrollView.scrollToBottomAnimated() + } else { + scrollView.scroll(by: 34) } } } + .onChange(of: revealedItems) { revealed in + mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealed, im.chatState) + scrollView.updateItems(mergedItems.boxedValue.items) + } + .onChange(of: chat.id) { _ in + allowLoadMoreItems = false + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + allowLoadMoreItems = true + } + } + .padding(.vertical, -100) + .onTapGesture { hideKeyboard() } + .onChange(of: searchText) { s in + if showSearch { + searchTextChanged(s) + } + } } - .scaleEffect(x: 1, y: -1, anchor: .center) } @ViewBuilder private func connectingText() -> some View { if case let .direct(contact) = chat.chatInfo, - !contact.ready, + !contact.sndReady, contact.active, !contact.nextSendGrpInv { Text("connecting…") .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .padding(.top) } else { EmptyView() } } - - private func floatingButtons(_ proxy: ScrollViewProxy) -> some View { - let counts = chatModel.unreadChatItemCounts(itemsInView: itemsInView) - return VStack { - let unreadAbove = chat.chatStats.unreadCount - counts.unreadBelow - if unreadAbove > 0 { - circleButton { - unreadCountText(unreadAbove) - .font(.callout) - .foregroundColor(.accentColor) + + private func updateWithInitiallyLoadedItems() { + if mergedItems.boxedValue.items.isEmpty { + mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealedItems, ItemsModel.shared.chatState) + } + let unreadIndex = mergedItems.boxedValue.items.lastIndex(where: { $0.hasUnread() }) + let unreadItemId: Int64? = if let unreadIndex { mergedItems.boxedValue.items[unreadIndex].newest().item.id } else { nil } + // this helps to speed up initial process of setting scroll position and reduce time needed + // to layout items on screen + if let unreadIndex, let unreadItemId { + scrollView.setScrollPosition(unreadIndex, unreadItemId) + } + scrollView.updateItems(mergedItems.boxedValue.items) + if let unreadIndex { + scrollView.scrollToItem(unreadIndex) + } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + allowLoadMoreItems = true + } + } + + private func searchTextChanged(_ s: String) { + Task { + await loadChat(chat: chat, search: s) + mergedItems.boxedValue = MergedItems.create(im.reversedChatItems, revealedItems, im.chatState) + await MainActor.run { + scrollView.updateItems(mergedItems.boxedValue.items) + } + if !s.isEmpty { + scrollView.scrollToBottom() + } else if let index = scrollView.listState.items.lastIndex(where: { $0.hasUnread() }) { + // scroll to the top unread item + scrollView.scrollToItem(index) + } else { + scrollView.scrollToBottom() + } + } + } + + class FloatingButtonModel: ObservableObject { + @Published var unreadAbove: Int = 0 + @Published var unreadBelow: Int = 0 + @Published var isNearBottom: Bool = true + @Published var date: Date? = nil + @Published var isDateVisible: Bool = false + var hideDateWorkItem: DispatchWorkItem? = nil + + func updateOnListChange(_ listState: EndlessScrollView.ListState) { + let lastVisibleItem = oldestPartiallyVisibleListItemInListStateOrNull(listState) + let unreadBelow = if let lastVisibleItem { + max(0, ItemsModel.shared.chatState.unreadTotal - lastVisibleItem.unreadBefore) + } else { + 0 + } + let unreadAbove = ItemsModel.shared.chatState.unreadTotal - unreadBelow + let date: Date? = + if let lastVisible = listState.visibleItems.last { + Calendar.current.startOfDay(for: lastVisible.item.oldest().item.meta.itemTs) + } else { + nil } - .onTapGesture { scrollUp(proxy) } - .contextMenu { - Button { - if let ci = chatModel.topItemInView(itemsInView: itemsInView) { - Task { - await markChatRead(chat, aboveItem: ci) + + // set the counters and date indicator + DispatchQueue.main.async { [weak self] in + guard let it = self else { return } + it.setDate(visibility: true) + it.unreadAbove = unreadAbove + it.unreadBelow = unreadBelow + it.date = date + } + + // set floating button indication mode + let nearBottom = listState.firstVisibleItemIndex < 1 + if nearBottom != self.isNearBottom { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { [weak self] in + self?.isNearBottom = nearBottom + } + } + + // hide Date indicator after 1 second of no scrolling + hideDateWorkItem?.cancel() + let workItem = DispatchWorkItem { [weak self] in + guard let it = self else { return } + it.setDate(visibility: false) + it.hideDateWorkItem = nil + } + DispatchQueue.main.async { [weak self] in + self?.hideDateWorkItem = workItem + DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: workItem) + } + } + + func resetDate() { + date = nil + isDateVisible = false + } + + private func setDate(visibility isVisible: Bool) { + if isVisible { + if !isNearBottom, + !isDateVisible, + let date, !Calendar.current.isDateInToday(date) { + withAnimation { self.isDateVisible = true } + } + } else if isDateVisible { + withAnimation { self.isDateVisible = false } + } + } + + } + + private struct FloatingButtons: View { + let theme: AppTheme + let scrollView: EndlessScrollView + let chat: Chat + @Binding var loadingMoreItems: Bool + @Binding var loadingTopItems: Bool + @Binding var requestedTopScroll: Bool + @Binding var loadingBottomItems: Bool + @Binding var requestedBottomScroll: Bool + @Binding var animatedScrollingInProgress: Bool + let listState: EndlessScrollView.ListState + @ObservedObject var model: FloatingButtonModel + let reloadItems: () -> Void + + var body: some View { + ZStack(alignment: .top) { + if let date = model.date { + DateSeparator(date: date) + .padding(.vertical, 4).padding(.horizontal, 8) + .background(.thinMaterial) + .clipShape(Capsule()) + .opacity(model.isDateVisible ? 1 : 0) + .padding(.vertical, 4) + } + VStack { + if model.unreadAbove > 0 && !animatedScrollingInProgress { + if loadingTopItems && requestedTopScroll { + circleButton { ProgressView() } + } else { + circleButton { + unreadCountText(model.unreadAbove) + .font(.callout) + .foregroundColor(theme.colors.primary) + } + .onTapGesture { + if loadingTopItems { + requestedTopScroll = true + requestedBottomScroll = false + } else { + scrollToTopUnread() + } + } + .contextMenu { + Button { + Task { + await markChatRead(chat) + } + } label: { + Label("Mark read", systemImage: "checkmark") + } + } + } + } + Spacer() + if listState.firstVisibleItemIndex != 0 && !animatedScrollingInProgress { + if loadingBottomItems && requestedBottomScroll { + circleButton { ProgressView() } + } else { + circleButton { + Group { + if model.unreadBelow > 0 { + unreadCountText(model.unreadBelow) + .font(.callout) + .foregroundColor(theme.colors.primary) + } else { + Image(systemName: "chevron.down").foregroundColor(theme.colors.primary) + } + } + } + .onTapGesture { + if loadingBottomItems || !ItemsModel.shared.lastItemsLoaded { + requestedTopScroll = false + requestedBottomScroll = true + } else { + scrollToBottom() + } } } - } label: { - Label("Mark read", systemImage: "checkmark") } } + .padding() + .frame(maxWidth: .infinity, alignment: .trailing) } - Spacer() - if counts.unreadBelow > 0 { - circleButton { - unreadCountText(counts.unreadBelow) - .font(.callout) - .foregroundColor(.accentColor) + .onChange(of: loadingTopItems) { loading in + if !loading && requestedTopScroll { + requestedTopScroll = false + scrollToTopUnread() } - .onTapGesture { scrollToBottom(proxy) } - } else if counts.totalBelow > 16 { - circleButton { - Image(systemName: "chevron.down") - .foregroundColor(.accentColor) + } + .onChange(of: loadingBottomItems) { loading in + if !loading && requestedBottomScroll && ItemsModel.shared.lastItemsLoaded { + requestedBottomScroll = false + scrollToBottom() + } + } + .onDisappear(perform: model.resetDate) + } + + private func scrollToTopUnread() { + Task { + if !ItemsModel.shared.chatState.splits.isEmpty { + await MainActor.run { loadingMoreItems = true } + await loadChat(chatId: chat.id, openAroundItemId: nil, clearItems: false) + await MainActor.run { reloadItems() } + if let index = listState.items.lastIndex(where: { $0.hasUnread() }) { + await MainActor.run { animatedScrollingInProgress = true } + await scrollView.scrollToItemAnimated(index) + await MainActor.run { animatedScrollingInProgress = false } + } + await MainActor.run { loadingMoreItems = false } + } else if let index = listState.items.lastIndex(where: { $0.hasUnread() }) { + await MainActor.run { animatedScrollingInProgress = true } + // scroll to the top unread item + await scrollView.scrollToItemAnimated(index) + await MainActor.run { animatedScrollingInProgress = false } + } else { + logger.debug("No more unread items, total: \(listState.items.count)") } - .onTapGesture { scrollToBottom(proxy) } } } - .padding() - } - - private func circleButton(_ content: @escaping () -> Content) -> some View { - ZStack { - Circle() - .foregroundColor(Color(uiColor: .tertiarySystemGroupedBackground)) - .frame(width: 44, height: 44) - content() + + private func scrollToBottom() { + animatedScrollingInProgress = true + Task { + await scrollView.scrollToItemAnimated(0, top: false) + await MainActor.run { animatedScrollingInProgress = false } + } + } + + private func circleButton(_ content: @escaping () -> Content) -> some View { + ZStack { + Circle() + .foregroundColor(Color(uiColor: .tertiarySystemGroupedBackground)) + .frame(width: 44, height: 44) + content() + } } } - + + private struct DateSeparator: View { + let date: Date + + var body: some View { + Text(String.localizedStringWithFormat( + NSLocalizedString("%@, %@", comment: "format for date separator in chat"), + date.formatted(.dateTime.weekday(.abbreviated)), + date.formatted( + Calendar.current.isDate(date, equalTo: .now, toGranularity: .year) + ? .dateTime.day().month(.abbreviated) + : .dateTime.day().month(.abbreviated).year() + ) + )) + .font(.callout) + .fontWeight(.medium) + .foregroundStyle(.secondary) + } + } + private func callButton(_ contact: Contact, _ media: CallMediaType, imageName: String) -> some View { Button { CallController.shared.startCall(contact, media) @@ -429,8 +903,8 @@ struct ChatView: View { private func endCallButton(_ call: Call) -> some View { Button { - if let uuid = call.callkitUUID { - CallController.shared.endCall(callUUID: uuid) + if CallController.useCallKit(), let callUUID = call.callUUID { + CallController.shared.endCall(callUUID: callUUID) } else { CallController.shared.endCall(call: call) {} } @@ -441,18 +915,40 @@ struct ChatView: View { private func searchButton() -> some View { Button { - searchMode = true - searchFocussed = true - searchText = "" + focusSearch() } label: { Label("Search", systemImage: "magnifyingglass") } } - + + private func focusSearch() { + showSearch = true + searchFocussed = true + searchText = "" + } + + private func closeSearch() { + showSearch = false + searchText = "" + searchFocussed = false + } + + private func closeKeyboardAndRun(_ action: @escaping () -> Void) { + var delay: TimeInterval = 0 + if keyboardVisible || keyboardHiddenDate.timeIntervalSinceNow >= -1 || showSearch { + delay = 0.5 + closeSearch() + hideKeyboard() + } + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { + action() + } + } + private func addMembersButton() -> some View { Button { if case let .group(gInfo) = chat.chatInfo { - Task { await loadGroupMembers(gInfo) { showAddMembersSheet = true } } + Task { await chatModel.loadGroupMembers(gInfo) { showAddMembersSheet = true } } } } label: { Image(systemName: "person.crop.circle.badge.plus") @@ -478,141 +974,555 @@ struct ChatView: View { } } - private func loadChatItems(_ cInfo: ChatInfo, _ ci: ChatItem, _ proxy: ScrollViewProxy) { - if let firstItem = chatModel.reversedChatItems.last, firstItem.id == ci.id { - if loadingItems || firstPage { return } - loadingItems = true - Task { - do { - let items = try await apiGetChatItems( - type: cInfo.chatType, - id: cInfo.apiId, - pagination: .before(chatItemId: firstItem.id, count: 50), - search: searchText - ) - await MainActor.run { - if items.count == 0 { - firstPage = true - } else { - chatModel.reversedChatItems.append(contentsOf: items.reversed()) - } - loadingItems = false - } - } catch let error { - logger.error("apiGetChat error: \(responseError(error))") - await MainActor.run { loadingItems = false } + private func showModerateSelectedMessagesAlert(_ groupInfo: GroupInfo) { + guard let count = selectedChatItems?.count, count > 0 else { return } + + AlertManager.shared.showAlert(Alert( + title: Text(count == 1 ? "Delete member message?" : "Delete \(count) messages of members?"), + message: Text( + groupInfo.fullGroupPreferences.fullDelete.on + ? (count == 1 ? "The message will be deleted for all members." : "The messages will be deleted for all members.") + : (count == 1 ? "The message will be marked as moderated for all members." : "The messages will be marked as moderated for all members.") + ), + primaryButton: .destructive(Text("Delete")) { + if let selected = selectedChatItems { + deleteMessages(chat, selected.sorted(), .cidmBroadcast, moderate: true, deletedSelectedMessages) } + }, + secondaryButton: .cancel() + )) + } + + private func deletedSelectedMessages() async { + await MainActor.run { + withAnimation { + selectedChatItems = nil } } } - - @ViewBuilder private func chatItemView(_ ci: ChatItem, _ maxWidth: CGFloat) -> some View { - ChatItemWithMenu( - chat: chat, - chatItem: ci, - maxWidth: maxWidth, - composeState: $composeState, - selectedMember: $selectedMember, - chatView: self + + private func forwardSelectedMessages() { + Task { + do { + if let selectedChatItems { + let (validItems, confirmation) = try await apiPlanForwardChatItems( + type: chat.chatInfo.chatType, + id: chat.chatInfo.apiId, + itemIds: Array(selectedChatItems) + ) + if let confirmation { + if validItems.count > 0 { + showAlert( + String.localizedStringWithFormat( + NSLocalizedString("Forward %d message(s)?", comment: "alert title"), + validItems.count + ), + message: forwardConfirmationText(confirmation) + "\n" + + NSLocalizedString("Forward messages without files?", comment: "alert message") + ) { + switch confirmation { + case let .filesNotAccepted(fileIds): + [forwardAction(validItems), downloadAction(fileIds), cancelAlertAction] + default: + [forwardAction(validItems), cancelAlertAction] + } + } + } else { + showAlert( + NSLocalizedString("Nothing to forward!", comment: "alert title"), + message: forwardConfirmationText(confirmation) + ) { + switch confirmation { + case let .filesNotAccepted(fileIds): + [downloadAction(fileIds), cancelAlertAction] + default: + [okAlertAction] + } + } + } + } else { + await openForwardingSheet(validItems) + } + } + } catch { + logger.error("Plan forward chat items failed: \(error.localizedDescription)") + } + } + + func forwardConfirmationText(_ fc: ForwardConfirmation) -> String { + switch fc { + case let .filesNotAccepted(fileIds): + String.localizedStringWithFormat( + NSLocalizedString("%d file(s) were not downloaded.", comment: "forward confirmation reason"), + fileIds.count + ) + case let .filesInProgress(filesCount): + String.localizedStringWithFormat( + NSLocalizedString("%d file(s) are still being downloaded.", comment: "forward confirmation reason"), + filesCount + ) + case let .filesMissing(filesCount): + String.localizedStringWithFormat( + NSLocalizedString("%d file(s) were deleted.", comment: "forward confirmation reason"), + filesCount + ) + case let .filesFailed(filesCount): + String.localizedStringWithFormat( + NSLocalizedString("%d file(s) failed to download.", comment: "forward confirmation reason"), + filesCount + ) + } + } + + func forwardAction(_ items: [Int64]) -> UIAlertAction { + UIAlertAction( + title: NSLocalizedString("Forward messages", comment: "alert action"), + style: .default, + handler: { _ in Task { await openForwardingSheet(items) } } + ) + } + + func downloadAction(_ fileIds: [Int64]) -> UIAlertAction { + UIAlertAction( + title: NSLocalizedString("Download files", comment: "alert action"), + style: .default, + handler: { _ in + Task { + if let user = ChatModel.shared.currentUser { + await receiveFiles(user: user, fileIds: fileIds) + } + } + } + ) + } + + func openForwardingSheet(_ items: [Int64]) async { + let im = ItemsModel.shared + var items = Set(items) + var fci = [ChatItem]() + for reversedChatItem in im.reversedChatItems { + if items.contains(reversedChatItem.id) { + items.remove(reversedChatItem.id) + fci.insert(reversedChatItem, at: 0) + } + if items.isEmpty { break } + } + await MainActor.run { forwardedChatItems = fci } + } + } + + private func loadChatItems(_ chat: Chat, _ pagination: ChatPagination) async -> Bool { + if loadingMoreItems { return false } + await MainActor.run { + loadingMoreItems = true + if case .before = pagination { + loadingTopItems = true + } else if case .after = pagination { + loadingBottomItems = true + } + } + let triedToLoad = await loadChatItemsUnchecked(chat, pagination) + await MainActor.run { + loadingMoreItems = false + if case .before = pagination { + loadingTopItems = false + } else if case .after = pagination { + loadingBottomItems = false + } + } + return triedToLoad + } + + private func loadChatItemsUnchecked(_ chat: Chat, _ pagination: ChatPagination) async -> Bool { + await apiLoadMessages( + chat.chatInfo.id, + pagination, + im.chatState, + searchText, + nil, + { visibleItemIndexesNonReversed(scrollView.listState, mergedItems.boxedValue) } + ) + return true + } + + func stopAudioPlayer() { + VoiceItemState.chatView.values.forEach { $0.audioPlayer?.stop() } + VoiceItemState.chatView = [:] + } + + func onChatItemsUpdated() { + if !mergedItems.boxedValue.isActualState() { + //logger.debug("Items are not actual, waiting for the next update: \(String(describing: mergedItems.boxedValue.splits)) \(ItemsModel.shared.chatState.splits), \(mergedItems.boxedValue.indexInParentItems.count) vs \(ItemsModel.shared.reversedChatItems.count)") + return + } + floatingButtonModel.updateOnListChange(scrollView.listState) + preloadIfNeeded( + $allowLoadMoreItems, + $ignoreLoadingRequests, + scrollView.listState, + mergedItems, + loadItems: { unchecked, pagination in + if unchecked { + await loadChatItemsUnchecked(chat, pagination) + } else { + await loadChatItems(chat, pagination) + } + }, + loadLastItems: { + if !loadingMoreItems { + await loadLastItems($loadingMoreItems, loadingBottomItems: $loadingBottomItems, chat) + } + } ) } private struct ChatItemWithMenu: View { @EnvironmentObject var m: ChatModel - @Environment(\.colorScheme) var colorScheme - @ObservedObject var chat: Chat - var chatItem: ChatItem - var maxWidth: CGFloat + @EnvironmentObject var theme: AppTheme + @AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var profileRadius = defaultProfileImageCorner + @Binding @ObservedObject var chat: Chat + @ObservedObject var dummyModel: ChatItemDummyModel = .shared + let index: Int + let isLastItem: Bool + let chatItem: ChatItem + let scrollToItemId: (ChatItem.ID) -> Void + let merged: MergedItem + let maxWidth: CGFloat @Binding var composeState: ComposeState @Binding var selectedMember: GMember? - var chatView: ChatView + @Binding var showChatInfoSheet: Bool + @Binding var revealedItems: Set @State private var deletingItem: ChatItem? = nil @State private var showDeleteMessage = false @State private var deletingItems: [Int64] = [] @State private var showDeleteMessages = false - @State private var revealed = false + @State private var archivingReports: Set? = nil + @State private var showArchivingReports = false @State private var showChatItemInfoSheet: Bool = false @State private var chatItemInfo: ChatItemInfo? + @State private var msgWidth: CGFloat = 0 + @State private var touchInProgress: Bool = false + + @Binding var selectedChatItems: Set? + @Binding var forwardedChatItems: [ChatItem] + + @Binding var searchText: String + var closeKeyboardAndRun: (@escaping () -> Void) -> Void @State private var allowMenu: Bool = true + @State private var markedRead = false + @State private var markReadTask: Task? = nil + @State private var actionSheet: SomeActionSheet? = nil - @State private var audioPlayer: AudioPlayer? - @State private var playbackState: VoiceMessagePlaybackState = .noPlayback - @State private var playbackTime: TimeInterval? + var revealed: Bool { revealedItems.contains(chatItem.id) } + + typealias ItemSeparation = (timestamp: Bool, largeGap: Bool, date: Date?) + + private func reveal(_ yes: Bool) -> Void { + merged.revealItems(yes, $revealedItems) + } + + func getItemSeparation(_ chatItem: ChatItem, _ prevItem: ChatItem?) -> ItemSeparation { + guard let prevItem else { + return ItemSeparation(timestamp: true, largeGap: true, date: nil) + } + + let sameMemberAndDirection = if case .groupRcv(let prevGroupMember) = prevItem.chatDir, case .groupRcv(let groupMember) = chatItem.chatDir { + groupMember.groupMemberId == prevGroupMember.groupMemberId + } else { + chatItem.chatDir.sent == prevItem.chatDir.sent + } + let largeGap = !sameMemberAndDirection || prevItem.meta.itemTs.timeIntervalSince(chatItem.meta.itemTs) > 60 + + return ItemSeparation( + timestamp: largeGap || formatTimestampMeta(chatItem.meta.itemTs) != formatTimestampMeta(prevItem.meta.itemTs), + largeGap: largeGap, + date: Calendar.current.isDate(chatItem.meta.itemTs, inSameDayAs: prevItem.meta.itemTs) ? nil : prevItem.meta.itemTs + ) + } + + func shouldShowAvatar(_ current: ChatItem, _ older: ChatItem?) -> Bool { + let oldIsGroupRcv = switch older?.chatDir { + case .groupRcv: true + default: false + } + let sameMember = switch (older?.chatDir, current.chatDir) { + case (.groupRcv(let oldMember), .groupRcv(let member)): + oldMember.memberId == member.memberId + default: + false + } + if case .groupRcv = current.chatDir, (older == nil || (!oldIsGroupRcv || !sameMember)) { + return true + } else { + return false + } + } var body: some View { - let (currIndex, nextItem) = m.getNextChatItem(chatItem) - let ciCategory = chatItem.mergeCategory - if (ciCategory != nil && ciCategory == nextItem?.mergeCategory) { - // memberConnected events and deleted items are aggregated at the last chat item in a row, see ChatItemView - ZStack {} // scroll doesn't work if it's EmptyView() + let im = ItemsModel.shared + + let last = isLastItem ? im.reversedChatItems.last : nil + let listItem = merged.newest() + let item = listItem.item + let range: ClosedRange? = if case let .grouped(_, _, _, rangeInReversed, _, _, _, _) = merged { + rangeInReversed.boxedValue } else { - let (prevHidden, prevItem) = m.getPrevShownChatItem(currIndex, ciCategory) - let range = itemsRange(currIndex, prevHidden) - if revealed, let range = range { - let items = Array(zip(Array(range), m.reversedChatItems[range])) - ForEach(items, id: \.1.viewId) { (i, ci) in - let prev = i == prevHidden ? prevItem : m.reversedChatItems[i + 1] - chatItemView(ci, nil, prev) + nil + } + let showAvatar = shouldShowAvatar(item, listItem.nextItem) + let single = switch merged { + case .single: true + default: false + } + let itemSeparation = getItemSeparation(item, single || revealed ? listItem.prevItem: nil) + return VStack(spacing: 0) { + if let last { + DateSeparator(date: last.meta.itemTs).padding(8) + } + chatItemListView(range, showAvatar, item, itemSeparation) + .overlay { + if let selected = selectedChatItems, chatItem.canBeDeletedForSelf { + Color.clear + .contentShape(Rectangle()) + .simultaneousGesture(TapGesture().onEnded { + let checked = selected.contains(chatItem.id) + selectUnselectChatItem(select: !checked, chatItem) + }) + } } + if let date = itemSeparation.date { + DateSeparator(date: date).padding(8) + } + } + .onAppear { + if markedRead { + return } else { - chatItemView(chatItem, range, prevItem) + markedRead = true + } + if let range { + let (itemIds, unreadMentions) = unreadItemIds(range) + if !itemIds.isEmpty { + waitToMarkRead { + await apiMarkChatItemsRead(chat.chatInfo, itemIds, mentionsRead: unreadMentions) + } + } + } else if chatItem.isRcvNew { + waitToMarkRead { + await apiMarkChatItemsRead(chat.chatInfo, [chatItem.id], mentionsRead: chatItem.meta.userMention ? 1 : 0) + } + } + } + .onDisappear { + markReadTask?.cancel() + markedRead = false + } + .actionSheet(item: $actionSheet) { $0.actionSheet } + // skip updating struct on touch if no need to show GoTo button + .if(touchInProgress || searchIsNotBlank || (chatItem.meta.itemForwarded != nil && chatItem.meta.itemForwarded != .unknown)) { + // long press listener steals taps from top-level listener, so repeating it's logic here as well + $0.onTapGesture { + hideKeyboard() + } + .onLongPressGesture(minimumDuration: .infinity, perform: {}, onPressingChanged: { pressing in + touchInProgress = pressing + }) + } + } + + private func unreadItemIds(_ range: ClosedRange) -> ([ChatItem.ID], Int) { + let im = ItemsModel.shared + var unreadItems: [ChatItem.ID] = [] + var unreadMentions: Int = 0 + + for i in range { + if i < 0 || i >= im.reversedChatItems.count { + break + } + let ci = im.reversedChatItems[i] + if ci.isRcvNew { + unreadItems.append(ci.id) + if ci.meta.userMention { + unreadMentions += 1 + } + } + } + + return (unreadItems, unreadMentions) + } + + private func waitToMarkRead(_ op: @Sendable @escaping () async -> Void) { + markReadTask = Task { + do { + _ = try await Task.sleep(nanoseconds: 600_000000) + if m.chatId == chat.chatInfo.id { + await op() + } + } catch { + // task was cancelled } } } - @ViewBuilder func chatItemView(_ ci: ChatItem, _ range: ClosedRange?, _ prevItem: ChatItem?) -> some View { + private var searchIsNotBlank: Bool { + get { + searchText.count > 0 && !searchText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty + } + } + + @available(iOS 16.0, *) + struct MemberLayout: Layout { + let spacing: Double + let msgWidth: Double + + private func sizes(subviews: Subviews, proposal: ProposedViewSize) -> (CGSize, CGSize) { + assert(subviews.count == 2, "member layout must contain exactly two subviews") + let roleSize = subviews[1].sizeThatFits(proposal) + let memberSize = subviews[0].sizeThatFits( + ProposedViewSize( + width: (proposal.width ?? msgWidth) - roleSize.width, + height: proposal.height + ) + ) + return (memberSize, roleSize) + } + + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) -> CGSize { + let (memberSize, roleSize) = sizes(subviews: subviews, proposal: proposal) + return CGSize( + width: min( + proposal.width ?? msgWidth, + max(msgWidth, roleSize.width + spacing + memberSize.width) + ), + height: max(memberSize.height, roleSize.height) + ) + } + + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) { + let (memberSize, roleSize) = sizes(subviews: subviews, proposal: proposal) + subviews[0].place( + at: CGPoint(x: bounds.minX, y: bounds.midY - memberSize.height / 2), + proposal: ProposedViewSize(memberSize) + ) + subviews[1].place( + at: CGPoint( + x: bounds.minX + max(memberSize.width + spacing, msgWidth - roleSize.width), + y: bounds.midY - roleSize.height / 2 + ), + proposal: ProposedViewSize(roleSize) + ) + } + } + + @ViewBuilder func chatItemListView( + _ range: ClosedRange?, + _ showAvatar: Bool, + _ ci: ChatItem, + _ itemSeparation: ItemSeparation + ) -> some View { + let bottomPadding: Double = itemSeparation.largeGap ? 10 : 2 if case let .groupRcv(member) = ci.chatDir, - case let .group(groupInfo) = chat.chatInfo { - let (prevMember, memCount): (GroupMember?, Int) = - if let range = range { - m.getPrevHiddenMember(member, range) - } else { - (nil, 1) - } - if prevItem == nil || showMemberImage(member, prevItem) || prevMember != nil { + case .group = chat.chatInfo { + if showAvatar { VStack(alignment: .leading, spacing: 4) { if ci.content.showMemberName { - Text(memberNames(member, prevMember, memCount)) - .font(.caption) - .foregroundStyle(.secondary) - .padding(.leading, memberImageSize + 14) - .padding(.top, 7) - } - HStack(alignment: .top, spacing: 8) { - ProfileImage(imageStr: member.memberProfile.image) - .frame(width: memberImageSize, height: memberImageSize) - .onTapGesture { - if chatView.membersLoaded { - selectedMember = m.getGroupMember(member.groupMemberId) - } else { - Task { - await chatView.loadGroupMembers(groupInfo) { - selectedMember = m.getGroupMember(member.groupMemberId) + Group { + let (prevMember, memCount): (GroupMember?, Int) = + if let range = range { + m.getPrevHiddenMember(member, range) + } else { + (nil, 1) + } + if memCount == 1 && member.memberRole > .member { + Group { + if #available(iOS 16.0, *) { + MemberLayout(spacing: 16, msgWidth: msgWidth) { + Text(member.chatViewName) + .lineLimit(1) + Text(member.memberRole.text) + .fontWeight(.semibold) + .lineLimit(1) + .padding(.trailing, 8) + } + } else { + HStack(spacing: 16) { + Text(member.chatViewName) + .lineLimit(1) + Text(member.memberRole.text) + .fontWeight(.semibold) + .lineLimit(1) + .layoutPriority(1) } } } + .frame( + maxWidth: maxWidth, + alignment: chatItem.chatDir.sent ? .trailing : .leading + ) + } else { + Text(memberNames(member, prevMember, memCount)) + .lineLimit(2) } - .appSheet(item: $selectedMember) { member in - GroupMemberInfoView(groupInfo: groupInfo, groupMember: member, navigation: true) - } - chatItemWithMenu(ci, range, maxWidth) + } + .font(.caption) + .foregroundStyle(.secondary) + .padding(.leading, memberImageSize + 14 + (selectedChatItems != nil && ci.canBeDeletedForSelf ? 12 + 24 : 0)) + .padding(.top, 3) // this is in addition to message sequence gap + } + HStack(alignment: .center, spacing: 0) { + if selectedChatItems != nil && ci.canBeDeletedForSelf { + SelectedChatItem(ciId: ci.id, selectedChatItems: $selectedChatItems) + .padding(.trailing, 12) + } + HStack(alignment: .top, spacing: 10) { + MemberProfileImage(member, size: memberImageSize, backgroundColor: theme.colors.background) + .simultaneousGesture(TapGesture().onEnded { + if let mem = m.getGroupMember(member.groupMemberId) { + selectedMember = mem + } else { + let mem = GMember.init(member) + m.groupMembers.append(mem) + m.groupMembersIndexes[member.groupMemberId] = m.groupMembers.count - 1 + selectedMember = mem + } + }) + chatItemWithMenu(ci, range, maxWidth, itemSeparation) + .onPreferenceChange(DetermineWidth.Key.self) { msgWidth = $0 } + } } } - .padding(.top, 5) + .padding(.bottom, bottomPadding) .padding(.trailing) .padding(.leading, 12) } else { - chatItemWithMenu(ci, range, maxWidth) - .padding(.top, 5) - .padding(.trailing) - .padding(.leading, memberImageSize + 8 + 12) + HStack(alignment: .center, spacing: 0) { + if selectedChatItems != nil && ci.canBeDeletedForSelf { + SelectedChatItem(ciId: ci.id, selectedChatItems: $selectedChatItems) + .padding(.leading, 12) + } + chatItemWithMenu(ci, range, maxWidth, itemSeparation) + .padding(.trailing) + .padding(.leading, 10 + memberImageSize + 12) + } + .padding(.bottom, bottomPadding) } } else { - chatItemWithMenu(ci, range, maxWidth) - .padding(.horizontal) - .padding(.top, 5) + HStack(alignment: .center, spacing: 0) { + if selectedChatItems != nil && ci.canBeDeletedForSelf { + if chat.chatInfo.chatType == .group { + SelectedChatItem(ciId: ci.id, selectedChatItems: $selectedChatItems) + .padding(.leading, 12) + } else { + SelectedChatItem(ciId: ci.id, selectedChatItems: $selectedChatItems) + .padding(.leading) + } + } + chatItemWithMenu(ci, range, maxWidth, itemSeparation) + .padding(.horizontal) + } + .padding(.bottom, bottomPadding) } } @@ -627,27 +1537,29 @@ struct ChatView: View { } } - @ViewBuilder func chatItemWithMenu(_ ci: ChatItem, _ range: ClosedRange?, _ maxWidth: CGFloat) -> some View { + func chatItemWithMenu(_ ci: ChatItem, _ range: ClosedRange?, _ maxWidth: CGFloat, _ itemSeparation: ItemSeparation) -> some View { let alignment: Alignment = ci.chatDir.sent ? .trailing : .leading - let uiMenu: Binding = Binding( - get: { UIMenu(title: "", children: menu(ci, range, live: composeState.liveMessage != nil)) }, - set: { _ in } - ) - - VStack(alignment: alignment.horizontal, spacing: 3) { - ChatItemView( - chat: chat, - chatItem: ci, - maxWidth: maxWidth, - scrollProxy: chatView.scrollProxy, - revealed: $revealed, - allowMenu: $allowMenu, - audioPlayer: $audioPlayer, - playbackState: $playbackState, - playbackTime: $playbackTime - ) - .uiKitContextMenu(maxWidth: maxWidth, menu: uiMenu, allowMenu: $allowMenu) - .accessibilityLabel("") + return VStack(alignment: alignment.horizontal, spacing: 3) { + HStack { + if ci.chatDir.sent { + goToItemButton(true) + } + ChatItemView( + chat: chat, + chatItem: ci, + scrollToItemId: scrollToItemId, + maxWidth: maxWidth, + allowMenu: $allowMenu + ) + .environment(\.revealed, revealed) + .environment(\.showTimestamp, itemSeparation.timestamp) + .modifier(ChatItemClipped(ci, tailVisible: itemSeparation.largeGap && (ci.meta.itemDeleted == nil || revealed))) + .contextMenu { menu(ci, range, live: composeState.liveMessage != nil) } + .accessibilityLabel("") + if !ci.chatDir.sent { + goToItemButton(false) + } + } if ci.content.msgContent != nil && (ci.meta.itemDeleted == nil || revealed) && ci.reactions.count > 0 { chatItemReactions(ci) .padding(.bottom, 4) @@ -655,33 +1567,41 @@ struct ChatView: View { } .confirmationDialog("Delete message?", isPresented: $showDeleteMessage, titleVisibility: .visible) { Button("Delete for me", role: .destructive) { - deleteMessage(.cidmInternal) + deleteMessage(.cidmInternal, moderate: false) } - if let di = deletingItem, di.meta.editable && !di.localNote { - Button(broadcastDeleteButtonText, role: .destructive) { - deleteMessage(.cidmBroadcast) + if let di = deletingItem, di.meta.deletable && !di.localNote && !di.isReport { + Button(broadcastDeleteButtonText(chat), role: .destructive) { + deleteMessage(.cidmBroadcast, moderate: false) } } } .confirmationDialog(deleteMessagesTitle, isPresented: $showDeleteMessages, titleVisibility: .visible) { Button("Delete for me", role: .destructive) { - deleteMessages() + deleteMessages(chat, deletingItems, moderate: false) + } + } + .confirmationDialog(archivingReports?.count == 1 ? "Archive report?" : "Archive \(archivingReports?.count ?? 0) reports?", isPresented: $showArchivingReports, titleVisibility: .visible) { + Button("For me", role: .destructive) { + if let reports = self.archivingReports { + archiveReports(chat.chatInfo, reports.sorted(), false) + self.archivingReports = [] + } + } + if case let ChatInfo.group(groupInfo) = chat.chatInfo, groupInfo.membership.memberActive { + Button("For all moderators", role: .destructive) { + if let reports = self.archivingReports { + archiveReports(chat.chatInfo, reports.sorted(), true) + self.archivingReports = [] + } + } } } .frame(maxWidth: maxWidth, maxHeight: .infinity, alignment: alignment) .frame(minWidth: 0, maxWidth: .infinity, alignment: alignment) - .onDisappear { - if ci.content.msgContent?.isVoice == true { - allowMenu = true - audioPlayer?.stop() - playbackState = .noPlayback - playbackTime = TimeInterval(0) - } - } .sheet(isPresented: $showChatItemInfoSheet, onDismiss: { chatItemInfo = nil }) { - ChatItemInfoView(ci: ci, chatItemInfo: $chatItemInfo) + ChatItemInfoView(ci: ci, userMemberId: chat.chatInfo.groupInfo?.membership.memberId, chatItemInfo: $chatItemInfo) } } @@ -705,147 +1625,201 @@ struct ChatView: View { Text("\(r.totalReacted)") .font(.caption) .fontWeight(r.userReacted ? .bold : .light) - .foregroundColor(r.userReacted ? .accentColor : .secondary) + .foregroundColor(r.userReacted ? theme.colors.primary : theme.colors.secondary) } } .padding(.horizontal, 6) .padding(.vertical, 4) - - if chat.chatInfo.featureEnabled(.reactions) && (ci.allowAddReaction || r.userReacted) { - v.onTapGesture { + .if(chat.chatInfo.featureEnabled(.reactions) && (ci.allowAddReaction || r.userReacted)) { v in + v.simultaneousGesture(TapGesture().onEnded { setReaction(ci, add: !r.userReacted, reaction: r.reaction) + }) + } + switch chat.chatInfo { + case let .group(groupInfo): + v.contextMenu { + ReactionContextMenu( + groupInfo: groupInfo, + itemId: ci.id, + reactionCount: r, + selectedMember: $selectedMember, + profileRadius: profileRadius + ) } - } else { + case let .direct(contact): + v.contextMenu { + contactReactionMenu(contact, r) + } + default: v } } } } - private func menu(_ ci: ChatItem, _ range: ClosedRange?, live: Bool) -> [UIMenuElement] { - var menu: [UIMenuElement] = [] - if let mc = ci.content.msgContent, ci.meta.itemDeleted == nil || revealed { - let rs = allReactions(ci) + @ViewBuilder + private func menu(_ ci: ChatItem, _ range: ClosedRange?, live: Bool) -> some View { + if case let .group(gInfo) = chat.chatInfo, ci.isReport, ci.meta.itemDeleted == nil { + if ci.chatDir != .groupSnd, gInfo.membership.memberRole >= .moderator { + archiveReportButton(ci) + } + deleteButton(ci, label: "Delete report") + } else if let mc = ci.content.msgContent, !ci.isReport, ci.meta.itemDeleted == nil || revealed { if chat.chatInfo.featureEnabled(.reactions) && ci.allowAddReaction, - rs.count > 0 { - var rm: UIMenu - if #available(iOS 16, *) { - var children: [UIMenuElement] = Array(rs.prefix(topReactionsCount(rs))) - if let sm = reactionUIMenu(rs) { - children.append(sm) - } - rm = UIMenu(title: "", options: .displayInline, children: children) - rm.preferredElementSize = .small - } else { - rm = reactionUIMenuPreiOS16(rs) - } - menu.append(rm) + availableReactions.count > 0 { + reactionsGroup } if ci.meta.itemDeleted == nil && !ci.isLiveDummy && !live && !ci.localNote { - menu.append(replyUIAction(ci)) + replyButton } let fileSource = getLoadedFileSource(ci.file) let fileExists = if let fs = fileSource, FileManager.default.fileExists(atPath: getAppFilePath(fs.filePath).path) { true } else { false } let copyAndShareAllowed = !ci.content.text.isEmpty || (ci.content.msgContent?.isImage == true && fileExists) if copyAndShareAllowed { - menu.append(shareUIAction(ci)) - menu.append(copyUIAction(ci)) + shareButton(ci) + copyButton(ci) } if let fileSource = fileSource, fileExists { if case .image = ci.content.msgContent, let image = getLoadedImage(ci.file) { if image.imageData != nil { - menu.append(saveFileAction(fileSource)) + saveButton(file: fileSource) } else { - menu.append(saveImageAction(image)) + saveButton(image: image) } } else { - menu.append(saveFileAction(fileSource)) + saveButton(file: fileSource) } + } else if let file = ci.file, case .rcvInvitation = file.fileStatus, fileSizeValid(file) { + downloadButton(file: file) } if ci.meta.editable && !mc.isVoice && !live { - menu.append(editAction(ci)) + editButton(chatItem) + } + if ci.meta.itemDeleted == nil + && (ci.file == nil || (fileSource != nil && fileExists)) + && !ci.isLiveDummy && !live { + forwardButton } if !ci.isLiveDummy { - menu.append(viewInfoUIAction(ci)) + viewInfoButton(ci) } if revealed { - menu.append(hideUIAction()) + hideButton() } if ci.meta.itemDeleted == nil && !ci.localNote, let file = ci.file, let cancelAction = file.cancelAction { - menu.append(cancelFileUIAction(file.fileId, cancelAction)) + cancelFileButton(file.fileId, cancelAction) } if !live || !ci.meta.isLive { - menu.append(deleteUIAction(ci)) + deleteButton(ci) } - if let (groupInfo, _) = ci.memberToModerate(chat.chatInfo) { - menu.append(moderateUIAction(ci, groupInfo)) + if ci.chatDir != .groupSnd { + if let (groupInfo, _) = ci.memberToModerate(chat.chatInfo) { + moderateButton(ci, groupInfo) + } else if ci.meta.itemDeleted == nil && chat.groupFeatureEnabled(.reports), + case let .group(gInfo) = chat.chatInfo, + gInfo.membership.memberRole == .member + && !live + && composeState.voiceMessageRecordingState == .noRecording { + reportButton(ci) + } } } else if ci.meta.itemDeleted != nil { if revealed { - menu.append(hideUIAction()) + hideButton() } else if !ci.isDeletedContent { - menu.append(revealUIAction()) + revealButton(ci) } else if range != nil { - menu.append(expandUIAction()) + expandButton() } - menu.append(viewInfoUIAction(ci)) - menu.append(deleteUIAction(ci)) + viewInfoButton(ci) + deleteButton(ci) } else if ci.isDeletedContent { - menu.append(viewInfoUIAction(ci)) - menu.append(deleteUIAction(ci)) + viewInfoButton(ci) + deleteButton(ci) } else if ci.mergeCategory != nil && ((range?.count ?? 0) > 1 || revealed) { - menu.append(revealed ? shrinkUIAction() : expandUIAction()) + if revealed { shrinkButton() } else { expandButton() } + deleteButton(ci) + } else if ci.showLocalDelete { + deleteButton(ci) + } else { + EmptyView() + } + if selectedChatItems == nil && ci.canBeDeletedForSelf { + Divider() + selectButton(ci) } - return menu } - - private func replyUIAction(_ ci: ChatItem) -> UIAction { - UIAction( - title: NSLocalizedString("Reply", comment: "chat item action"), - image: UIImage(systemName: "arrowshape.turn.up.left") - ) { _ in + + var replyButton: Button { + Button { withAnimation { if composeState.editing { - composeState = ComposeState(contextItem: .quotedItem(chatItem: ci)) + composeState = ComposeState(contextItem: .quotedItem(chatItem: chatItem)) } else { - composeState = composeState.copy(contextItem: .quotedItem(chatItem: ci)) + composeState = composeState.copy(contextItem: .quotedItem(chatItem: chatItem)) } } + } label: { + Label( + NSLocalizedString("Reply", comment: "chat item action"), + systemImage: "arrowshape.turn.up.left" + ) + } + } + + var forwardButton: Button { + Button { + forwardedChatItems = [chatItem] + } label: { + Label( + NSLocalizedString("Forward", comment: "chat item action"), + systemImage: "arrowshape.turn.up.forward" + ) + } + } + + private var reactionsGroup: some View { + if #available(iOS 16.4, *) { + return ControlGroup { + if availableReactions.count > 4 { + reactions(till: 3) + Menu { + reactions(from: 3) + } label: { + Image(systemName: "ellipsis") + } + } else { reactions() } + }.controlGroupStyle(.compactMenu) + } else { + return Menu { + reactions() + } label: { + Label( + NSLocalizedString("React…", comment: "chat item menu"), + systemImage: "face.smiling" + ) + } } } - private func reactionUIMenuPreiOS16(_ rs: [UIAction]) -> UIMenu { - UIMenu( - title: NSLocalizedString("React…", comment: "chat item menu"), - image: UIImage(systemName: "face.smiling"), - children: rs - ) - } - - @available(iOS 16.0, *) - private func reactionUIMenu(_ rs: [UIAction]) -> UIMenu? { - var children = rs - children.removeFirst(min(rs.count, topReactionsCount(rs))) - if children.count == 0 { return nil } - return UIMenu( - title: "", - image: UIImage(systemName: "ellipsis"), - children: children - ) - } - - private func allReactions(_ ci: ChatItem) -> [UIAction] { - MsgReaction.values.compactMap { r in - ci.reactions.contains(where: { $0.userReacted && $0.reaction == r }) - ? nil - : UIAction(title: r.text) { _ in setReaction(ci, add: true, reaction: r) } + func reactions(from: Int? = nil, till: Int? = nil) -> some View { + ForEach(availableReactions[(from ?? 0)..<(till ?? availableReactions.count)]) { reaction in + Button(reaction.text) { + setReaction(chatItem, add: true, reaction: reaction) + } } } - private func topReactionsCount(_ rs: [UIAction]) -> Int { - rs.count > 4 ? 3 : 4 + /// Reactions, which has not been used yet + private var availableReactions: Array { + MsgReaction.values + .filter { reaction in + !chatItem.reactions.contains { + $0.userReacted && $0.reaction == reaction + } + } } private func setReaction(_ ci: ChatItem, add: Bool, reaction: MsgReaction) { @@ -868,24 +1842,23 @@ struct ChatView: View { } } - private func shareUIAction(_ ci: ChatItem) -> UIAction { - UIAction( - title: NSLocalizedString("Share", comment: "chat item action"), - image: UIImage(systemName: "square.and.arrow.up") - ) { _ in + private func shareButton(_ ci: ChatItem) -> Button { + Button { var shareItems: [Any] = [ci.content.text] if case .image = ci.content.msgContent, let image = getLoadedImage(ci.file) { shareItems.append(image) } showShareSheet(items: shareItems) + } label: { + Label( + NSLocalizedString("Share", comment: "chat item action"), + systemImage: "square.and.arrow.up" + ) } } - - private func copyUIAction(_ ci: ChatItem) -> UIAction { - UIAction( - title: NSLocalizedString("Copy", comment: "chat item action"), - image: UIImage(systemName: "doc.on.doc") - ) { _ in + + private func copyButton(_ ci: ChatItem) -> Button { + Button { if case let .image(text, _) = ci.content.msgContent, text == "", let image = getLoadedImage(ci.file) { @@ -893,43 +1866,79 @@ struct ChatView: View { } else { UIPasteboard.general.string = ci.content.text } - } - } - - private func saveImageAction(_ image: UIImage) -> UIAction { - UIAction( - title: NSLocalizedString("Save", comment: "chat item action"), - image: UIImage(systemName: "square.and.arrow.down") - ) { _ in - UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil) - } - } - - private func saveFileAction(_ fileSource: CryptoFile) -> UIAction { - UIAction( - title: NSLocalizedString("Save", comment: "chat item action"), - image: UIImage(systemName: fileSource.cryptoArgs == nil ? "square.and.arrow.down" : "lock.open") - ) { _ in - saveCryptoFile(fileSource) - } - } - - private func editAction(_ ci: ChatItem) -> UIAction { - UIAction( - title: NSLocalizedString("Edit", comment: "chat item action"), - image: UIImage(systemName: "square.and.pencil") - ) { _ in - withAnimation { - composeState = ComposeState(editingItem: ci) - } + } label: { + Label("Copy", systemImage: "doc.on.doc") } } - private func viewInfoUIAction(_ ci: ChatItem) -> UIAction { - UIAction( - title: NSLocalizedString("Info", comment: "chat item action"), - image: UIImage(systemName: "info.circle") - ) { _ in + func saveButton(image: UIImage) -> Button { + Button { + UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil) + } label: { + Label( + NSLocalizedString("Save", comment: "chat item action"), + systemImage: "square.and.arrow.down" + ) + } + } + + func saveButton(file: CryptoFile) -> Button { + Button { + saveCryptoFile(file) + } label: { + Label( + NSLocalizedString("Save", comment: "chat item action"), + systemImage: file.cryptoArgs == nil ? "square.and.arrow.down" : "lock.open" + ) + } + } + + func downloadButton(file: CIFile) -> Button { + Button { + Task { + logger.debug("ChatView downloadFileAction, in Task") + if let user = m.currentUser { + await receiveFile(user: user, fileId: file.fileId) + } + } + } label: { + Label( + NSLocalizedString("Download", comment: "chat item action"), + systemImage: "arrow.down.doc" + ) + } + } + + private func editButton(_ ci: ChatItem) -> Button { + Button { + withAnimation { + composeState = ComposeState(editingItem: ci) + } + } label: { + Label( + NSLocalizedString("Edit", comment: "chat item action"), + systemImage: "square.and.pencil" + ) + } + } + + private func selectButton(_ ci: ChatItem) -> Button { + Button { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + withAnimation { + selectUnselectChatItem(select: true, ci) + } + } + } label: { + Label( + NSLocalizedString("Select", comment: "chat item action"), + systemImage: "checkmark.circle" + ) + } + } + + private func viewInfoButton(_ ci: ChatItem) -> Button { + Button { Task { do { let cInfo = chat.chatInfo @@ -938,22 +1947,23 @@ struct ChatView: View { chatItemInfo = ciInfo } if case let .group(gInfo) = chat.chatInfo { - await chatView.loadGroupMembers(gInfo) + await m.loadGroupMembers(gInfo) } } catch let error { logger.error("apiGetChatItemInfo error: \(responseError(error))") } await MainActor.run { showChatItemInfoSheet = true } } + } label: { + Label( + NSLocalizedString("Info", comment: "chat item action"), + systemImage: "info.circle" + ) } } - private func cancelFileUIAction(_ fileId: Int64, _ cancelAction: CancelAction) -> UIAction { - return UIAction( - title: cancelAction.uiAction, - image: UIImage(systemName: "xmark"), - attributes: [.destructive] - ) { _ in + private func cancelFileButton(_ fileId: Int64, _ cancelAction: CancelAction) -> Button { + Button { AlertManager.shared.showAlert(Alert( title: Text(cancelAction.alert.title), message: Text(cancelAction.alert.message), @@ -966,34 +1976,37 @@ struct ChatView: View { }, secondaryButton: .cancel() )) + } label: { + Label( + cancelAction.uiAction, + systemImage: "xmark" + ) } } - private func hideUIAction() -> UIAction { - UIAction( - title: NSLocalizedString("Hide", comment: "chat item action"), - image: UIImage(systemName: "eye.slash") - ) { _ in - withAnimation { - revealed = false + private func hideButton() -> Button { + Button { + withConditionalAnimation { + reveal(false) } + } label: { + Label( + NSLocalizedString("Hide", comment: "chat item action"), + systemImage: "eye.slash" + ) } } - - private func deleteUIAction(_ ci: ChatItem) -> UIAction { - UIAction( - title: NSLocalizedString("Delete", comment: "chat item action"), - image: UIImage(systemName: "trash"), - attributes: [.destructive] - ) { _ in - if !revealed && ci.meta.itemDeleted != nil, + + private func deleteButton(_ ci: ChatItem, label: LocalizedStringKey = "Delete") -> Button { + Button(role: .destructive) { + if !revealed, let currIndex = m.getChatItemIndex(ci), let ciCategory = ci.mergeCategory { let (prevHidden, _) = m.getPrevShownChatItem(currIndex, ciCategory) if let range = itemsRange(currIndex, prevHidden) { var itemIds: [Int64] = [] for i in range { - itemIds.append(m.reversedChatItems[i].id) + itemIds.append(ItemsModel.shared.reversedChatItems[i].id) } showDeleteMessages = true deletingItems = itemIds @@ -1005,6 +2018,8 @@ struct ChatView: View { showDeleteMessage = true deletingItem = ci } + } label: { + Label(label, systemImage: "trash") } } @@ -1018,63 +2033,106 @@ struct ChatView: View { } } - private func moderateUIAction(_ ci: ChatItem, _ groupInfo: GroupInfo) -> UIAction { - UIAction( - title: NSLocalizedString("Moderate", comment: "chat item action"), - image: UIImage(systemName: "flag"), - attributes: [.destructive] - ) { _ in + private func moderateButton(_ ci: ChatItem, _ groupInfo: GroupInfo) -> Button { + Button(role: .destructive) { AlertManager.shared.showAlert(Alert( title: Text("Delete member message?"), message: Text( - groupInfo.fullGroupPreferences.fullDelete.on - ? "The message will be deleted for all members." - : "The message will be marked as moderated for all members." - ), + groupInfo.fullGroupPreferences.fullDelete.on + ? "The message will be deleted for all members." + : "The message will be marked as moderated for all members." + ), primaryButton: .destructive(Text("Delete")) { deletingItem = ci - deleteMessage(.cidmBroadcast) + deleteMessage(.cidmBroadcast, moderate: true) }, secondaryButton: .cancel() )) + } label: { + Label( + NSLocalizedString("Moderate", comment: "chat item action"), + systemImage: "flag" + ) } } - private func revealUIAction() -> UIAction { - UIAction( - title: NSLocalizedString("Reveal", comment: "chat item action"), - image: UIImage(systemName: "eye") - ) { _ in - withAnimation { - revealed = true + private func archiveReportButton(_ cItem: ChatItem) -> Button { + Button { + archivingReports = [cItem.id] + showArchivingReports = true + } label: { + Label("Archive report", systemImage: "archivebox") + } + } + + private func revealButton(_ ci: ChatItem) -> Button { + Button { + withConditionalAnimation { + reveal(true) } + } label: { + Label( + NSLocalizedString("Reveal", comment: "chat item action"), + systemImage: "eye" + ) } } - private func expandUIAction() -> UIAction { - UIAction( - title: NSLocalizedString("Expand", comment: "chat item action"), - image: UIImage(systemName: "arrow.up.and.line.horizontal.and.arrow.down") - ) { _ in - withAnimation { - revealed = true + private func expandButton() -> Button { + Button { + withConditionalAnimation { + reveal(true) } + } label: { + Label( + NSLocalizedString("Expand", comment: "chat item action"), + systemImage: "arrow.up.and.line.horizontal.and.arrow.down" + ) } } - private func shrinkUIAction() -> UIAction { - UIAction( - title: NSLocalizedString("Hide", comment: "chat item action"), - image: UIImage(systemName: "arrow.down.and.line.horizontal.and.arrow.up") - ) { _ in - withAnimation { - revealed = false + private func shrinkButton() -> Button { + Button { + withConditionalAnimation { + reveal(false) } + } label: { + Label ( + NSLocalizedString("Hide", comment: "chat item action"), + systemImage: "arrow.down.and.line.horizontal.and.arrow.up" + ) } } - private var broadcastDeleteButtonText: LocalizedStringKey { - chat.chatInfo.featureEnabled(.fullDelete) ? "Delete for everyone" : "Mark deleted for everyone" + private func reportButton(_ ci: ChatItem) -> Button { + Button(role: .destructive) { + var buttons: [ActionSheet.Button] = ReportReason.supportedReasons.map { reason in + .default(Text(reason.text)) { + withAnimation { + if composeState.editing { + composeState = ComposeState(preview: .noPreview, contextItem: .reportedItem(chatItem: chatItem, reason: reason)) + } else { + composeState = composeState.copy(preview: .noPreview, contextItem: .reportedItem(chatItem: chatItem, reason: reason)) + } + } + } + } + + buttons.append(.cancel()) + + actionSheet = SomeActionSheet( + actionSheet: ActionSheet( + title: Text("Report reason?"), + buttons: buttons + ), + id: "reportChatMessage" + ) + } label: { + Label ( + NSLocalizedString("Report", comment: "chat item action"), + systemImage: "flag" + ) + } } var deleteMessagesTitle: LocalizedStringKey { @@ -1082,101 +2140,351 @@ struct ChatView: View { return n == 1 ? "Delete message?" : "Delete \(n) messages?" } - private func deleteMessages() { - let itemIds = deletingItems - if itemIds.count > 0 { - let chatInfo = chat.chatInfo - Task { - var deletedItems: [ChatItem] = [] - for itemId in itemIds { - do { - let (di, _) = try await apiDeleteChatItem( - type: chatInfo.chatType, - id: chatInfo.apiId, - itemId: itemId, - mode: .cidmInternal - ) - deletedItems.append(di) - } catch { - logger.error("ChatView.deleteMessage error: \(error.localizedDescription)") - } - } - await MainActor.run { - for di in deletedItems { - m.removeChatItem(chatInfo, di) - } + private func selectUnselectChatItem(select: Bool, _ ci: ChatItem) { + selectedChatItems = selectedChatItems ?? [] + var itemIds: [Int64] = [] + if !revealed, + let currIndex = m.getChatItemIndex(ci), + let ciCategory = ci.mergeCategory { + let (prevHidden, _) = m.getPrevShownChatItem(currIndex, ciCategory) + if let range = itemsRange(currIndex, prevHidden) { + for i in range { + itemIds.append(ItemsModel.shared.reversedChatItems[i].id) } + } else { + itemIds.append(ci.id) } + } else { + itemIds.append(ci.id) + } + if select { + if let sel = selectedChatItems { + selectedChatItems = sel.union(itemIds) + } + } else { + itemIds.forEach { selectedChatItems?.remove($0) } } } - private func deleteMessage(_ mode: CIDeleteMode) { + private func deleteMessage(_ mode: CIDeleteMode, moderate: Bool) { logger.debug("ChatView deleteMessage") Task { logger.debug("ChatView deleteMessage: in Task") do { if let di = deletingItem { - var deletedItem: ChatItem - var toItem: ChatItem? - if case .cidmBroadcast = mode, - let (groupInfo, groupMember) = di.memberToModerate(chat.chatInfo) { - (deletedItem, toItem) = try await apiDeleteMemberChatItem( + let r = if case .cidmBroadcast = mode, + moderate, + let (groupInfo, _) = di.memberToModerate(chat.chatInfo) { + try await apiDeleteMemberChatItems( groupId: groupInfo.apiId, - groupMemberId: groupMember.groupMemberId, - itemId: di.id + itemIds: [di.id] ) } else { - (deletedItem, toItem) = try await apiDeleteChatItem( + try await apiDeleteChatItems( type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, - itemId: di.id, + itemIds: [di.id], mode: mode ) } - DispatchQueue.main.async { - deletingItem = nil - if let toItem = toItem { - _ = m.upsertChatItem(chat.chatInfo, toItem) - } else { - m.removeChatItem(chat.chatInfo, deletedItem) + if let itemDeletion = r.first { + await MainActor.run { + deletingItem = nil + if let toItem = itemDeletion.toChatItem { + _ = m.upsertChatItem(chat.chatInfo, toItem.chatItem) + } else { + m.removeChatItem(chat.chatInfo, itemDeletion.deletedChatItem.chatItem) + } + let deletedItem = itemDeletion.deletedChatItem.chatItem + if deletedItem.isActiveReport { + m.decreaseGroupReportsCounter(chat.chatInfo.id) + } } } } } catch { - logger.error("ChatView.deleteMessage error: \(error.localizedDescription)") + logger.error("ChatView.deleteMessage error: \(error)") } } } + + @ViewBuilder private func contactReactionMenu(_ contact: Contact, _ r: CIReactionCount) -> some View { + if !r.userReacted || r.totalReacted > 1 { + Button { showChatInfoSheet = true } label: { + profileMenuItem(Text(contact.displayName), contact.image, radius: profileRadius) + } + } + if r.userReacted { + Button {} label: { + profileMenuItem(Text("you"), m.currentUser?.profile.image, radius: profileRadius) + } + .disabled(true) + } + } + + func goToItemInnerButton(_ alignStart: Bool, _ image: String, touchInProgress: Bool, _ onClick: @escaping () -> Void) -> some View { + Image(systemName: image) + .resizable() + .frame(width: 13, height: 13) + .padding([alignStart ? .trailing : .leading], 10) + .tint(theme.colors.secondary.opacity(touchInProgress ? 1.0 : 0.4)) + .simultaneousGesture(TapGesture().onEnded(onClick)) + } + + @ViewBuilder + func goToItemButton(_ alignStart: Bool) -> some View { + let chatTypeApiIdMsgId = chatItem.meta.itemForwarded?.chatTypeApiIdMsgId + if searchIsNotBlank { + goToItemInnerButton(alignStart, "magnifyingglass", touchInProgress: touchInProgress) { + closeKeyboardAndRun { + ItemsModel.shared.loadOpenChatNoWait(chat.id, chatItem.id) + } + } + } else if let chatTypeApiIdMsgId { + goToItemInnerButton(alignStart, "arrow.right", touchInProgress: touchInProgress) { + closeKeyboardAndRun { + let (chatType, apiId, msgId) = chatTypeApiIdMsgId + ItemsModel.shared.loadOpenChatNoWait("\(chatType.rawValue)\(apiId)", msgId) + } + } + } + } + + private struct SelectedChatItem: View { + @EnvironmentObject var theme: AppTheme + var ciId: Int64 + @Binding var selectedChatItems: Set? + @State var checked: Bool = false + var body: some View { + Image(systemName: checked ? "checkmark.circle.fill" : "circle") + .resizable() + .foregroundColor(checked ? theme.colors.primary : Color(uiColor: .tertiaryLabel)) + .frame(width: 24, height: 24) + .onAppear { + checked = selectedChatItems?.contains(ciId) == true + } + .onChange(of: selectedChatItems) { selected in + checked = selected?.contains(ciId) == true + } + } + } + } +} + +private func broadcastDeleteButtonText(_ chat: Chat) -> LocalizedStringKey { + chat.chatInfo.featureEnabled(.fullDelete) ? "Delete for everyone" : "Mark deleted for everyone" +} + +private func deleteMessages(_ chat: Chat, _ deletingItems: [Int64], _ mode: CIDeleteMode = .cidmInternal, moderate: Bool, _ onSuccess: @escaping () async -> Void = {}) { + let itemIds = deletingItems + if itemIds.count > 0 { + let chatInfo = chat.chatInfo + Task { + do { + let deletedItems = if case .cidmBroadcast = mode, + moderate, + case .group = chat.chatInfo { + try await apiDeleteMemberChatItems( + groupId: chatInfo.apiId, + itemIds: itemIds + ) + } else { + try await apiDeleteChatItems( + type: chatInfo.chatType, + id: chatInfo.apiId, + itemIds: itemIds, + mode: mode + ) + } + + await MainActor.run { + for di in deletedItems { + if let toItem = di.toChatItem { + _ = ChatModel.shared.upsertChatItem(chat.chatInfo, toItem.chatItem) + } else { + ChatModel.shared.removeChatItem(chatInfo, di.deletedChatItem.chatItem) + } + let deletedItem = di.deletedChatItem.chatItem + if deletedItem.isActiveReport { + ChatModel.shared.decreaseGroupReportsCounter(chat.chatInfo.id) + } + } + } + await onSuccess() + } catch { + logger.error("ChatView.deleteMessages error: \(error.localizedDescription)") + } + } + } +} + +func archiveReports(_ chatInfo: ChatInfo, _ itemIds: [Int64], _ forAll: Bool, _ onSuccess: @escaping () async -> Void = {}) { + if itemIds.count > 0 { + Task { + do { + let deleted = try await apiDeleteReceivedReports( + groupId: chatInfo.apiId, + itemIds: itemIds, + mode: forAll ? CIDeleteMode.cidmBroadcast : CIDeleteMode.cidmInternalMark + ) + + await MainActor.run { + for di in deleted { + if let toItem = di.toChatItem { + _ = ChatModel.shared.upsertChatItem(chatInfo, toItem.chatItem) + } else { + ChatModel.shared.removeChatItem(chatInfo, di.deletedChatItem.chatItem) + } + let deletedItem = di.deletedChatItem.chatItem + if deletedItem.isActiveReport { + ChatModel.shared.decreaseGroupReportsCounter(chatInfo.id) + } + } + } + await onSuccess() + } catch { + logger.error("ChatView.archiveReports error: \(error.localizedDescription)") + } + } + } +} + +private func buildTheme() -> AppTheme { + if let cId = ChatModel.shared.chatId, let chat = ChatModel.shared.getChat(cId) { + let perChatTheme = if case let .direct(contact) = chat.chatInfo { + contact.uiThemes?.preferredMode(!AppTheme.shared.colors.isLight) + } else if case let .group(groupInfo) = chat.chatInfo { + groupInfo.uiThemes?.preferredMode(!AppTheme.shared.colors.isLight) + } else { + nil as ThemeModeOverride? + } + let overrides = if perChatTheme != nil { + ThemeManager.currentColors(nil, perChatTheme, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + } else { + nil as ThemeManager.ActiveTheme? + } + let theme = overrides ?? CurrentColors + return AppTheme(name: theme.name, base: theme.base, colors: theme.colors, appColors: theme.appColors, wallpaper: theme.wallpaper) + } else { + return AppTheme.shared + } +} + +struct ReactionContextMenu: View { + @EnvironmentObject var m: ChatModel + let groupInfo: GroupInfo + var itemId: Int64 + var reactionCount: CIReactionCount + @Binding var selectedMember: GMember? + var profileRadius: CGFloat + @State private var memberReactions: [MemberReaction] = [] + + var body: some View { + groupMemberReactionList() + .task { + await loadChatItemReaction() + } + } + + @ViewBuilder private func groupMemberReactionList() -> some View { + if memberReactions.isEmpty { + ForEach(Array(repeating: 0, count: reactionCount.totalReacted), id: \.self) { _ in + textSpace + } + } else { + ForEach(memberReactions, id: \.groupMember.groupMemberId) { mr in + let mem = mr.groupMember + let userMember = mem.groupMemberId == groupInfo.membership.groupMemberId + Button { + if let member = m.getGroupMember(mem.groupMemberId) { + selectedMember = member + } else { + let member = GMember.init(mem) + m.groupMembers.append(member) + m.groupMembersIndexes[member.groupMemberId] = m.groupMembers.count - 1 + selectedMember = member + } + } label: { + profileMenuItem(Text(mem.displayName), mem.image, radius: profileRadius) + } + .disabled(userMember) + } + } } - private func scrollToBottom(_ proxy: ScrollViewProxy) { - if let ci = chatModel.reversedChatItems.first { - withAnimation { proxy.scrollTo(ci.viewId, anchor: .top) } - } - } - - private func scrollUp(_ proxy: ScrollViewProxy) { - if let ci = chatModel.topItemInView(itemsInView: itemsInView) { - withAnimation { proxy.scrollTo(ci.viewId, anchor: .top) } + private func loadChatItemReaction() async { + do { + let memberReactions = try await apiGetReactionMembers( + groupId: groupInfo.groupId, + itemId: itemId, + reaction: reactionCount.reaction + ) + await MainActor.run { + self.memberReactions = memberReactions + } + } catch let error { + logger.error("apiGetReactionMembers error: \(responseError(error))") } } } -@ViewBuilder func toggleNtfsButton(_ chat: Chat) -> some View { - Button { - toggleNotifications(chat, enableNtfs: !chat.chatInfo.ntfsEnabled) - } label: { - if chat.chatInfo.ntfsEnabled { - Label("Mute", systemImage: "speaker.slash") +func profileMenuItem(_ nameText: Text, _ image: String?, radius: CGFloat) -> some View { + HStack { + nameText + if let image, let img = imageFromBase64(image) { + Image(uiImage: maskToCustomShape(img, size: 30, radius: radius)) } else { - Label("Unmute", systemImage: "speaker.wave.2") + Image(systemName: "person.crop.circle") } } } -func toggleNotifications(_ chat: Chat, enableNtfs: Bool) { +func maskToCustomShape(_ image: UIImage, size: CGFloat, radius: CGFloat) -> UIImage { + let path = Path { path in + if radius >= 50 { + path.addEllipse(in: CGRect(x: 0, y: 0, width: size, height: size)) + } else if radius <= 0 { + path.addRect(CGRect(x: 0, y: 0, width: size, height: size)) + } else { + let cornerRadius = size * CGFloat(radius) / 100 + path.addRoundedRect( + in: CGRect(x: 0, y: 0, width: size, height: size), + cornerSize: CGSize(width: cornerRadius, height: cornerRadius), + style: .continuous + ) + } + } + + return UIGraphicsImageRenderer(size: CGSize(width: size, height: size)).image { context in + context.cgContext.addPath(path.cgPath) + context.cgContext.clip() + let scale = size / max(image.size.width, image.size.height) + let imageSize = CGSize(width: image.size.width * scale, height: image.size.height * scale) + let imageOrigin = CGPoint( + x: (size - imageSize.width) / 2, + y: (size - imageSize.height) / 2 + ) + image.draw(in: CGRect(origin: imageOrigin, size: imageSize)) + } +} + +struct ToggleNtfsButton: View { + @ObservedObject var chat: Chat + + var body: some View { + if let nextMode = chat.chatInfo.nextNtfMode { + Button { + toggleNotifications(chat, enableNtfs: nextMode) + } label: { + Label(nextMode.text(mentions: chat.chatInfo.hasMentions), systemImage: nextMode.icon) + } + } + } +} + +func toggleNotifications(_ chat: Chat, enableNtfs: MsgFilter) { var chatSettings = chat.chatInfo.chatSettings ?? ChatSettings.defaults - chatSettings.enableNtfs = enableNtfs ? .all : .none + chatSettings.enableNtfs = enableNtfs updateChatSettings(chat, chatSettings: chatSettings) } @@ -1191,6 +2499,9 @@ func updateChatSettings(_ chat: Chat, chatSettings: ChatSettings) { do { try await apiSetChatSettings(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, chatSettings: chatSettings) await MainActor.run { + let wasFavorite = chat.chatInfo.chatSettings?.favorite ?? false + ChatTagsModel.shared.updateChatFavorite(favorite: chatSettings.favorite, wasFavorite: wasFavorite) + let wasUnread = chat.unreadTag switch chat.chatInfo { case var .direct(contact): contact.chatSettings = chatSettings @@ -1200,6 +2511,7 @@ func updateChatSettings(_ chat: Chat, chatSettings: ChatSettings) { ChatModel.shared.updateGroup(groupInfo) default: () } + ChatTagsModel.shared.updateChatTagRead(chat, wasUnread: wasUnread) } } catch let error { logger.error("apiSetChatSettings error \(responseError(error))") @@ -1211,7 +2523,7 @@ struct ChatView_Previews: PreviewProvider { static var previews: some View { let chatModel = ChatModel() chatModel.chatId = "@1" - chatModel.reversedChatItems = [ + ItemsModel.shared.reversedChatItems = [ ChatItem.getSample(1, .directSnd, .now, "hello"), ChatItem.getSample(2, .directRcv, .now, "hi"), ChatItem.getSample(3, .directRcv, .now, "hi there"), diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeFileView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeFileView.swift index bc6a96aa86..1ec46816f5 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeFileView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeFileView.swift @@ -9,7 +9,7 @@ import SwiftUI struct ComposeFileView: View { - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme let fileName: String let cancelFile: (() -> Void) let cancelEnabled: Bool @@ -32,9 +32,8 @@ struct ComposeFileView: View { } .padding(.vertical, 1) .padding(.trailing, 12) - .frame(height: 50) - .background(colorScheme == .light ? sentColorLight : sentColorDark) + .frame(height: 54) + .background(theme.appColors.sentMessage) .frame(maxWidth: .infinity) - .padding(.top, 8) } } diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeImageView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeImageView.swift index edaf86912c..14026d79d1 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeImageView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeImageView.swift @@ -10,7 +10,7 @@ import SwiftUI import SimpleXChat struct ComposeImageView: View { - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme let images: [String] let cancelImage: (() -> Void) let cancelEnabled: Bool @@ -18,10 +18,7 @@ struct ComposeImageView: View { var body: some View { HStack(alignment: .center, spacing: 8) { let imgs: [UIImage] = images.compactMap { image in - if let data = Data(base64Encoded: dropImagePrefix(image)) { - return UIImage(data: data) - } - return nil + imageFromBase64(image) } if imgs.count == 0 { ProgressView() @@ -48,9 +45,9 @@ struct ComposeImageView: View { } .padding(.vertical, 1) .padding(.trailing, 12) - .background(colorScheme == .light ? sentColorLight : sentColorDark) + .background(theme.appColors.sentMessage) + .frame(minHeight: 54) .frame(maxWidth: .infinity) - .padding(.top, 8) } } diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift index cc779851ab..e629a984df 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift @@ -10,44 +10,15 @@ import SwiftUI import LinkPresentation import SimpleXChat -func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) { - logger.debug("getLinkMetadata: fetching URL preview") - LPMetadataProvider().startFetchingMetadata(for: url){ metadata, error in - if let e = error { - logger.error("Error retrieving link metadata: \(e.localizedDescription)") - } - if let metadata = metadata, - let imageProvider = metadata.imageProvider, - imageProvider.canLoadObject(ofClass: UIImage.self) { - imageProvider.loadObject(ofClass: UIImage.self){ object, error in - var linkPreview: LinkPreview? = nil - if let error = error { - logger.error("Couldn't load image preview from link metadata with error: \(error.localizedDescription)") - } else { - if let image = object as? UIImage, - let resized = resizeImageToStrSize(image, maxDataSize: 14000), - let title = metadata.title, - let uri = metadata.originalURL { - linkPreview = LinkPreview(uri: uri, title: title, image: resized) - } - } - cb(linkPreview) - } - } else { - cb(nil) - } - } -} - struct ComposeLinkView: View { - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme let linkPreview: LinkPreview? var cancelPreview: (() -> Void)? = nil let cancelEnabled: Bool var body: some View { HStack(alignment: .center, spacing: 8) { - if let linkPreview = linkPreview { + if let linkPreview { linkPreviewView(linkPreview) } else { ProgressView() @@ -62,15 +33,14 @@ struct ComposeLinkView: View { } .padding(.vertical, 1) .padding(.trailing, 12) - .background(colorScheme == .light ? sentColorLight : sentColorDark) + .background(theme.appColors.sentMessage) + .frame(minHeight: 54) .frame(maxWidth: .infinity) - .padding(.top, 8) } private func linkPreviewView(_ linkPreview: LinkPreview) -> some View { HStack(alignment: .center, spacing: 8) { - if let data = Data(base64Encoded: dropImagePrefix(linkPreview.image)), - let uiImage = UIImage(data: data) { + if let uiImage = imageFromBase64(linkPreview.image) { Image(uiImage: uiImage) .resizable() .aspectRatio(contentMode: .fit) @@ -82,10 +52,10 @@ struct ComposeLinkView: View { Text(linkPreview.uri.absoluteString) .font(.caption) .lineLimit(1) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } .padding(.vertical, 5) - .frame(maxWidth: .infinity, minHeight: 60, maxHeight: 60) + .frame(maxWidth: .infinity, minHeight: 60) } } } diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift index 604e0a276d..8993de886f 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift @@ -11,6 +11,8 @@ import SimpleXChat import SwiftyGif import PhotosUI +let MAX_NUMBER_OF_MENTIONS = 3 + enum ComposePreview { case noPreview case linkPreview(linkPreview: LinkPreview?) @@ -19,10 +21,12 @@ enum ComposePreview { case filePreview(fileName: String, file: URL) } -enum ComposeContextItem { +enum ComposeContextItem: Equatable { case noContextItem case quotedItem(chatItem: ChatItem) case editingItem(chatItem: ChatItem) + case forwardingItems(chatItems: [ChatItem], fromChatInfo: ChatInfo) + case reportedItem(chatItem: ChatItem, reason: ReportReason) } enum VoiceMessageRecordingState { @@ -37,31 +41,41 @@ struct LiveMessage { var sentMsg: String? } +typealias MentionedMembers = [String: CIMention] + struct ComposeState { var message: String + var parsedMessage: [FormattedText] var liveMessage: LiveMessage? = nil var preview: ComposePreview var contextItem: ComposeContextItem var voiceMessageRecordingState: VoiceMessageRecordingState var inProgress = false var useLinkPreviews: Bool = UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS) + var mentions: MentionedMembers = [:] init( message: String = "", + parsedMessage: [FormattedText] = [], liveMessage: LiveMessage? = nil, preview: ComposePreview = .noPreview, contextItem: ComposeContextItem = .noContextItem, - voiceMessageRecordingState: VoiceMessageRecordingState = .noRecording + voiceMessageRecordingState: VoiceMessageRecordingState = .noRecording, + mentions: MentionedMembers = [:] ) { self.message = message + self.parsedMessage = parsedMessage self.liveMessage = liveMessage self.preview = preview self.contextItem = contextItem self.voiceMessageRecordingState = voiceMessageRecordingState + self.mentions = mentions } init(editingItem: ChatItem) { - self.message = editingItem.content.text + let text = editingItem.content.text + self.message = text + self.parsedMessage = editingItem.formattedText ?? FormattedText.plain(text) self.preview = chatItemPreview(chatItem: editingItem) self.contextItem = .editingItem(chatItem: editingItem) if let emc = editingItem.content.msgContent, @@ -70,24 +84,51 @@ struct ComposeState { } else { self.voiceMessageRecordingState = .noRecording } + self.mentions = editingItem.mentions ?? [:] + } + + init(forwardingItems: [ChatItem], fromChatInfo: ChatInfo) { + self.message = "" + self.parsedMessage = [] + self.preview = .noPreview + self.contextItem = .forwardingItems(chatItems: forwardingItems, fromChatInfo: fromChatInfo) + self.voiceMessageRecordingState = .noRecording } func copy( message: String? = nil, + parsedMessage: [FormattedText]? = nil, liveMessage: LiveMessage? = nil, preview: ComposePreview? = nil, contextItem: ComposeContextItem? = nil, - voiceMessageRecordingState: VoiceMessageRecordingState? = nil + voiceMessageRecordingState: VoiceMessageRecordingState? = nil, + mentions: MentionedMembers? = nil ) -> ComposeState { ComposeState( message: message ?? self.message, + parsedMessage: parsedMessage ?? self.parsedMessage, liveMessage: liveMessage ?? self.liveMessage, preview: preview ?? self.preview, contextItem: contextItem ?? self.contextItem, - voiceMessageRecordingState: voiceMessageRecordingState ?? self.voiceMessageRecordingState + voiceMessageRecordingState: voiceMessageRecordingState ?? self.voiceMessageRecordingState, + mentions: mentions ?? self.mentions ) } - + + func mentionMemberName(_ name: String) -> String { + var n = 0 + var tryName = name + while mentions[tryName] != nil { + n += 1 + tryName = "\(name)_\(n)" + } + return tryName + } + + var memberMentions: [String: Int64] { + self.mentions.compactMapValues { $0.memberRef?.groupMemberId } + } + var editing: Bool { switch contextItem { case .editingItem: return true @@ -102,12 +143,37 @@ struct ComposeState { } } + var forwarding: Bool { + switch contextItem { + case .forwardingItems: return true + default: return false + } + } + + var reporting: Bool { + switch contextItem { + case .reportedItem: return true + default: return false + } + } + + var submittingValidReport: Bool { + switch contextItem { + case let .reportedItem(_, reason): + switch reason { + case .other: return !message.isEmpty + default: return true + } + default: return false + } + } + var sendEnabled: Bool { switch preview { case let .mediaPreviews(media): return !media.isEmpty case .voicePreview: return voiceMessageRecordingState == .finished case .filePreview: return true - default: return !message.isEmpty || liveMessage != nil + default: return !message.isEmpty || forwarding || liveMessage != nil || submittingValidReport } } @@ -152,8 +218,15 @@ struct ComposeState { } } + var manyMediaPreviews: Bool { + switch preview { + case let .mediaPreviews(mediaPreviews): return mediaPreviews.count > 1 + default: return false + } + } + var attachmentDisabled: Bool { - if editing || liveMessage != nil || inProgress { return true } + if editing || forwarding || liveMessage != nil || inProgress || reporting { return true } switch preview { case .noPreview: return false case .linkPreview: return false @@ -161,6 +234,25 @@ struct ComposeState { } } + var attachmentPreview: Bool { + switch preview { + case .noPreview: false + case .linkPreview: false + case let .mediaPreviews(mediaPreviews): !mediaPreviews.isEmpty + case .voicePreview: false + case .filePreview: true + } + } + + var placeholder: String? { + switch contextItem { + case let .reportedItem(_, reason): + return reason.text + default: + return nil + } + } + var empty: Bool { message == "" && noPreview } @@ -229,11 +321,16 @@ enum UploadContent: Equatable { struct ComposeView: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme @ObservedObject var chat: Chat @Binding var composeState: ComposeState @Binding var keyboardVisible: Bool + @Binding var keyboardHiddenDate: Date + @Binding var selectedRange: NSRange + var disabledText: LocalizedStringKey? = nil @State var linkUrl: URL? = nil + @State var hasSimplexLink: Bool = false @State var prevLinkUrl: URL? = nil @State var pendingLinkUrl: URL? = nil @State var cancelledLinks: Set = [] @@ -253,12 +350,34 @@ struct ComposeView: View { // this is a workaround to fire an explicit event in certain cases @State private var stopPlayback: Bool = false - @AppStorage(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true + @UserDefault(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true + @UserDefault(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial var body: some View { VStack(spacing: 0) { + Divider() if chat.chatInfo.contact?.nextSendGrpInv ?? false { ContextInvitingContactMemberView() + Divider() + } + + if case let .reportedItem(_, reason) = composeState.contextItem { + reportReasonView(reason) + Divider() + } + // preference checks should match checks in forwarding list + let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks) + let fileProhibited = composeState.attachmentPreview && !chat.groupFeatureEnabled(.files) + let voiceProhibited = composeState.voicePreview && !chat.chatInfo.featureEnabled(.voice) + if simplexLinkProhibited { + msgNotAllowedView("SimpleX links not allowed", icon: "link") + Divider() + } else if fileProhibited { + msgNotAllowedView("Files and media not allowed", icon: "doc") + Divider() + } else if voiceProhibited { + msgNotAllowedView("Voice messages not allowed", icon: "mic") + Divider() } contextItemView() switch (composeState.editing, composeState.preview) { @@ -273,12 +392,13 @@ struct ComposeView: View { Image(systemName: "paperclip") .resizable() } - .disabled(composeState.attachmentDisabled || !chat.userCanSend || (chat.chatInfo.contact?.nextSendGrpInv ?? false)) + .disabled(composeState.attachmentDisabled || !chat.chatInfo.sendMsgEnabled || (chat.chatInfo.contact?.nextSendGrpInv ?? false)) .frame(width: 25, height: 25) - .padding(.bottom, 12) + .padding(.bottom, 16) .padding(.leading, 12) + .tint(theme.colors.primary) if case let .group(g) = chat.chatInfo, - !g.fullGroupPreferences.files.on { + !g.fullGroupPreferences.files.on(for: g.membership) { b.disabled(true).onTapGesture { AlertManager.shared.showAlertMsg( title: "Files and media prohibited!", @@ -291,6 +411,7 @@ struct ComposeView: View { ZStack(alignment: .leading) { SendMessageView( composeState: $composeState, + selectedRange: $selectedRange, sendMessage: { ttl in sendMessage(ttl: ttl) resetLinkPreview() @@ -303,6 +424,7 @@ struct ComposeView: View { }, nextSendGrpInv: chat.chatInfo.contact?.nextSendGrpInv ?? false, voiceMessageAllowed: chat.chatInfo.featureEnabled(.voice), + disableSendButton: simplexLinkProhibited || fileProhibited || voiceProhibited, showEnableVoiceMessagesAlert: chat.chatInfo.showEnableVoiceMessagesAlert, startVoiceMessageRecording: { Task { @@ -314,40 +436,46 @@ struct ComposeView: View { timedMessageAllowed: chat.chatInfo.featureEnabled(.timedMessages), onMediaAdded: { media in if !media.isEmpty { chosenMedia = media }}, keyboardVisible: $keyboardVisible, + keyboardHiddenDate: $keyboardHiddenDate, sendButtonColor: chat.chatInfo.incognito ? .indigo.opacity(colorScheme == .dark ? 1 : 0.7) - : .accentColor + : theme.colors.primary ) .padding(.trailing, 12) - .background(.background) - .disabled(!chat.userCanSend) + .disabled(!chat.chatInfo.sendMsgEnabled) - if chat.userIsObserver { - Text("you are observer") + if let disabledText { + Text(disabledText) .italic() - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .padding(.horizontal, 12) - .onTapGesture { - AlertManager.shared.showAlertMsg( - title: "You can't send messages!", - message: "Please contact group admin." - ) - } } } } } - .onChange(of: composeState.message) { _ in + .background { + Color.clear + .overlay(ToolbarMaterial.material(toolbarMaterial)) + .ignoresSafeArea(.all, edges: .bottom) + } + .onChange(of: composeState.message) { msg in + let parsedMsg = parseSimpleXMarkdown(msg) + composeState = composeState.copy(parsedMessage: parsedMsg ?? FormattedText.plain(msg)) if composeState.linkPreviewAllowed { - if composeState.message.count > 0 { - showLinkPreview(composeState.message) + if msg.count > 0 { + showLinkPreview(parsedMsg) } else { resetLinkPreview() + hasSimplexLink = false } + } else if msg.count > 0 && !chat.groupFeatureEnabled(.simplexLinks) { + (_, hasSimplexLink) = getSimplexLink(parsedMsg) + } else { + hasSimplexLink = false } } - .onChange(of: chat.userCanSend) { canSend in - if !canSend { + .onChange(of: chat.chatInfo.sendMsgEnabled) { sendEnabled in + if !sendEnabled { cancelCurrentVoiceRecording() clearCurrentDraft() clearState() @@ -397,7 +525,7 @@ struct ComposeView: View { Task { var media: [(String, UploadContent)] = [] for content in selected { - if let img = resizeImageToStrSize(content.uiImage, maxDataSize: 14000) { + if let img = await resizeImageToStrSize(content.uiImage, maxDataSize: 14000) { media.append((img, content)) await MainActor.run { composeState = composeState.copy(preview: .mediaPreviews(mediaPreviews: media)) @@ -489,7 +617,7 @@ struct ComposeView: View { } private func addMediaContent(_ content: UploadContent) async { - if let img = resizeImageToStrSize(content.uiImage, maxDataSize: 14000) { + if let img = await resizeImageToStrSize(content.uiImage, maxDataSize: 14000) { var newMedia: [(String, UploadContent?)] = [] if case var .mediaPreviews(media) = composeState.preview { media.append((img, content)) @@ -580,6 +708,7 @@ struct ComposeView: View { cancelPreview: cancelLinkPreview, cancelEnabled: !composeState.inProgress ) + Divider() case let .mediaPreviews(mediaPreviews: media): ComposeImageView( images: media.map { (img, _) in img }, @@ -588,6 +717,7 @@ struct ComposeView: View { chosenMedia = [] }, cancelEnabled: !composeState.editing && !composeState.inProgress) + Divider() case let .voicePreview(recordingFileName, _): ComposeVoiceView( recordingFileName: recordingFileName, @@ -600,6 +730,7 @@ struct ComposeView: View { cancelEnabled: !composeState.editing && !composeState.inProgress, stopPlayback: $stopPlayback ) + Divider() case let .filePreview(fileName, _): ComposeFileView( fileName: fileName, @@ -607,9 +738,42 @@ struct ComposeView: View { composeState = composeState.copy(preview: .noPreview) }, cancelEnabled: !composeState.editing && !composeState.inProgress) + Divider() } } + private func msgNotAllowedView(_ reason: LocalizedStringKey, icon: String) -> some View { + HStack { + Image(systemName: icon).foregroundColor(theme.colors.secondary) + Text(reason).italic() + } + .padding(12) + .frame(minHeight: 54) + .frame(maxWidth: .infinity, alignment: .leading) + .background(.thinMaterial) + } + + + private func reportReasonView(_ reason: ReportReason) -> some View { + let reportText = switch reason { + case .spam: NSLocalizedString("Report spam: only group moderators will see it.", comment: "report reason") + case .profile: NSLocalizedString("Report member profile: only group moderators will see it.", comment: "report reason") + case .community: NSLocalizedString("Report violation: only group moderators will see it.", comment: "report reason") + case .illegal: NSLocalizedString("Report content: only group moderators will see it.", comment: "report reason") + case .other: NSLocalizedString("Report other: only group moderators will see it.", comment: "report reason") + case .unknown: "" // Should never happen + } + + return Text(reportText) + .italic() + .font(.caption) + .padding(12) + .frame(minHeight: 44) + .frame(maxWidth: .infinity, alignment: .leading) + .background(.thinMaterial) + } + + @ViewBuilder private func contextItemView() -> some View { switch composeState.contextItem { case .noContextItem: @@ -617,17 +781,36 @@ struct ComposeView: View { case let .quotedItem(chatItem: quotedItem): ContextItemView( chat: chat, - contextItem: quotedItem, + contextItems: [quotedItem], contextIcon: "arrowshape.turn.up.left", cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) } ) + Divider() case let .editingItem(chatItem: editingItem): ContextItemView( chat: chat, - contextItem: editingItem, + contextItems: [editingItem], contextIcon: "pencil", cancelContextItem: { clearState() } ) + Divider() + case let .forwardingItems(chatItems, _): + ContextItemView( + chat: chat, + contextItems: chatItems, + contextIcon: "arrowshape.turn.up.forward", + cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) } + ) + Divider() + case let .reportedItem(chatItem: reportedItem, _): + ContextItemView( + chat: chat, + contextItems: [reportedItem], + contextIcon: "flag", + cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) }, + contextIconForeground: Color.red + ) + Divider() } } @@ -643,16 +826,25 @@ struct ComposeView: View { var sent: ChatItem? let msgText = text ?? composeState.message let liveMessage = composeState.liveMessage + let mentions = composeState.memberMentions if !live { if liveMessage != nil { composeState = composeState.copy(liveMessage: nil) } await sending() } if chat.chatInfo.contact?.nextSendGrpInv ?? false { await sendMemberContactInvitation() + } else if case let .forwardingItems(chatItems, fromChatInfo) = composeState.contextItem { + // Composed text is send as a reply to the last forwarded item + sent = await forwardItems(chatItems, fromChatInfo, ttl).last + if !composeState.message.isEmpty { + _ = await send(checkLinkPreview(), quoted: sent?.id, live: false, ttl: ttl, mentions: mentions) + } } else if case let .editingItem(ci) = composeState.contextItem { sent = await updateMessage(ci, live: live) } else if let liveMessage = liveMessage, liveMessage.sentMsg != nil { sent = await updateMessage(liveMessage.chatItem, live: live) + } else if case let .reportedItem(chatItem, reason) = composeState.contextItem { + sent = await send(reason, chatItemId: chatItem.id) } else { var quoted: Int64? = nil if case let .quotedItem(chatItem: quotedItem) = composeState.contextItem { @@ -661,42 +853,67 @@ struct ComposeView: View { switch (composeState.preview) { case .noPreview: - sent = await send(.text(msgText), quoted: quoted, live: live, ttl: ttl) + sent = await send(.text(msgText), quoted: quoted, live: live, ttl: ttl, mentions: mentions) case .linkPreview: - sent = await send(checkLinkPreview(), quoted: quoted, live: live, ttl: ttl) - case let .mediaPreviews(mediaPreviews: media): + sent = await send(checkLinkPreview(), quoted: quoted, live: live, ttl: ttl, mentions: mentions) + case let .mediaPreviews(media): + // TODO: CHECK THIS let last = media.count - 1 + var msgs: [ComposedMessage] = [] if last >= 0 { for i in 0.. 0 { + // Sleep to allow `progressByTimeout` update be rendered + try? await Task.sleep(nanoseconds: 100_000000) + } + if let (fileSource, msgContent) = mediaContent(media[i], text: "") { + msgs.append(ComposedMessage(fileSource: fileSource, msgContent: msgContent)) } - _ = try? await Task.sleep(nanoseconds: 100_000000) } - if case (_, .video(_, _, _)) = media[last] { - sent = await sendVideo(media[last], text: msgText, quoted: quoted, live: live, ttl: ttl) - } else { - sent = await sendImage(media[last], text: msgText, quoted: quoted, live: live, ttl: ttl) + if let (fileSource, msgContent) = mediaContent(media[last], text: msgText) { + msgs.append(ComposedMessage(fileSource: fileSource, quotedItemId: quoted, msgContent: msgContent)) } } - if sent == nil { - sent = await send(.text(msgText), quoted: quoted, live: live, ttl: ttl) + if msgs.isEmpty { + msgs = [ComposedMessage(quotedItemId: quoted, msgContent: .text(msgText))] } + sent = await send(msgs, live: live, ttl: ttl).last + case let .voicePreview(recordingFileName, duration): stopPlayback.toggle() let file = voiceCryptoFile(recordingFileName) - sent = await send(.voice(text: msgText, duration: duration), quoted: quoted, file: file, ttl: ttl) + sent = await send(.voice(text: msgText, duration: duration), quoted: quoted, file: file, ttl: ttl, mentions: mentions) case let .filePreview(_, file): if let savedFile = saveFileFromURL(file) { - sent = await send(.file(msgText), quoted: quoted, file: savedFile, live: live, ttl: ttl) + sent = await send(.file(msgText), quoted: quoted, file: savedFile, live: live, ttl: ttl, mentions: mentions) } } } - await MainActor.run { clearState(live: live) } + await MainActor.run { + let wasForwarding = composeState.forwarding + clearState(live: live) + if wasForwarding, + chatModel.draftChatId == chat.chatInfo.id, + let draft = chatModel.draft { + composeState = draft + } + } return sent + func mediaContent(_ media: (String, UploadContent?), text: String) -> (CryptoFile?, MsgContent)? { + let (previewImage, uploadContent) = media + return switch uploadContent { + case let .simpleImage(image): + (saveImage(image), .image(text: text, image: previewImage)) + case let .animatedImage(image): + (saveAnimImage(image), .image(text: text, image: previewImage)) + case let .video(_, url, duration): + (moveTempFileFromURL(url), .video(text: text, image: previewImage, duration: duration)) + case .none: + nil + } + } + func sending() async { await MainActor.run { composeState.inProgress = true } } @@ -723,7 +940,7 @@ struct ComposeView: View { type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, itemId: ei.id, - msg: mc, + updatedMessage: UpdatedMessage(msgContent: mc, mentions: composeState.memberMentions), live: live ) await MainActor.run { @@ -755,27 +972,13 @@ struct ComposeView: View { return .voice(text: msgText, duration: duration) case .file: return .file(msgText) + case .report(_, let reason): + return .report(text: msgText, reason: reason) case .unknown(let type, _): return .unknown(type: type, text: msgText) } } - func sendImage(_ imageData: (String, UploadContent?), text: String = "", quoted: Int64? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? { - let (image, data) = imageData - if let data = data, let savedFile = saveAnyImage(data) { - return await send(.image(text: text, image: image), quoted: quoted, file: savedFile, live: live, ttl: ttl) - } - return nil - } - - func sendVideo(_ imageData: (String, UploadContent?), text: String = "", quoted: Int64? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? { - let (image, data) = imageData - if case let .video(_, url, duration) = data, let savedFile = moveTempFileFromURL(url) { - return await send(.video(text: text, image: image, duration: duration), quoted: quoted, file: savedFile, live: live, ttl: ttl) - } - return nil - } - func voiceCryptoFile(_ fileName: String) -> CryptoFile? { if !privacyEncryptLocalFilesGroupDefault.get() { return CryptoFile.plain(fileName) @@ -790,35 +993,93 @@ struct ComposeView: View { return nil } } + + func send(_ reportReason: ReportReason, chatItemId: Int64) async -> ChatItem? { + if let chatItems = await apiReportMessage( + groupId: chat.chatInfo.apiId, + chatItemId: chatItemId, + reportReason: reportReason, + reportText: msgText + ) { + await MainActor.run { + for chatItem in chatItems { + chatModel.addChatItem(chat.chatInfo, chatItem) + } + } + return chatItems.first + } + + return nil + } + + func send(_ mc: MsgContent, quoted: Int64?, file: CryptoFile? = nil, live: Bool = false, ttl: Int?, mentions: [String: Int64]) async -> ChatItem? { + await send( + [ComposedMessage(fileSource: file, quotedItemId: quoted, msgContent: mc, mentions: mentions)], + live: live, + ttl: ttl + ).first + } - func send(_ mc: MsgContent, quoted: Int64?, file: CryptoFile? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? { - if let chatItem = chat.chatInfo.chatType == .local - ? await apiCreateChatItem(noteFolderId: chat.chatInfo.apiId, file: file, msg: mc) - : await apiSendMessage( + func send(_ msgs: [ComposedMessage], live: Bool, ttl: Int?) async -> [ChatItem] { + if let chatItems = chat.chatInfo.chatType == .local + ? await apiCreateChatItems(noteFolderId: chat.chatInfo.apiId, composedMessages: msgs) + : await apiSendMessages( type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, - file: file, - quotedItemId: quoted, - msg: mc, live: live, - ttl: ttl + ttl: ttl, + composedMessages: msgs ) { await MainActor.run { chatModel.removeLiveDummy(animated: false) - chatModel.addChatItem(chat.chatInfo, chatItem) + for chatItem in chatItems { + chatModel.addChatItem(chat.chatInfo, chatItem) + } } - return chatItem + return chatItems } - if let file = file { - removeFile(file.filePath) + for msg in msgs { + if let file = msg.fileSource { + removeFile(file.filePath) + } + } + return [] + } + + func forwardItems(_ forwardedItems: [ChatItem], _ fromChatInfo: ChatInfo, _ ttl: Int?) async -> [ChatItem] { + if let chatItems = await apiForwardChatItems( + toChatType: chat.chatInfo.chatType, + toChatId: chat.chatInfo.apiId, + fromChatType: fromChatInfo.chatType, + fromChatId: fromChatInfo.apiId, + itemIds: forwardedItems.map { $0.id }, + ttl: ttl + ) { + await MainActor.run { + for chatItem in chatItems { + chatModel.addChatItem(chat.chatInfo, chatItem) + } + if forwardedItems.count != chatItems.count { + showAlert( + String.localizedStringWithFormat( + NSLocalizedString("%d messages not forwarded", comment: "alert title"), + forwardedItems.count - chatItems.count + ), + message: NSLocalizedString("Messages were deleted after you selected them.", comment: "alert message") + ) + } + } + return chatItems + } else { + return [] } - return nil } func checkLinkPreview() -> MsgContent { switch (composeState.preview) { case let .linkPreview(linkPreview: linkPreview): - if let url = parseMessage(msgText), + if let parsedMsg = parseSimpleXMarkdown(msgText), + let url = getSimplexLink(parsedMsg).url, let linkPreview = linkPreview, url == linkPreview.uri { return .link(text: msgText, preview: linkPreview) @@ -829,14 +1090,6 @@ struct ComposeView: View { return .text(msgText) } } - - func saveAnyImage(_ img: UploadContent) -> CryptoFile? { - switch img { - case let .simpleImage(image): return saveImage(image) - case let .animatedImage(image): return saveAnimImage(image) - default: return nil - } - } } private func startVoiceMessageRecording() async { @@ -945,9 +1198,9 @@ struct ComposeView: View { } } - private func showLinkPreview(_ s: String) { + private func showLinkPreview(_ parsedMsg: [FormattedText]?) { prevLinkUrl = linkUrl - linkUrl = parseMessage(s) + (linkUrl, hasSimplexLink) = getSimplexLink(parsedMsg) if let url = linkUrl { if url != composeState.linkPreview?.uri && url != pendingLinkUrl { pendingLinkUrl = url @@ -964,13 +1217,17 @@ struct ComposeView: View { } } - private func parseMessage(_ msg: String) -> URL? { - let parsedMsg = parseSimpleXMarkdown(msg) - let uri = parsedMsg?.first(where: { ft in + private func getSimplexLink(_ parsedMsg: [FormattedText]?) -> (url: URL?, hasSimplexLink: Bool) { + guard let parsedMsg else { return (nil, false) } + let url: URL? = if let uri = parsedMsg.first(where: { ft in ft.format == .uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text) - }) - if let uri = uri { return URL(string: uri.text) } - else { return nil } + }) { + URL(string: uri.text) + } else { + nil + } + let simplexLink = parsedMsgHasSimplexLink(parsedMsg) + return (url, simplexLink) } private func isSimplexLink(_ link: String) -> Bool { @@ -992,11 +1249,14 @@ struct ComposeView: View { if pendingLinkUrl == url { composeState = composeState.copy(preview: .linkPreview(linkPreview: nil)) getLinkPreview(url: url) { linkPreview in - if let linkPreview = linkPreview, - pendingLinkUrl == url { + if let linkPreview, pendingLinkUrl == url { composeState = composeState.copy(preview: .linkPreview(linkPreview: linkPreview)) - pendingLinkUrl = nil + } else { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + composeState = composeState.copy(preview: .noPreview) + } } + pendingLinkUrl = nil } } } @@ -1013,18 +1273,23 @@ struct ComposeView_Previews: PreviewProvider { static var previews: some View { let chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []) @State var composeState = ComposeState(message: "hello") + @State var selectedRange = NSRange() return Group { ComposeView( chat: chat, composeState: $composeState, - keyboardVisible: Binding.constant(true) + keyboardVisible: Binding.constant(true), + keyboardHiddenDate: Binding.constant(Date.now), + selectedRange: $selectedRange ) .environmentObject(ChatModel()) ComposeView( chat: chat, composeState: $composeState, - keyboardVisible: Binding.constant(true) + keyboardVisible: Binding.constant(true), + keyboardHiddenDate: Binding.constant(Date.now), + selectedRange: $selectedRange ) .environmentObject(ChatModel()) } diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeVoiceView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeVoiceView.swift index 2617bc77bc..441a68fccb 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeVoiceView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeVoiceView.swift @@ -25,7 +25,7 @@ func voiceMessageTime_(_ time: TimeInterval?) -> String { struct ComposeVoiceView: View { @EnvironmentObject var chatModel: ChatModel - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme var recordingFileName: String @Binding var recordingTime: TimeInterval? @Binding var recordingState: VoiceMessageRecordingState @@ -50,9 +50,9 @@ struct ComposeVoiceView: View { } .padding(.vertical, 1) .frame(height: ComposeVoiceView.previewHeight) - .background(colorScheme == .light ? sentColorLight : sentColorDark) + .background(theme.appColors.sentMessage) + .frame(minHeight: 54) .frame(maxWidth: .infinity) - .padding(.top, 8) } private func recordingMode() -> some View { @@ -80,7 +80,7 @@ struct ComposeVoiceView: View { Button { startPlayback() } label: { - playPauseIcon("play.fill") + playPauseIcon("play.fill", theme.colors.primary) } Text(voiceMessageTime_(recordingTime)) case .playing: @@ -88,7 +88,7 @@ struct ComposeVoiceView: View { audioPlayer?.pause() playbackState = .paused } label: { - playPauseIcon("pause.fill") + playPauseIcon("pause.fill", theme.colors.primary) } Text(voiceMessageTime_(playbackTime)) case .paused: @@ -96,7 +96,7 @@ struct ComposeVoiceView: View { audioPlayer?.play() playbackState = .playing } label: { - playPauseIcon("play.fill") + playPauseIcon("play.fill", theme.colors.primary) } Text(voiceMessageTime_(playbackTime)) } @@ -131,7 +131,7 @@ struct ComposeVoiceView: View { } } - private func playPauseIcon(_ image: String, _ color: Color = .accentColor) -> some View { + private func playPauseIcon(_ image: String, _ color: Color) -> some View { Image(systemName: image) .resizable() .aspectRatio(contentMode: .fit) @@ -147,9 +147,11 @@ struct ComposeVoiceView: View { } label: { Image(systemName: "multiply") } + .tint(theme.colors.primary) } struct SliderBar: View { + @EnvironmentObject var theme: AppTheme var length: TimeInterval @Binding var progress: TimeInterval? var seek: (TimeInterval) -> Void @@ -158,10 +160,12 @@ struct ComposeVoiceView: View { Slider(value: Binding(get: { progress ?? TimeInterval(0) }, set: { seek($0) }), in: 0 ... length) .frame(maxWidth: .infinity) .frame(height: 4) + .tint(theme.colors.primary) } } private struct ProgressBar: View { + @EnvironmentObject var theme: AppTheme var length: TimeInterval @Binding var progress: TimeInterval? @@ -169,7 +173,7 @@ struct ComposeVoiceView: View { GeometryReader { geometry in ZStack { Rectangle() - .fill(Color.accentColor) + .fill(theme.colors.primary) .frame(width: min(CGFloat((progress ?? TimeInterval(0)) / length) * geometry.size.width, geometry.size.width), height: 4) .animation(.linear, value: progress) } diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextInvitingContactMemberView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextInvitingContactMemberView.swift index acb4f6d3e1..82090f312a 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextInvitingContactMemberView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextInvitingContactMemberView.swift @@ -9,19 +9,18 @@ import SwiftUI struct ContextInvitingContactMemberView: View { - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme var body: some View { HStack { Image(systemName: "message") - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) Text("Send direct message to connect") } .padding(12) - .frame(minHeight: 50) + .frame(minHeight: 54) .frame(maxWidth: .infinity, alignment: .leading) - .background(colorScheme == .light ? sentColorLight : sentColorDark) - .padding(.top, 8) + .background(.thinMaterial) } } diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextItemView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextItemView.swift index 3eb128cded..845442c75f 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextItemView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextItemView.swift @@ -10,11 +10,13 @@ import SwiftUI import SimpleXChat struct ContextItemView: View { - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme @ObservedObject var chat: Chat - let contextItem: ChatItem + let contextItems: [ChatItem] let contextIcon: String let cancelContextItem: () -> Void + var contextIconForeground: Color? = nil + var showSender: Bool = true var body: some View { HStack { @@ -22,14 +24,23 @@ struct ContextItemView: View { .resizable() .aspectRatio(contentMode: .fit) .frame(width: 16, height: 16) - .foregroundColor(.secondary) - if let sender = contextItem.memberDisplayName { - VStack(alignment: .leading, spacing: 4) { - Text(sender).font(.caption).foregroundColor(.secondary) - msgContentView(lines: 2) - } + .foregroundColor(contextIconForeground ?? theme.colors.secondary) + if let singleItem = contextItems.first, contextItems.count == 1 { + if showSender, let sender = singleItem.memberDisplayName { + VStack(alignment: .leading, spacing: 4) { + Text(sender).font(.caption).foregroundColor(theme.colors.secondary) + msgContentView(lines: 2, contextItem: singleItem) + } + } else { + msgContentView(lines: 3, contextItem: singleItem) + } } else { - msgContentView(lines: 3) + Text( + chat.chatInfo.chatType == .local + ? "Saving \(contextItems.count) messages" + : "Forwarding \(contextItems.count) messages" + ) + .italic() } Spacer() Button { @@ -39,29 +50,52 @@ struct ContextItemView: View { } label: { Image(systemName: "multiply") } + .tint(theme.colors.primary) } .padding(12) - .frame(minHeight: 50) + .frame(minHeight: 54) .frame(maxWidth: .infinity) - .background(chatItemFrameColor(contextItem, colorScheme)) - .padding(.top, 8) + .background(background) } - private func msgContentView(lines: Int) -> some View { - MsgContentView( - chat: chat, - text: contextItem.text, - formattedText: contextItem.formattedText, - showSecrets: false - ) - .multilineTextAlignment(isRightToLeft(contextItem.text) ? .trailing : .leading) - .lineLimit(lines) + private var background: Color { + contextItems.first + .map { chatItemFrameColor($0, theme) } + ?? Color(uiColor: .tertiarySystemBackground) + } + + private func msgContentView(lines: Int, contextItem: ChatItem) -> some View { + contextMsgPreview(contextItem) + .multilineTextAlignment(isRightToLeft(contextItem.text) ? .trailing : .leading) + .lineLimit(lines) + } + + private func contextMsgPreview(_ contextItem: ChatItem) -> some View { + let r = messageText(contextItem.text, contextItem.formattedText, sender: nil, preview: true, mentions: contextItem.mentions, userMemberId: nil, showSecrets: nil, backgroundColor: UIColor(background)) + let t = attachment() + Text(AttributedString(r.string)) + return t.if(r.hasSecrets, transform: hiddenSecretsView) + + func attachment() -> Text { + let isFileLoaded = if let fileSource = getLoadedFileSource(contextItem.file) { + FileManager.default.fileExists(atPath: getAppFilePath(fileSource.filePath).path) + } else { false } + switch contextItem.content.msgContent { + case .file: return isFileLoaded ? image("doc.fill") : Text("") + case .image: return image("photo") + case .voice: return isFileLoaded ? image("play.fill") : Text("") + default: return Text("") + } + } + + func image(_ s: String) -> Text { + Text(Image(systemName: s)).foregroundColor(Color(uiColor: .tertiaryLabel)) + textSpace + } } } struct ContextItemView_Previews: PreviewProvider { static var previews: some View { let contextItem: ChatItem = ChatItem.getSample(1, .directSnd, .now, "hello") - return ContextItemView(chat: Chat.sampleData, contextItem: contextItem, contextIcon: "pencil.circle", cancelContextItem: {}) + return ContextItemView(chat: Chat.sampleData, contextItems: [contextItem], contextIcon: "pencil.circle", cancelContextItem: {}, contextIconForeground: Color.red) } } diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/NativeTextEditor.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/NativeTextEditor.swift index 3eead5b0af..d809fd7b76 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/NativeTextEditor.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/NativeTextEditor.swift @@ -16,27 +16,26 @@ struct NativeTextEditor: UIViewRepresentable { @Binding var disableEditing: Bool @Binding var height: CGFloat @Binding var focused: Bool - let alignment: TextAlignment + @Binding var lastUnfocusedDate: Date + @Binding var placeholder: String? + @Binding var selectedRange: NSRange let onImagesAdded: ([UploadContent]) -> Void - private let minHeight: CGFloat = 37 + static let minHeight: CGFloat = 39 - private let defaultHeight: CGFloat = { - let field = CustomUITextField(height: Binding.constant(0)) - field.textContainerInset = UIEdgeInsets(top: 8, left: 5, bottom: 6, right: 4) - return min(max(field.sizeThatFits(CGSizeMake(field.frame.size.width, CGFloat.greatestFiniteMagnitude)).height, 37), 360).rounded(.down) - }() - - func makeUIView(context: Context) -> UITextView { - let field = CustomUITextField(height: _height) + func makeUIView(context: Context) -> CustomUITextField { + let field = CustomUITextField(parent: self, height: _height) + field.backgroundColor = .clear field.text = text - field.textAlignment = alignment == .leading ? .left : .right + field.textAlignment = alignment(text) field.autocapitalizationType = .sentences field.setOnTextChangedListener { newText, images in if !disableEditing { - // Speed up the process of updating layout, reduce jumping content on screen - if !isShortEmoji(newText) { updateHeight(field) } text = newText + field.textAlignment = alignment(text) + field.updateFont() + // Speed up the process of updating layout, reduce jumping content on screen + field.updateHeight() } else { field.text = text } @@ -44,52 +43,60 @@ struct NativeTextEditor: UIViewRepresentable { onImagesAdded(images) } } - field.setOnFocusChangedListener { focused = $0 } + field.setOnFocusChangedListener { + focused = $0 + if !focused { + lastUnfocusedDate = .now + } + } field.delegate = field field.textContainerInset = UIEdgeInsets(top: 8, left: 5, bottom: 6, right: 4) - updateFont(field) - updateHeight(field) + field.setPlaceholderView() + field.updateFont() + field.updateHeight(updateBindingNow: false) return field } - func updateUIView(_ field: UITextView, context: Context) { - field.text = text - field.textAlignment = alignment == .leading ? .left : .right - updateFont(field) - updateHeight(field) - } - - private func updateHeight(_ field: UITextView) { - let maxHeight = min(360, field.font!.lineHeight * 12) - // When having emoji in text view and then removing it, sizeThatFits shows previous size (too big for empty text view), so using work around with default size - let newHeight = field.text == "" - ? defaultHeight - : min(max(field.sizeThatFits(CGSizeMake(field.frame.size.width, CGFloat.greatestFiniteMagnitude)).height, minHeight), maxHeight).rounded(.down) - - if field.frame.size.height != newHeight { - field.frame.size = CGSizeMake(field.frame.size.width, newHeight) - (field as! CustomUITextField).invalidateIntrinsicContentHeight(newHeight) + func updateUIView(_ field: CustomUITextField, context: Context) { + if field.markedTextRange == nil && field.text != text { + field.text = text + field.textAlignment = alignment(text) + field.updateFont() + field.updateHeight(updateBindingNow: false) + } + if field.placeholder != placeholder { + field.placeholder = placeholder + } + if field.selectedRange != selectedRange { + field.selectedRange = selectedRange } - } - - private func updateFont(_ field: UITextView) { - field.font = isShortEmoji(field.text) - ? (field.text.count < 4 ? largeEmojiUIFont : mediumEmojiUIFont) - : UIFont.preferredFont(forTextStyle: .body) } } -private class CustomUITextField: UITextView, UITextViewDelegate { +private func alignment(_ text: String) -> NSTextAlignment { + isRightToLeft(text) ? .right : .left +} + +class CustomUITextField: UITextView, UITextViewDelegate { + var parent: NativeTextEditor? var height: Binding var newHeight: CGFloat = 0 var onTextChanged: (String, [UploadContent]) -> Void = { newText, image in } var onFocusChanged: (Bool) -> Void = { focused in } - - init(height: Binding) { + + private let placeholderLabel: UILabel = UILabel() + + init(parent: NativeTextEditor?, height: Binding) { + self.parent = parent self.height = height super.init(frame: .zero, textContainer: nil) } + var placeholder: String? { + get { placeholderLabel.text } + set { placeholderLabel.text = newValue } + } + required init?(coder: NSCoder) { fatalError("Not implemented") } @@ -102,16 +109,63 @@ private class CustomUITextField: UITextView, UITextViewDelegate { invalidateIntrinsicContentSize() } - override var intrinsicContentSize: CGSize { - if height.wrappedValue != newHeight { - DispatchQueue.main.asyncAfter(deadline: .now(), execute: { self.height.wrappedValue = self.newHeight }) + func updateHeight(updateBindingNow: Bool = true) { + let maxHeight = min(360, font!.lineHeight * 12) + let newHeight = min(max(sizeThatFits(CGSizeMake(frame.size.width, CGFloat.greatestFiniteMagnitude)).height, NativeTextEditor.minHeight), maxHeight).rounded(.down) + + if self.newHeight != newHeight { + frame.size = CGSizeMake(frame.size.width, newHeight) + invalidateIntrinsicContentHeight(newHeight) + if updateBindingNow { + self.height.wrappedValue = newHeight + } else { + DispatchQueue.main.async { + self.height.wrappedValue = newHeight + } + } } - return CGSizeMake(0, newHeight) + } + + func updateFont() { + let newFont = isShortEmoji(text) + ? (text.count < 4 ? largeEmojiUIFont : mediumEmojiUIFont) + : UIFont.preferredFont(forTextStyle: .body) + if font != newFont { + font = newFont + // force apply new font because it has problem with doing it when the field had two emojis + if text.count == 0 { + text = " " + text = "" + } + } + } + + override func layoutSubviews() { + super.layoutSubviews() + updateHeight() + } + + override var intrinsicContentSize: CGSize { + CGSizeMake(0, newHeight) } func setOnTextChangedListener(onTextChanged: @escaping (String, [UploadContent]) -> Void) { self.onTextChanged = onTextChanged } + + func setPlaceholderView() { + placeholderLabel.textColor = .lightGray + placeholderLabel.font = UIFont.preferredFont(forTextStyle: .body) + placeholderLabel.isHidden = !text.isEmpty + placeholderLabel.translatesAutoresizingMaskIntoConstraints = false + addSubview(placeholderLabel) + + NSLayoutConstraint.activate([ + placeholderLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 7), + placeholderLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -7), + placeholderLabel.topAnchor.constraint(equalTo: topAnchor, constant: 8) + ]) + } func setOnFocusChangedListener(onFocusChanged: @escaping (Bool) -> Void) { self.onFocusChanged = onFocusChanged @@ -160,6 +214,7 @@ private class CustomUITextField: UITextView, UITextViewDelegate { } func textViewDidChange(_ textView: UITextView) { + placeholderLabel.isHidden = !text.isEmpty if textView.markedTextRange == nil { var images: [UploadContent] = [] var rangeDiff = 0 @@ -191,10 +246,22 @@ private class CustomUITextField: UITextView, UITextViewDelegate { func textViewDidBeginEditing(_ textView: UITextView) { onFocusChanged(true) + updateSelectedRange(textView) } func textViewDidEndEditing(_ textView: UITextView) { onFocusChanged(false) + updateSelectedRange(textView) + } + + func textViewDidChangeSelection(_ textView: UITextView) { + updateSelectedRange(textView) + } + + private func updateSelectedRange(_ textView: UITextView) { + if parent?.selectedRange != textView.selectedRange { + parent?.selectedRange = textView.selectedRange + } } } @@ -205,7 +272,9 @@ struct NativeTextEditor_Previews: PreviewProvider{ disableEditing: Binding.constant(false), height: Binding.constant(100), focused: Binding.constant(false), - alignment: TextAlignment.leading, + lastUnfocusedDate: Binding.constant(.now), + placeholder: Binding.constant("Placeholder"), + selectedRange: Binding.constant(NSRange(location: 0, length: 0)), onImagesAdded: { _ in } ) .fixedSize(horizontal: false, vertical: true) diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift index 8f7b23c888..e7b02c9aea 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift @@ -13,6 +13,9 @@ private let liveMsgInterval: UInt64 = 3000_000000 struct SendMessageView: View { @Binding var composeState: ComposeState + @Binding var selectedRange: NSRange + @EnvironmentObject var theme: AppTheme + @Environment(\.isEnabled) var isEnabled var sendMessage: (Int?) -> Void var sendLiveMessage: (() async -> Void)? = nil var updateLiveMessage: (() async -> Void)? = nil @@ -20,6 +23,7 @@ struct SendMessageView: View { var nextSendGrpInv: Bool = false var showVoiceMessageButton: Bool = true var voiceMessageAllowed: Bool = true + var disableSendButton = false var showEnableVoiceMessagesAlert: ChatInfo.ShowEnableVoiceMessagesAlert = .other var startVoiceMessageRecording: (() -> Void)? = nil var finishVoiceMessageRecording: (() -> Void)? = nil @@ -29,8 +33,9 @@ struct SendMessageView: View { @State private var holdingVMR = false @Namespace var namespace @Binding var keyboardVisible: Bool + @Binding var keyboardHiddenDate: Date var sendButtonColor = Color.accentColor - @State private var teHeight: CGFloat = 42 + @State private var teHeight: CGFloat = NativeTextEditor.minHeight @State private var teFont: Font = .body @State private var sendButtonSize: CGFloat = 29 @State private var sendButtonOpacity: CGFloat = 1 @@ -38,57 +43,57 @@ struct SendMessageView: View { @State private var showCustomTimePicker = false @State private var selectedDisappearingMessageTime: Int? = customDisappearingMessageTimeDefault.get() @State private var progressByTimeout = false - @AppStorage(DEFAULT_LIVE_MESSAGE_ALERT_SHOWN) private var liveMessageAlertShown = false + @UserDefault(DEFAULT_LIVE_MESSAGE_ALERT_SHOWN) private var liveMessageAlertShown = false var body: some View { - ZStack { - HStack(alignment: .bottom) { - ZStack(alignment: .leading) { - if case .voicePreview = composeState.preview { - Text("Voice message…") - .font(teFont.italic()) - .multilineTextAlignment(.leading) - .foregroundColor(.secondary) - .padding(.horizontal, 10) - .padding(.vertical, 8) - .frame(maxWidth: .infinity) - } else { - let alignment: TextAlignment = isRightToLeft(composeState.message) ? .trailing : .leading - NativeTextEditor( - text: $composeState.message, - disableEditing: $composeState.inProgress, - height: $teHeight, - focused: $keyboardVisible, - alignment: alignment, - onImagesAdded: onMediaAdded - ) - .allowsTightening(false) - .fixedSize(horizontal: false, vertical: true) - } - } - - if progressByTimeout { - ProgressView() - .scaleEffect(1.4) - .frame(width: 31, height: 31, alignment: .center) - .padding([.bottom, .trailing], 3) - } else { - VStack(alignment: .trailing) { - if teHeight > 100 && !composeState.inProgress { - deleteTextButton() - Spacer() - } - composeActionButtons() - } - .frame(height: teHeight, alignment: .bottom) - } + let composeShape = RoundedRectangle(cornerSize: CGSize(width: 20, height: 20)) + ZStack(alignment: .leading) { + if case .voicePreview = composeState.preview { + Text("Voice message…") + .font(teFont.italic()) + .multilineTextAlignment(.leading) + .foregroundColor(theme.colors.secondary) + .padding(.horizontal, 10) + .padding(.vertical, 8) + .padding(.trailing, 32) + .frame(maxWidth: .infinity) + } else { + NativeTextEditor( + text: $composeState.message, + disableEditing: $composeState.inProgress, + height: $teHeight, + focused: $keyboardVisible, + lastUnfocusedDate: $keyboardHiddenDate, + placeholder: Binding(get: { composeState.placeholder }, set: { _ in }), + selectedRange: $selectedRange, + onImagesAdded: onMediaAdded + ) + .padding(.trailing, 32) + .allowsTightening(false) + .fixedSize(horizontal: false, vertical: true) } - .padding(.vertical, 1) - .overlay( - RoundedRectangle(cornerSize: CGSize(width: 20, height: 20)) - .strokeBorder(.secondary, lineWidth: 0.3, antialiased: true) - ) } + .overlay(alignment: .topTrailing, content: { + if !progressByTimeout && teHeight > 100 && !composeState.inProgress { + deleteTextButton() + } + }) + .overlay(alignment: .bottomTrailing, content: { + if progressByTimeout { + ProgressView() + .scaleEffect(1.4) + .frame(width: 31, height: 31, alignment: .center) + .padding([.bottom, .trailing], 4) + } else { + composeActionButtons() + // required for intercepting clicks + .background(.white.opacity(0.000001)) + } + }) + .padding(.vertical, 1) + .background(theme.colors.background) + .clipShape(composeShape) + .overlay(composeShape.strokeBorder(.secondary, lineWidth: 0.5).opacity(0.7)) .onChange(of: composeState.message, perform: { text in updateFont(text) }) .onChange(of: composeState.inProgress) { inProgress in if inProgress { @@ -106,9 +111,12 @@ struct SendMessageView: View { let vmrs = composeState.voiceMessageRecordingState if nextSendGrpInv { inviteMemberContactButton() + } else if case .reportedItem = composeState.contextItem { + sendMessageButton() } else if showVoiceMessageButton && composeState.message.isEmpty && !composeState.editing + && !composeState.forwarding && composeState.liveMessage == nil && ((composeState.noPreview && vmrs == .noRecording) || (vmrs == .recording && holdingVMR)) { @@ -164,7 +172,7 @@ struct SendMessageView: View { !composeState.sendEnabled || composeState.inProgress ) - .frame(width: 29, height: 29) + .frame(width: 31, height: 31) .padding([.bottom, .trailing], 4) } @@ -184,9 +192,10 @@ struct SendMessageView: View { !composeState.sendEnabled || composeState.inProgress || (!voiceMessageAllowed && composeState.voicePreview) || - composeState.endLiveDisabled + composeState.endLiveDisabled || + disableSendButton ) - .frame(width: 29, height: 29) + .frame(width: 31, height: 31) .contextMenu{ sendButtonContextMenuItems() } @@ -226,6 +235,7 @@ struct SendMessageView: View { !composeState.editing { if case .noContextItem = composeState.contextItem, !composeState.voicePreview, + !composeState.manyMediaPreviews, let send = sendLiveMessage, let update = updateLiveMessage { Button { @@ -246,6 +256,8 @@ struct SendMessageView: View { } private struct RecordVoiceMessageButton: View { + @Environment(\.isEnabled) var isEnabled + @EnvironmentObject var theme: AppTheme var startVoiceMessageRecording: (() -> Void)? var finishVoiceMessageRecording: (() -> Void)? @Binding var holdingVMR: Bool @@ -253,12 +265,14 @@ struct SendMessageView: View { @State private var pressed: TimeInterval? = nil var body: some View { - Button(action: {}) { - Image(systemName: "mic.fill") - .foregroundColor(.accentColor) - } + Image(systemName: isEnabled ? "mic.fill" : "mic") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .foregroundColor(isEnabled ? theme.colors.primary : theme.colors.secondary) + .opacity(holdingVMR ? 0.7 : 1) .disabled(disabled) - .frame(width: 29, height: 29) + .frame(width: 31, height: 31) .padding([.bottom, .trailing], 4) ._onButtonGesture { down in if down { @@ -266,9 +280,7 @@ struct SendMessageView: View { pressed = ProcessInfo.processInfo.systemUptime startVoiceMessageRecording?() } else { - let now = ProcessInfo.processInfo.systemUptime - if let pressed = pressed, - now - pressed >= 1 { + if let pressed, ProcessInfo.processInfo.systemUptime - pressed >= 1 { finishVoiceMessageRecording?() } holdingVMR = false @@ -308,10 +320,13 @@ struct SendMessageView: View { } } label: { Image(systemName: "mic") - .foregroundColor(.secondary) + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) + .foregroundColor(theme.colors.secondary) } .disabled(composeState.inProgress) - .frame(width: 29, height: 29) + .frame(width: 31, height: 31) .padding([.bottom, .trailing], 4) } @@ -322,7 +337,7 @@ struct SendMessageView: View { Image(systemName: "multiply") .resizable() .scaledToFit() - .foregroundColor(.accentColor) + .foregroundColor(theme.colors.primary) .frame(width: 15, height: 15) } .frame(width: 29, height: 29) @@ -339,7 +354,7 @@ struct SendMessageView: View { Image(systemName: "bolt.fill") .resizable() .scaledToFit() - .foregroundColor(.accentColor) + .foregroundColor(isEnabled ? theme.colors.primary : theme.colors.secondary) .frame(width: 20, height: 20) } .frame(width: 29, height: 29) @@ -382,7 +397,7 @@ struct SendMessageView: View { } Task { _ = try? await Task.sleep(nanoseconds: liveMsgInterval) - while composeState.liveMessage != nil { + while await composeState.liveMessage != nil { await update() _ = try? await Task.sleep(nanoseconds: liveMsgInterval) } @@ -393,10 +408,10 @@ struct SendMessageView: View { private func finishVoiceMessageRecordingButton() -> some View { Button(action: { finishVoiceMessageRecording?() }) { Image(systemName: "stop.fill") - .foregroundColor(.accentColor) + .foregroundColor(theme.colors.primary) } .disabled(composeState.inProgress) - .frame(width: 29, height: 29) + .frame(width: 31, height: 31) .padding([.bottom, .trailing], 4) } @@ -412,8 +427,10 @@ struct SendMessageView: View { struct SendMessageView_Previews: PreviewProvider { static var previews: some View { @State var composeStateNew = ComposeState() + @State var selectedRange = NSRange() let ci = ChatItem.getSample(1, .directSnd, .now, "hello") @State var composeStateEditing = ComposeState(editingItem: ci) + @State var selectedRangeEditing = NSRange() @State var sendEnabled: Bool = true return Group { @@ -422,9 +439,11 @@ struct SendMessageView_Previews: PreviewProvider { Spacer(minLength: 0) SendMessageView( composeState: $composeStateNew, + selectedRange: $selectedRange, sendMessage: { _ in }, onMediaAdded: { _ in }, - keyboardVisible: Binding.constant(true) + keyboardVisible: Binding.constant(true), + keyboardHiddenDate: Binding.constant(Date.now) ) } VStack { @@ -432,9 +451,11 @@ struct SendMessageView_Previews: PreviewProvider { Spacer(minLength: 0) SendMessageView( composeState: $composeStateEditing, + selectedRange: $selectedRangeEditing, sendMessage: { _ in }, onMediaAdded: { _ in }, - keyboardVisible: Binding.constant(true) + keyboardVisible: Binding.constant(true), + keyboardHiddenDate: Binding.constant(Date.now) ) } } diff --git a/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift b/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift index 86acbf6d54..e4489e46ee 100644 --- a/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift +++ b/apps/ios/Shared/Views/Chat/ContactPreferencesView.swift @@ -12,10 +12,12 @@ import SimpleXChat struct ContactPreferencesView: View { @Environment(\.dismiss) var dismiss: DismissAction @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme @Binding var contact: Contact - @State var featuresAllowed: ContactFeaturesAllowed - @State var currentFeaturesAllowed: ContactFeaturesAllowed + @Binding var featuresAllowed: ContactFeaturesAllowed + @Binding var currentFeaturesAllowed: ContactFeaturesAllowed @State private var showSaveDialogue = false + let savePreferences: () -> Void var body: some View { let user: User = chatModel.currentUser! @@ -47,7 +49,10 @@ struct ContactPreferencesView: View { savePreferences() dismiss() } - Button("Exit without saving") { dismiss() } + Button("Exit without saving") { + featuresAllowed = currentFeaturesAllowed + dismiss() + } } } @@ -66,8 +71,8 @@ struct ContactPreferencesView: View { .frame(height: 36) infoRow("Contact allows", pref.contactPreference.allow.text) } - header: { featureHeader(feature, enabled) } - footer: { featureFooter(feature, enabled) } + header: { featureHeader(feature, enabled).foregroundColor(theme.colors.secondary) } + footer: { featureFooter(feature, enabled).foregroundColor(theme.colors.secondary) } } private func timedMessagesFeatureSection() -> some View { @@ -102,8 +107,8 @@ struct ContactPreferencesView: View { infoRow("Delete after", timeText(pref.contactPreference.ttl)) } } - header: { featureHeader(.timedMessages, enabled) } - footer: { featureFooter(.timedMessages, enabled) } + header: { featureHeader(.timedMessages, enabled).foregroundColor(theme.colors.secondary) } + footer: { featureFooter(.timedMessages, enabled).foregroundColor(theme.colors.secondary) } } private func featureHeader(_ feature: ChatFeature, _ enabled: FeatureEnabled) -> some View { @@ -117,31 +122,15 @@ struct ContactPreferencesView: View { private func featureFooter(_ feature: ChatFeature, _ enabled: FeatureEnabled) -> some View { Text(feature.enabledDescription(enabled)) } - - private func savePreferences() { - Task { - do { - let prefs = contactFeaturesAllowedToPrefs(featuresAllowed) - if let toContact = try await apiSetContactPrefs(contactId: contact.contactId, preferences: prefs) { - await MainActor.run { - contact = toContact - chatModel.updateContact(toContact) - currentFeaturesAllowed = featuresAllowed - } - } - } catch { - logger.error("ContactPreferencesView apiSetContactPrefs error: \(responseError(error))") - } - } - } } struct ContactPreferencesView_Previews: PreviewProvider { static var previews: some View { ContactPreferencesView( contact: Binding.constant(Contact.sampleData), - featuresAllowed: ContactFeaturesAllowed.sampleData, - currentFeaturesAllowed: ContactFeaturesAllowed.sampleData + featuresAllowed: Binding.constant(ContactFeaturesAllowed.sampleData), + currentFeaturesAllowed: Binding.constant(ContactFeaturesAllowed.sampleData), + savePreferences: {} ) } } diff --git a/apps/ios/Shared/Views/Chat/EndlessScrollView.swift b/apps/ios/Shared/Views/Chat/EndlessScrollView.swift new file mode 100644 index 0000000000..cc61754b26 --- /dev/null +++ b/apps/ios/Shared/Views/Chat/EndlessScrollView.swift @@ -0,0 +1,715 @@ +// +// EndlessScrollView.swift +// SimpleX (iOS) +// +// Created by Stanislav Dmitrenko on 25.01.2025. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +struct ScrollRepresentable: UIViewControllerRepresentable where ScrollItem : Identifiable, ScrollItem: Hashable { + + let scrollView: EndlessScrollView + let content: (Int, ScrollItem) -> Content + + func makeUIViewController(context: Context) -> ScrollController { + ScrollController.init(scrollView: scrollView, content: content) + } + + func updateUIViewController(_ controller: ScrollController, context: Context) {} + + class ScrollController: UIViewController { + let scrollView: EndlessScrollView + fileprivate var items: [ScrollItem] = [] + fileprivate var content: ((Int, ScrollItem) -> Content)! + + fileprivate init(scrollView: EndlessScrollView, content: @escaping (Int, ScrollItem) -> Content) { + self.scrollView = scrollView + self.content = content + super.init(nibName: nil, bundle: nil) + self.view = scrollView + scrollView.createCell = createCell + scrollView.updateCell = updateCell + } + + required init?(coder: NSCoder) { fatalError() } + + private func createCell(_ index: Int, _ items: [ScrollItem], _ cellsToReuse: inout [UIView]) -> UIView { + let item: ScrollItem? = index >= 0 && index < items.count ? items[index] : nil + let cell: UIView + if #available(iOS 16.0, *), false { + let c: UITableViewCell = cellsToReuse.isEmpty ? UITableViewCell() : cellsToReuse.removeLast() as! UITableViewCell + if let item { + c.contentConfiguration = UIHostingConfiguration { self.content(index, item) } + .margins(.all, 0) + .minSize(height: 1) // Passing zero will result in system default of 44 points being used + } + cell = c + } else { + let c = cellsToReuse.isEmpty ? HostingCell() : cellsToReuse.removeLast() as! HostingCell + if let item { + c.set(content: self.content(index, item), parent: self) + } + cell = c + } + cell.isHidden = false + cell.backgroundColor = .clear + let size = cell.systemLayoutSizeFitting(CGSizeMake(scrollView.bounds.width, CGFloat.greatestFiniteMagnitude)) + cell.frame.size.width = scrollView.bounds.width + cell.frame.size.height = size.height + return cell + } + + private func updateCell(cell: UIView, _ index: Int, _ items: [ScrollItem]) { + let item = items[index] + if #available(iOS 16.0, *), false { + (cell as! UITableViewCell).contentConfiguration = UIHostingConfiguration { self.content(index, item) } + .margins(.all, 0) + .minSize(height: 1) // Passing zero will result in system default of 44 points being used + } else { + if let cell = cell as? HostingCell { + cell.set(content: self.content(index, item), parent: self) + } else { + fatalError("Unexpected Cell Type for: \(item)") + } + } + let size = cell.systemLayoutSizeFitting(CGSizeMake(scrollView.bounds.width, CGFloat.greatestFiniteMagnitude)) + cell.frame.size.width = scrollView.bounds.width + cell.frame.size.height = size.height + cell.setNeedsLayout() + } + } +} + +class EndlessScrollView: UIScrollView, UIScrollViewDelegate, UIGestureRecognizerDelegate where ScrollItem : Identifiable, ScrollItem: Hashable { + + /// Stores actual state of the scroll view and all elements drawn on the screen + let listState: ListState = ListState() + + /// Just some random big number that will probably be enough to scrolling down and up without reaching the end + var initialOffset: CGFloat = 100000000 + + /// Default item id when no items in the visible items list. Something that will never be in real data + fileprivate static var DEFAULT_ITEM_ID: any Hashable { get { Int64.min } } + + /// Storing an offset that was already used for laying down content to be able to see the difference + var prevProcessedOffset: CGFloat = 0 + + /// When screen is being rotated, it's important to track the view size and adjust scroll offset accordingly because the view doesn't know that the content + /// starts from bottom and ends at top, not vice versa as usual + var oldScreenHeight: CGFloat = 0 + + /// Not 100% correct height of the content since the items loaded lazily and their dimensions are unkown until they are on screen + var estimatedContentHeight: ContentHeight = ContentHeight() + + /// Specify here the value that is small enough to NOT see any weird animation when you scroll to items. Minimum expected item size is ok. Scroll speed depends on it too + var averageItemHeight: CGFloat = 30 + + /// This is used as a multiplier for difference between current index and scrollTo index using [averageItemHeight] as well. Increase it to get faster speed + var scrollStepMultiplier: CGFloat = 0.37 + + /// Adds content padding to top + var insetTop: CGFloat = 100 + + /// Adds content padding to bottom + var insetBottom: CGFloat = 100 + + var scrollToItemIndexDelayed: Int? = nil + + /// The second scroll view that is used only for purpose of displaying scroll bar with made-up content size and scroll offset that is gathered from main scroll view, see [estimatedContentHeight] + let scrollBarView: UIScrollView = UIScrollView(frame: .zero) + + /// Stores views that can be used to hold new content so it will be faster to replace something than to create the whole view from scratch + var cellsToReuse: [UIView] = [] + + /// Enable debug to see hundreds of logs + var debug: Bool = false + + var createCell: (Int, [ScrollItem], inout [UIView]) -> UIView? = { _, _, _ in nil } + var updateCell: (UIView, Int, [ScrollItem]) -> Void = { cell, _, _ in } + + + override init(frame: CGRect) { + super.init(frame: frame) + self.delegate = self + } + + required init?(coder: NSCoder) { fatalError() } + + class ListState: NSObject { + + /// Will be called on every change of the items array, visible items, and scroll position + var onUpdateListener: () -> Void = {} + + /// Items that were used to lay out the screen + var items: [ScrollItem] = [] { + didSet { + onUpdateListener() + } + } + + /// It is equai to the number of [items] + var totalItemsCount: Int { + items.count + } + + /// The items with their positions and other useful information. Only those that are visible on screen + var visibleItems: [EndlessScrollView.VisibleItem] = [] + + /// Index in [items] of the first item on screen. This is intentiallty not derived from visible items because it's is used as a starting point for laying out the screen + var firstVisibleItemIndex: Int = 0 + + /// Unique item id of the first visible item on screen + var firstVisibleItemId: any Hashable = EndlessScrollView.DEFAULT_ITEM_ID + + /// Item offset of the first item on screen. Most of the time it's non-positive but it can be positive as well when a user produce overscroll effect on top/bottom of the scroll view + var firstVisibleItemOffset: CGFloat = -100 + + /// Index of the last visible item on screen + var lastVisibleItemIndex: Int { + visibleItems.last?.index ?? 0 + } + + /// Specifies if visible items cover the whole screen or can cover it (if overscrolled) + var itemsCanCoverScreen: Bool = false + + /// Whether there is a non-animated scroll to item in progress or not + var isScrolling: Bool = false + /// Whether there is an animated scroll to item in progress or not + var isAnimatedScrolling: Bool = false + + override init() { + super.init() + } + } + + class VisibleItem { + let index: Int + let item: ScrollItem + let view: UIView + var offset: CGFloat + + init(index: Int, item: ScrollItem, view: UIView, offset: CGFloat) { + self.index = index + self.item = item + self.view = view + self.offset = offset + } + } + + class ContentHeight { + /// After that you should see overscroll effect. When scroll positon is far from + /// top/bottom items, these values are estimated based on items count multiplied by averageItemHeight or real item height (from visible items). Example: + /// [ 10, 9, 8, 7, (6, 5, 4, 3), 2, 1, 0] - 6, 5, 4, 3 are visible and have know heights but others have unknown height and for them averageItemHeight will be used to calculate the whole content height + var topOffsetY: CGFloat = 0 + var bottomOffsetY: CGFloat = 0 + + var virtualScrollOffsetY: CGFloat = 0 + + /// How much distance were overscolled on top which often means to show sticky scrolling that should scroll back to real position after a users finishes dragging the scrollView + var overscrolledTop: CGFloat = 0 + + /// Adds content padding to bottom and top + var inset: CGFloat = 100 + + /// Estimated height of the contents of scroll view + var height: CGFloat { + get { bottomOffsetY - topOffsetY } + } + + /// Estimated height of the contents of scroll view + distance of overscrolled effect. It's only updated when number of item changes to prevent jumping of scroll bar + var virtualOverscrolledHeight: CGFloat { + get { + bottomOffsetY - topOffsetY + overscrolledTop - inset * 2 + } + } + + func update( + _ contentOffset: CGPoint, + _ listState: ListState, + _ averageItemHeight: CGFloat, + _ updateStaleHeight: Bool + ) { + let lastVisible = listState.visibleItems.last + let firstVisible = listState.visibleItems.first + guard let last = lastVisible, let first = firstVisible else { + topOffsetY = contentOffset.y + bottomOffsetY = contentOffset.y + virtualScrollOffsetY = 0 + overscrolledTop = 0 + return + } + topOffsetY = last.view.frame.origin.y - CGFloat(listState.totalItemsCount - last.index - 1) * averageItemHeight - self.inset + bottomOffsetY = first.view.frame.origin.y + first.view.bounds.height + CGFloat(first.index) * averageItemHeight + self.inset + virtualScrollOffsetY = contentOffset.y - topOffsetY + overscrolledTop = max(0, last.index == listState.totalItemsCount - 1 ? last.view.frame.origin.y - contentOffset.y : 0) + } + } + + var topY: CGFloat { + get { contentOffset.y } + } + + var bottomY: CGFloat { + get { contentOffset.y + bounds.height } + } + + override func layoutSubviews() { + super.layoutSubviews() + if contentSize.height == 0 { + setup() + } + let newScreenHeight = bounds.height + if newScreenHeight != oldScreenHeight && oldScreenHeight != 0 { + contentOffset.y += oldScreenHeight - newScreenHeight + scrollBarView.frame = CGRectMake(frame.width - 10, self.insetTop, 10, frame.height - self.insetTop - self.insetBottom) + } + oldScreenHeight = newScreenHeight + adaptItems(listState.items, false) + if let index = scrollToItemIndexDelayed { + scrollToItem(index) + scrollToItemIndexDelayed = nil + } + } + + private func setup() { + contentSize = CGSizeMake(frame.size.width, initialOffset * 2) + prevProcessedOffset = initialOffset + contentOffset = CGPointMake(0, initialOffset) + + showsVerticalScrollIndicator = false + scrollBarView.showsHorizontalScrollIndicator = false + panGestureRecognizer.delegate = self + addGestureRecognizer(scrollBarView.panGestureRecognizer) + superview!.addSubview(scrollBarView) + } + + func updateItems(_ items: [ScrollItem], _ forceReloadVisible: Bool = false) { + if !Thread.isMainThread { + logger.error("Use main thread to update items") + return + } + if bounds.height == 0 { + self.listState.items = items + // this function requires to have valid bounds and it will be called again once it has them + return + } + adaptItems(items, forceReloadVisible) + snapToContent(animated: false) + } + + /// [forceReloadVisible]: reloads every item that was visible regardless of hashValue changes + private func adaptItems(_ items: [ScrollItem], _ forceReloadVisible: Bool, overridenOffset: CGFloat? = nil) { + let start = Date.now + // special case when everything was removed + if items.isEmpty { + listState.visibleItems.forEach { item in item.view.removeFromSuperview() } + listState.visibleItems = [] + listState.itemsCanCoverScreen = false + listState.firstVisibleItemId = EndlessScrollView.DEFAULT_ITEM_ID + listState.firstVisibleItemIndex = 0 + listState.firstVisibleItemOffset = -insetTop + + estimatedContentHeight.update(contentOffset, listState, averageItemHeight, true) + scrollBarView.contentSize = .zero + scrollBarView.contentOffset = .zero + + prevProcessedOffset = contentOffset.y + // this check is just to prevent didSet listener from firing on the same empty array, no use for this + if !self.listState.items.isEmpty { + self.listState.items = items + } + return + } + + let contentOffsetY = overridenOffset ?? contentOffset.y + + var oldVisible = listState.visibleItems + var newVisible: [VisibleItem] = [] + var visibleItemsHeight: CGFloat = 0 + let offsetsDiff = contentOffsetY - prevProcessedOffset + + var shouldBeFirstVisible = items.firstIndex(where: { item in item.id == listState.firstVisibleItemId as! ScrollItem.ID }) ?? 0 + + var wasFirstVisibleItemOffset = listState.firstVisibleItemOffset + var alreadyChangedIndexWhileScrolling = false + var allowOneMore = false + var nextOffsetY: CGFloat = 0 + var i = shouldBeFirstVisible + // building list of visible items starting from the first one that should be visible + while i >= 0 && i < items.count { + let item = items[i] + let visibleIndex = oldVisible.firstIndex(where: { vis in vis.item.id == item.id }) + let visible: VisibleItem? + if let visibleIndex { + let v = oldVisible.remove(at: visibleIndex) + if forceReloadVisible || v.view.bounds.width != bounds.width || v.item.hashValue != item.hashValue { + let wasHeight = v.view.bounds.height + updateCell(v.view, i, items) + if wasHeight < v.view.bounds.height && i == 0 && shouldBeFirstVisible == i { + v.view.frame.origin.y -= v.view.bounds.height - wasHeight + } + } + visible = v + } else { + visible = nil + } + if shouldBeFirstVisible == i { + if let vis = visible { + + if // there is auto scroll in progress and the first item has a higher offset than bottom part + // of the screen. In order to make scrolling down & up equal in time, we treat this as a sign to + // re-make the first visible item + (listState.isAnimatedScrolling && vis.view.frame.origin.y + vis.view.bounds.height < contentOffsetY + bounds.height) || + // the fist visible item previously is hidden now, remove it and move on + !isVisible(vis.view) { + let newIndex: Int + if listState.isAnimatedScrolling { + // skip many items to make the scrolling take less time + var indexDiff = !alreadyChangedIndexWhileScrolling ? Int(ceil(abs(offsetsDiff / averageItemHeight))) : 0 + // if index was already changed, no need to change it again. Otherwise, the scroll will overscoll and return back animated. Because it means the whole screen was scrolled + alreadyChangedIndexWhileScrolling = true + + indexDiff = offsetsDiff <= 0 ? indexDiff : -indexDiff + newIndex = max(0, min(items.count - 1, i + indexDiff)) + // offset for the first visible item can now be 0 because the previous first visible item doesn't exist anymore + wasFirstVisibleItemOffset = 0 + } else { + // don't skip multiple items if it's manual scrolling gesture + newIndex = i + (offsetsDiff <= 0 ? 1 : -1) + } + shouldBeFirstVisible = newIndex + i = newIndex + + cellsToReuse.append(vis.view) + hideAndRemoveFromSuperviewIfNeeded(vis.view) + continue + } + } + let vis: VisibleItem + if let visible { + vis = VisibleItem(index: i, item: item, view: visible.view, offset: offsetToBottom(visible.view)) + } else { + let cell = createCell(i, items, &cellsToReuse)! + cell.frame.origin.y = bottomY + wasFirstVisibleItemOffset - cell.frame.height + vis = VisibleItem(index: i, item: item, view: cell, offset: offsetToBottom(cell)) + } + if vis.view.superview == nil { + addSubview(vis.view) + } + newVisible.append(vis) + visibleItemsHeight += vis.view.frame.height + nextOffsetY = vis.view.frame.origin.y + } else { + let vis: VisibleItem + if let visible { + vis = VisibleItem(index: i, item: item, view: visible.view, offset: offsetToBottom(visible.view)) + nextOffsetY -= vis.view.frame.height + vis.view.frame.origin.y = nextOffsetY + } else { + let cell = createCell(i, items, &cellsToReuse)! + nextOffsetY -= cell.frame.height + cell.frame.origin.y = nextOffsetY + vis = VisibleItem(index: i, item: item, view: cell, offset: offsetToBottom(cell)) + } + if vis.view.superview == nil { + addSubview(vis.view) + } + newVisible.append(vis) + visibleItemsHeight += vis.view.frame.height + } + if abs(nextOffsetY) < contentOffsetY && !allowOneMore { + break + } else if abs(nextOffsetY) < contentOffsetY { + allowOneMore = false + } + i += 1 + } + if let firstVisible = newVisible.first, firstVisible.view.frame.origin.y + firstVisible.view.frame.height < contentOffsetY + bounds.height, firstVisible.index > 0 { + var offset: CGFloat = firstVisible.view.frame.origin.y + firstVisible.view.frame.height + let index = firstVisible.index + for i in stride(from: index - 1, through: 0, by: -1) { + let item = items[i] + let visibleIndex = oldVisible.firstIndex(where: { vis in vis.item.id == item.id }) + let vis: VisibleItem + if let visibleIndex { + let visible = oldVisible.remove(at: visibleIndex) + visible.view.frame.origin.y = offset + vis = VisibleItem(index: i, item: item, view: visible.view, offset: offsetToBottom(visible.view)) + } else { + let cell = createCell(i, items, &cellsToReuse)! + cell.frame.origin.y = offset + vis = VisibleItem(index: i, item: item, view: cell, offset: offsetToBottom(cell)) + } + if vis.view.superview == nil { + addSubview(vis.view) + } + offset += vis.view.frame.height + newVisible.insert(vis, at: 0) + visibleItemsHeight += vis.view.frame.height + if offset >= contentOffsetY + bounds.height { + break + } + } + } + + // removing already unneeded visible items + oldVisible.forEach { vis in + cellsToReuse.append(vis.view) + hideAndRemoveFromSuperviewIfNeeded(vis.view) + } + let itemsCountChanged = listState.items.count != items.count + prevProcessedOffset = contentOffsetY + + listState.visibleItems = newVisible + // bottom drawing starts from 0 until top visible area at least (bound.height - insetTop) or above top bar (bounds.height). + // For visible items to preserve offset after adding more items having such height is enough + listState.itemsCanCoverScreen = visibleItemsHeight >= bounds.height - insetTop + + listState.firstVisibleItemId = listState.visibleItems.first?.item.id ?? EndlessScrollView.DEFAULT_ITEM_ID + listState.firstVisibleItemIndex = listState.visibleItems.first?.index ?? 0 + listState.firstVisibleItemOffset = listState.visibleItems.first?.offset ?? -insetTop + // updating the items with the last step in order to call listener with fully updated state + listState.items = items + + estimatedContentHeight.update(contentOffset, listState, averageItemHeight, itemsCountChanged) + scrollBarView.contentSize = CGSizeMake(bounds.width, estimatedContentHeight.virtualOverscrolledHeight) + scrollBarView.contentOffset = CGPointMake(0, estimatedContentHeight.virtualScrollOffsetY) + scrollBarView.isHidden = listState.visibleItems.count == listState.items.count && (listState.visibleItems.isEmpty || -listState.firstVisibleItemOffset + (listState.visibleItems.last?.offset ?? 0) + insetTop < bounds.height) + + if debug { + println("time spent \((-start.timeIntervalSinceNow).description.prefix(5).replacingOccurrences(of: "0.000", with: "<0").replacingOccurrences(of: "0.", with: ""))") + } + } + + func setScrollPosition(_ index: Int, _ id: Int64, _ offset: CGFloat = 0) { + listState.firstVisibleItemIndex = index + listState.firstVisibleItemId = id + listState.firstVisibleItemOffset = offset == 0 ? -bounds.height + insetTop + insetBottom : offset + } + + func scrollToItem(_ index: Int, top: Bool = true) { + if index >= listState.items.count || listState.isScrolling || listState.isAnimatedScrolling { + return + } + if bounds.height == 0 || contentSize.height == 0 { + scrollToItemIndexDelayed = index + return + } + listState.isScrolling = true + defer { + listState.isScrolling = false + } + + // just a faster way to set top item as requested index + listState.firstVisibleItemIndex = index + listState.firstVisibleItemId = listState.items[index].id + listState.firstVisibleItemOffset = -bounds.height + insetTop + insetBottom + scrollBarView.flashScrollIndicators() + adaptItems(listState.items, false) + + var adjustedOffset = self.contentOffset.y + var i = 0 + + var upPrev = index > listState.firstVisibleItemIndex + //let firstOrLastIndex = upPrev ? listState.visibleItems.last?.index ?? 0 : listState.firstVisibleItemIndex + //let step: CGFloat = max(0.1, CGFloat(abs(index - firstOrLastIndex)) * scrollStepMultiplier) + + var stepSlowdownMultiplier: CGFloat = 1 + while i < 200 { + let up = index > listState.firstVisibleItemIndex + if upPrev != up { + stepSlowdownMultiplier = stepSlowdownMultiplier * 0.5 + upPrev = up + } + + // these two lines makes scrolling's finish non-linear and NOT overscroll visually when reach target index + let firstOrLastIndex = up ? listState.visibleItems.last?.index ?? 0 : listState.firstVisibleItemIndex + let step: CGFloat = max(0.1, CGFloat(abs(index - firstOrLastIndex)) * scrollStepMultiplier) * stepSlowdownMultiplier + + let offsetToScroll = (up ? -averageItemHeight : averageItemHeight) * step + adjustedOffset += offsetToScroll + if let item = listState.visibleItems.first(where: { $0.index == index }) { + let y = if top { + min(estimatedContentHeight.bottomOffsetY - bounds.height, item.view.frame.origin.y - insetTop) + } else { + max(estimatedContentHeight.topOffsetY - insetTop - insetBottom, item.view.frame.origin.y + item.view.bounds.height - bounds.height + insetBottom) + } + setContentOffset(CGPointMake(contentOffset.x, y), animated: false) + scrollBarView.flashScrollIndicators() + break + } + contentOffset = CGPointMake(contentOffset.x, adjustedOffset) + adaptItems(listState.items, false) + snapToContent(animated: false) + i += 1 + } + adaptItems(listState.items, false) + snapToContent(animated: false) + estimatedContentHeight.update(contentOffset, listState, averageItemHeight, true) + } + + func scrollToItemAnimated(_ index: Int, top: Bool = true) async { + if index >= listState.items.count || listState.isScrolling || listState.isAnimatedScrolling { + return + } + listState.isAnimatedScrolling = true + defer { + listState.isAnimatedScrolling = false + } + var adjustedOffset = self.contentOffset.y + var i = 0 + + var upPrev = index > listState.firstVisibleItemIndex + //let firstOrLastIndex = upPrev ? listState.visibleItems.last?.index ?? 0 : listState.firstVisibleItemIndex + //let step: CGFloat = max(0.1, CGFloat(abs(index - firstOrLastIndex)) * scrollStepMultiplier) + + var stepSlowdownMultiplier: CGFloat = 1 + while i < 200 { + let up = index > listState.firstVisibleItemIndex + if upPrev != up { + stepSlowdownMultiplier = stepSlowdownMultiplier * 0.5 + upPrev = up + } + + // these two lines makes scrolling's finish non-linear and NOT overscroll visually when reach target index + let firstOrLastIndex = up ? listState.visibleItems.last?.index ?? 0 : listState.firstVisibleItemIndex + let step: CGFloat = max(0.1, CGFloat(abs(index - firstOrLastIndex)) * scrollStepMultiplier) * stepSlowdownMultiplier + + //println("Scrolling step \(step) \(stepSlowdownMultiplier) index \(index) \(firstOrLastIndex) \(index - firstOrLastIndex) \(adjustedOffset), up \(up), i \(i)") + + let offsetToScroll = (up ? -averageItemHeight : averageItemHeight) * step + adjustedOffset += offsetToScroll + if let item = listState.visibleItems.first(where: { $0.index == index }) { + let y = if top { + min(estimatedContentHeight.bottomOffsetY - bounds.height, item.view.frame.origin.y - insetTop) + } else { + max(estimatedContentHeight.topOffsetY - insetTop - insetBottom, item.view.frame.origin.y + item.view.bounds.height - bounds.height + insetBottom) + } + setContentOffset(CGPointMake(contentOffset.x, y), animated: true) + scrollBarView.flashScrollIndicators() + break + } + contentOffset = CGPointMake(contentOffset.x, adjustedOffset) + + // skipping unneded relayout if this offset is already processed + if prevProcessedOffset - contentOffset.y != 0 { + adaptItems(listState.items, false) + snapToContent(animated: false) + } + // let UI time to update to see the animated position change + await MainActor.run {} + + i += 1 + } + estimatedContentHeight.update(contentOffset, listState, averageItemHeight, true) + } + + func scrollToBottom() { + scrollToItem(0, top: false) + } + + func scrollToBottomAnimated() { + Task { + await scrollToItemAnimated(0, top: false) + } + } + + func scroll(by: CGFloat, animated: Bool = true) { + setContentOffset(CGPointMake(contentOffset.x, contentOffset.y + by), animated: animated) + } + + func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { + if !listState.items.isEmpty { + scrollToBottomAnimated() + } + return false + } + + private func snapToContent(animated: Bool) { + let topBlankSpace = estimatedContentHeight.height < bounds.height ? bounds.height - estimatedContentHeight.height : 0 + if topY < estimatedContentHeight.topOffsetY - topBlankSpace { + setContentOffset(CGPointMake(0, estimatedContentHeight.topOffsetY - topBlankSpace), animated: animated) + } else if bottomY > estimatedContentHeight.bottomOffsetY { + setContentOffset(CGPointMake(0, estimatedContentHeight.bottomOffsetY - bounds.height), animated: animated) + } + } + + func offsetToBottom(_ view: UIView) -> CGFloat { + bottomY - (view.frame.origin.y + view.frame.height) + } + + /// If I try to .removeFromSuperview() right when I need to remove the view, it is possible to crash the app when the view was hidden in result of + /// pressing Hide in menu on top of the revealed item within the group. So at that point the item should still be attached to the view + func hideAndRemoveFromSuperviewIfNeeded(_ view: UIView) { + if view.isHidden { + // already passed this function + return + } + (view as? ReusableView)?.prepareForReuse() + view.isHidden = true + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + if view.isHidden { view.removeFromSuperview() } + } + } + + /// Synchronizing both scrollViews + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + true + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + if !decelerate { + snapToContent(animated: true) + } + } + + override var contentOffset: CGPoint { + get { super.contentOffset } + set { + var newOffset = newValue + let topBlankSpace = estimatedContentHeight.height < bounds.height ? bounds.height - estimatedContentHeight.height : 0 + if contentOffset.y > 0 && newOffset.y < estimatedContentHeight.topOffsetY - topBlankSpace && contentOffset.y > newOffset.y { + if !isDecelerating { + newOffset.y = min(contentOffset.y, newOffset.y + abs(newOffset.y - estimatedContentHeight.topOffsetY + topBlankSpace) / 1.8) + } else { + DispatchQueue.main.async { + self.setContentOffset(newValue, animated: false) + self.snapToContent(animated: true) + } + } + } else if contentOffset.y > 0 && newOffset.y + bounds.height > estimatedContentHeight.bottomOffsetY && contentOffset.y < newOffset.y { + if !isDecelerating { + newOffset.y = max(contentOffset.y, newOffset.y - abs(newOffset.y + bounds.height - estimatedContentHeight.bottomOffsetY) / 1.8) + } else { + DispatchQueue.main.async { + self.setContentOffset(newValue, animated: false) + self.snapToContent(animated: true) + } + } + } + super.contentOffset = newOffset + } + } + + private func stopScrolling() { + let offsetYToStopAt = if abs(contentOffset.y - estimatedContentHeight.topOffsetY) < abs(bottomY - estimatedContentHeight.bottomOffsetY) { + estimatedContentHeight.topOffsetY + } else { + estimatedContentHeight.bottomOffsetY - bounds.height + } + setContentOffset(CGPointMake(contentOffset.x, offsetYToStopAt), animated: false) + } + + func isVisible(_ view: UIView) -> Bool { + if view.superview == nil { + return false + } + return view.frame.intersects(CGRectMake(0, contentOffset.y, bounds.width, bounds.height)) + } +} + +private func println(_ text: String) { + print("\(Date.now.timeIntervalSince1970): \(text)") +} diff --git a/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift b/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift index b89c006c61..7cd543af10 100644 --- a/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift +++ b/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift @@ -21,6 +21,7 @@ struct AddGroupMembersView: View { struct AddGroupMembersViewCommon: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme var chat: Chat @State var groupInfo: GroupInfo var creatingGroup: Bool = false @@ -34,7 +35,7 @@ struct AddGroupMembersViewCommon: View { private enum AddGroupMembersAlert: Identifiable { case prohibitedToInviteIncognito - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -46,14 +47,13 @@ struct AddGroupMembersViewCommon: View { var body: some View { if creatingGroup { - NavigationView { - addGroupMembersView() - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button ("Skip") { addedMembersCb(selectedContacts) } - } + addGroupMembersView() + .navigationBarBackButtonHidden() + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button ("Skip") { addedMembersCb(selectedContacts) } } - } + } } else { addGroupMembersView() } @@ -70,7 +70,7 @@ struct AddGroupMembersViewCommon: View { if (membersToAdd.isEmpty) { Text("No contacts to add") - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .padding() .frame(maxWidth: .infinity, alignment: .center) .listRowBackground(Color.clear) @@ -78,7 +78,12 @@ struct AddGroupMembersViewCommon: View { let count = selectedContacts.count Section { if creatingGroup { - groupPreferencesButton($groupInfo, true) + GroupPreferencesButton( + groupInfo: $groupInfo, + preferences: groupInfo.fullGroupPreferences, + currentPreferences: groupInfo.fullGroupPreferences, + creatingGroup: true + ) } rolePicker() inviteMembersButton() @@ -90,21 +95,25 @@ struct AddGroupMembersViewCommon: View { Button { selectedContacts.removeAll() } label: { Text("Clear").font(.caption) } Spacer() Text("\(count) contact(s) selected") + .foregroundColor(theme.colors.secondary) } } else { Text("No contacts selected") .frame(maxWidth: .infinity, alignment: .trailing) + .foregroundColor(theme.colors.secondary) } } } Section { - searchFieldView(text: $searchText, focussed: $searchFocussed) + searchFieldView(text: $searchText, focussed: $searchFocussed, theme.colors.primary, theme.colors.secondary) .padding(.leading, 2) let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase let members = s == "" ? membersToAdd : membersToAdd.filter { $0.chatViewName.localizedLowercase.contains(s) } - ForEach(members) { contact in - contactCheckView(contact) + ForEach(members + [dummyContact]) { contact in + if contact.contactId != dummyContact.contactId { + contactCheckView(contact) + } } } } @@ -119,20 +128,30 @@ struct AddGroupMembersViewCommon: View { message: Text("You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile") ) case let .error(title, error): - return Alert(title: Text(title), message: Text(error)) + return mkAlert(title: title, message: error) } } .onChange(of: selectedContacts) { _ in searchFocussed = false } + .modifier(ThemedBackground(grouped: true)) } + // Resolves keyboard losing focus bug in iOS16 and iOS17, + // when there are no items inside `ForEach(memebers)` loop + private let dummyContact: Contact = { + var dummy = Contact.sampleData + dummy.contactId = -1 + return dummy + }() + private func inviteMembersButton() -> some View { - Button { + let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Invite to group" : "Invite to chat" + return Button { inviteMembers() } label: { HStack { - Text("Invite to group") + Text(label) Image(systemName: "checkmark") } } @@ -156,10 +175,8 @@ struct AddGroupMembersViewCommon: View { private func rolePicker() -> some View { Picker("New member role", selection: $selectedRole) { - ForEach(GroupMemberRole.allCases) { role in - if role <= groupInfo.membership.memberRole && role != .author { - Text(role.text) - } + ForEach(GroupMemberRole.supportedRoles.filter({ $0 <= groupInfo.membership.memberRole })) { role in + Text(role.text) } } .frame(height: 36) @@ -172,14 +189,14 @@ struct AddGroupMembersViewCommon: View { var iconColor: Color if prohibitedToInviteIncognito { icon = "theatermasks.circle.fill" - iconColor = Color(uiColor: .tertiaryLabel) + iconColor = Color(uiColor: .tertiaryLabel).asAnotherColorFromSecondary(theme) } else { if checked { icon = "checkmark.circle.fill" - iconColor = .accentColor + iconColor = theme.colors.primary } else { icon = "circle" - iconColor = Color(uiColor: .tertiaryLabel) + iconColor = Color(uiColor: .tertiaryLabel).asAnotherColorFromSecondary(theme) } } return Button { @@ -194,11 +211,10 @@ struct AddGroupMembersViewCommon: View { } } label: { HStack{ - ProfileImage(imageStr: contact.image) - .frame(width: 30, height: 30) + ProfileImage(imageStr: contact.image, size: 30) .padding(.trailing, 2) Text(ChatInfo.direct(contact: contact).chatViewName) - .foregroundColor(prohibitedToInviteIncognito ? .secondary : .primary) + .foregroundColor(prohibitedToInviteIncognito ? theme.colors.secondary : theme.colors.onBackground) .lineLimit(1) Spacer() Image(systemName: icon) @@ -208,7 +224,7 @@ struct AddGroupMembersViewCommon: View { } } -func searchFieldView(text: Binding, focussed: FocusState.Binding) -> some View { +func searchFieldView(text: Binding, focussed: FocusState.Binding, _ onBackgroundColor: Color, _ secondaryColor: Color) -> some View { HStack { Image(systemName: "magnifyingglass") .resizable() @@ -217,8 +233,9 @@ func searchFieldView(text: Binding, focussed: FocusState.Binding) .padding(.trailing, 10) TextField("Search", text: text) .focused(focussed) - .foregroundColor(.primary) + .foregroundColor(onBackgroundColor) .frame(maxWidth: .infinity) + .autocorrectionDisabled(true) Image(systemName: "xmark.circle.fill") .resizable() .scaledToFit() @@ -229,7 +246,7 @@ func searchFieldView(text: Binding, focussed: FocusState.Binding) focussed.wrappedValue = false } } - .foregroundColor(.secondary) + .foregroundColor(secondaryColor) .frame(height: 36) } diff --git a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift index 88b36077b4..15749b0761 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift @@ -13,17 +13,23 @@ let SMALL_GROUPS_RCPS_MEM_LIMIT: Int = 20 struct GroupChatInfoView: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme @Environment(\.dismiss) var dismiss: DismissAction @ObservedObject var chat: Chat @Binding var groupInfo: GroupInfo + var onSearch: () -> Void + @State var localAlias: String + @FocusState private var aliasTextFieldFocused: Bool @State private var alert: GroupChatInfoViewAlert? = nil - @State private var groupLink: String? + @State private var groupLink: CreatedConnLink? @State private var groupLinkMemberRole: GroupMemberRole = .member - @State private var showAddMembersSheet: Bool = false + @State private var groupLinkNavLinkActive: Bool = false + @State private var addMembersNavLinkActive: Bool = false @State private var connectionStats: ConnectionStats? @State private var connectionCode: String? @State private var sendReceipts = SendReceipts.userDefault(true) @State private var sendReceiptsUserDefault = true + @State private var progressIndicator = false @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false @State private var searchText: String = "" @FocusState private var searchFocussed @@ -39,7 +45,7 @@ struct GroupChatInfoView: View { case blockForAllAlert(mem: GroupMember) case unblockForAllAlert(mem: GroupMember) case removeMemberAlert(mem: GroupMember) - case error(title: LocalizedStringKey, error: LocalizedStringKey) + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -62,80 +68,114 @@ struct GroupChatInfoView: View { NavigationView { let members = chatModel.groupMembers .filter { m in let status = m.wrapped.memberStatus; return status != .memLeft && status != .memRemoved } - .sorted { $0.displayName.lowercased() < $1.displayName.lowercased() } + .sorted { $0.wrapped.memberRole > $1.wrapped.memberRole } - List { - groupInfoHeader() - .listRowBackground(Color.clear) - - Section { - if groupInfo.canEdit { - editGroupButton() - } - if groupInfo.groupProfile.description != nil || groupInfo.canEdit { - addOrEditWelcomeMessage() - } - groupPreferencesButton($groupInfo) - if members.filter({ $0.wrapped.memberCurrent }).count <= SMALL_GROUPS_RCPS_MEM_LIMIT { - sendReceiptsOption() - } else { - sendReceiptsOptionDisabled() - } - } header: { - Text("") - } footer: { - Text("Only group owners can change group preferences.") - } - - Section("\(members.count + 1) members") { - if groupInfo.canAddMembers { - groupLinkButton() - if (chat.chatInfo.incognito) { - Label("Invite members", systemImage: "plus") - .foregroundColor(Color(uiColor: .tertiaryLabel)) - .onTapGesture { alert = .cantInviteIncognitoAlert } + ZStack { + List { + groupInfoHeader() + .listRowBackground(Color.clear) + + localAliasTextEdit() + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .padding(.bottom, 18) + + infoActionButtons() + .padding(.horizontal) + .frame(maxWidth: .infinity) + .frame(height: infoViewActionButtonHeight) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + + Section { + if groupInfo.isOwner && groupInfo.businessChat == nil { + editGroupButton() + } + if groupInfo.groupProfile.description != nil || (groupInfo.isOwner && groupInfo.businessChat == nil) { + addOrEditWelcomeMessage() + } + GroupPreferencesButton(groupInfo: $groupInfo, preferences: groupInfo.fullGroupPreferences, currentPreferences: groupInfo.fullGroupPreferences) + if members.filter({ $0.wrapped.memberCurrent }).count <= SMALL_GROUPS_RCPS_MEM_LIMIT { + sendReceiptsOption() } else { - addMembersButton() + sendReceiptsOptionDisabled() } + + NavigationLink { + ChatWallpaperEditorSheet(chat: chat) + } label: { + Label("Chat theme", systemImage: "photo") + } + } header: { + Text("") + } footer: { + let label: LocalizedStringKey = ( + groupInfo.businessChat == nil + ? "Only group owners can change group preferences." + : "Only chat owners can change preferences." + ) + Text(label) + .foregroundColor(theme.colors.secondary) } - if members.count > 8 { - searchFieldView(text: $searchText, focussed: $searchFocussed) - .padding(.leading, 8) + + Section { + ChatTTLOption(chat: chat, progressIndicator: $progressIndicator) + } footer: { + Text("Delete chat messages from your device.") } - let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase - let filteredMembers = s == "" ? members : members.filter { $0.wrapped.chatViewName.localizedLowercase.contains(s) } - MemberRowView(groupInfo: groupInfo, groupMember: GMember(groupInfo.membership), user: true, alert: $alert) - ForEach(filteredMembers) { member in - ZStack { - NavigationLink { - memberInfoView(member) - } label: { - EmptyView() + + Section(header: Text("\(members.count + 1) members").foregroundColor(theme.colors.secondary)) { + if groupInfo.canAddMembers { + if groupInfo.businessChat == nil { + groupLinkButton() } - .opacity(0) - MemberRowView(groupInfo: groupInfo, groupMember: member, alert: $alert) + if (chat.chatInfo.incognito) { + Label("Invite members", systemImage: "plus") + .foregroundColor(Color(uiColor: .tertiaryLabel)) + .onTapGesture { alert = .cantInviteIncognitoAlert } + } else { + addMembersButton() + } + } + searchFieldView(text: $searchText, focussed: $searchFocussed, theme.colors.onBackground, theme.colors.secondary) + .padding(.leading, 8) + let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase + let filteredMembers = s == "" + ? members + : members.filter { $0.wrapped.localAliasAndFullName.localizedLowercase.contains(s) } + MemberRowView(chat: chat, groupInfo: groupInfo, groupMember: GMember(groupInfo.membership), user: true, alert: $alert) + ForEach(filteredMembers) { member in + MemberRowView(chat: chat, groupInfo: groupInfo, groupMember: member, alert: $alert) + } + } + + Section { + clearChatButton() + if groupInfo.canDelete { + deleteGroupButton() + } + if groupInfo.membership.memberCurrent { + leaveGroupButton() + } + } + + if developerTools { + Section(header: Text("For console").foregroundColor(theme.colors.secondary)) { + infoRow("Local name", chat.chatInfo.localDisplayName) + infoRow("Database ID", "\(chat.chatInfo.apiId)") } } } - - Section { - clearChatButton() - if groupInfo.canDelete { - deleteGroupButton() - } - if groupInfo.membership.memberCurrent { - leaveGroupButton() - } - } - - if developerTools { - Section(header: Text("For console")) { - infoRow("Local name", chat.chatInfo.localDisplayName) - infoRow("Database ID", "\(chat.chatInfo.apiId)") - } + .modifier(ThemedBackground(grouped: true)) + .navigationBarHidden(true) + .disabled(progressIndicator) + .opacity(progressIndicator ? 0.6 : 1) + + if progressIndicator { + ProgressView().scaleEffect(2) } } - .navigationBarHidden(true) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) .alert(item: $alert) { alertItem in @@ -150,7 +190,7 @@ struct GroupChatInfoView: View { case let .blockForAllAlert(mem): return blockForAllAlert(groupInfo, mem) case let .unblockForAllAlert(mem): return unblockForAllAlert(groupInfo, mem) case let .removeMemberAlert(mem): return removeMemberAlert(mem) - case let .error(title, error): return Alert(title: Text(title), message: Text(error)) + case let .error(title, error): return mkAlert(title: title, message: error) } } .onAppear { @@ -166,17 +206,15 @@ struct GroupChatInfoView: View { logger.error("GroupChatInfoView apiGetGroupLink: \(responseError(error))") } } - .keyboardPadding() } private func groupInfoHeader() -> some View { VStack { let cInfo = chat.chatInfo - ChatInfoImage(chat: chat, color: Color(uiColor: .tertiarySystemFill)) - .frame(width: 192, height: 192) + ChatInfoImage(chat: chat, size: 192, color: Color(uiColor: .tertiarySystemFill)) .padding(.top, 12) .padding() - Text(cInfo.displayName) + Text(cInfo.groupInfo?.groupProfile.displayName ?? cInfo.displayName) .font(.largeTitle) .multilineTextAlignment(.center) .lineLimit(4) @@ -191,50 +229,167 @@ struct GroupChatInfoView: View { .frame(maxWidth: .infinity, alignment: .center) } - private func addMembersButton() -> some View { - NavigationLink { - AddGroupMembersView(chat: chat, groupInfo: groupInfo) - .onAppear { - searchFocussed = false - Task { - let groupMembers = await apiListMembers(groupInfo.groupId) - await MainActor.run { - chatModel.groupMembers = groupMembers.map { GMember.init($0) } - } + private func localAliasTextEdit() -> some View { + TextField("Set chat name…", text: $localAlias) + .disableAutocorrection(true) + .focused($aliasTextFieldFocused) + .submitLabel(.done) + .onChange(of: aliasTextFieldFocused) { focused in + if !focused { + setGroupAlias() + } + } + .onSubmit { + setGroupAlias() + } + .multilineTextAlignment(.center) + .foregroundColor(theme.colors.secondary) + } + + private func setGroupAlias() { + Task { + do { + if let gInfo = try await apiSetGroupAlias(groupId: chat.chatInfo.apiId, localAlias: localAlias) { + await MainActor.run { + chatModel.updateGroup(gInfo) } } - } label: { - Label("Invite members", systemImage: "plus") + } catch { + logger.error("setGroupAlias error: \(responseError(error))") + } + } + } + + func infoActionButtons() -> some View { + GeometryReader { g in + let buttonWidth = g.size.width / 4 + HStack(alignment: .center, spacing: 8) { + searchButton(width: buttonWidth) + if groupInfo.canAddMembers { + addMembersActionButton(width: buttonWidth) + } + if let nextNtfMode = chat.chatInfo.nextNtfMode { + muteButton(width: buttonWidth, nextNtfMode: nextNtfMode) + } + } + .frame(maxWidth: .infinity, alignment: .center) } } + private func searchButton(width: CGFloat) -> some View { + InfoViewButton(image: "magnifyingglass", title: "search", width: width) { + dismiss() + onSearch() + } + .disabled(!groupInfo.ready || chat.chatItems.isEmpty) + } + + private func addMembersActionButton(width: CGFloat) -> some View { + ZStack { + if chat.chatInfo.incognito { + InfoViewButton(image: "link.badge.plus", title: "invite", width: width) { + groupLinkNavLinkActive = true + } + + NavigationLink(isActive: $groupLinkNavLinkActive) { + groupLinkDestinationView() + } label: { + EmptyView() + } + .frame(width: 1, height: 1) + .hidden() + } else { + InfoViewButton(image: "person.fill.badge.plus", title: "invite", width: width) { + addMembersNavLinkActive = true + } + + NavigationLink(isActive: $addMembersNavLinkActive) { + addMembersDestinationView() + } label: { + EmptyView() + } + .frame(width: 1, height: 1) + .hidden() + } + } + .disabled(!groupInfo.ready) + } + + private func muteButton(width: CGFloat, nextNtfMode: MsgFilter) -> some View { + return InfoViewButton( + image: nextNtfMode.iconFilled, + title: "\(nextNtfMode.text(mentions: true))", + width: width + ) { + toggleNotifications(chat, enableNtfs: nextNtfMode) + } + .disabled(!groupInfo.ready) + } + + private func addMembersButton() -> some View { + let label: LocalizedStringKey = switch groupInfo.businessChat?.chatType { + case .customer: "Add team members" + case .business: "Add friends" + case .none: "Invite members" + } + return NavigationLink { + addMembersDestinationView() + } label: { + Label(label, systemImage: "plus") + } + } + + private func addMembersDestinationView() -> some View { + AddGroupMembersView(chat: chat, groupInfo: groupInfo) + .onAppear { + searchFocussed = false + Task { + await chatModel.loadGroupMembers(groupInfo) + } + } + } + private struct MemberRowView: View { + var chat: Chat var groupInfo: GroupInfo @ObservedObject var groupMember: GMember + @EnvironmentObject var theme: AppTheme var user: Bool = false @Binding var alert: GroupChatInfoViewAlert? var body: some View { let member = groupMember.wrapped - let v = HStack{ - ProfileImage(imageStr: member.image) - .frame(width: 38, height: 38) + let v1 = HStack{ + MemberProfileImage(member, size: 38) .padding(.trailing, 2) // TODO server connection status VStack(alignment: .leading) { - let t = Text(member.chatViewName).foregroundColor(member.memberIncognito ? .indigo : .primary) + let t = Text(member.chatViewName).foregroundColor(member.memberIncognito ? .indigo : theme.colors.onBackground) (member.verified ? memberVerifiedShield + t : t) .lineLimit(1) - let s = Text(member.memberStatus.shortText) - (user ? Text ("you: ") + s : s) + (user ? Text ("you: ") + Text(member.memberStatus.shortText) : Text(memberConnStatus(member))) .lineLimit(1) .font(.caption) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } Spacer() memberInfo(member) } - + + let v = ZStack { + if user { + v1 + } else { + NavigationLink { + memberInfoView() + } label: { + EmptyView() + } + .opacity(0) + v1 + } + } + if user { v } else if groupInfo.membership.memberRole >= .admin { @@ -259,15 +414,30 @@ struct GroupChatInfoView: View { } } + private func memberInfoView() -> some View { + GroupMemberInfoView(groupInfo: groupInfo, chat: chat, groupMember: groupMember) + .navigationBarHidden(false) + } + + private func memberConnStatus(_ member: GroupMember) -> LocalizedStringKey { + if member.activeConn?.connDisabled ?? false { + return "disabled" + } else if member.activeConn?.connInactive ?? false { + return "inactive" + } else { + return member.memberStatus.shortText + } + } + @ViewBuilder private func memberInfo(_ member: GroupMember) -> some View { if member.blocked { Text("blocked") - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } else { let role = member.memberRole if [.owner, .admin, .observer].contains(role) { Text(member.memberRole.text) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } } } @@ -278,13 +448,13 @@ struct GroupChatInfoView: View { Button { alert = .blockMemberAlert(mem: member) } label: { - Label("Block member", systemImage: "hand.raised").foregroundColor(.secondary) + Label("Block member", systemImage: "hand.raised").foregroundColor(theme.colors.secondary) } } else { Button { alert = .unblockMemberAlert(mem: member) } label: { - Label("Unblock member", systemImage: "hand.raised.slash").foregroundColor(.accentColor) + Label("Unblock member", systemImage: "hand.raised.slash").foregroundColor(theme.colors.primary) } } } @@ -296,13 +466,13 @@ struct GroupChatInfoView: View { Button { alert = .unblockForAllAlert(mem: member) } label: { - Label("Unblock for all", systemImage: "hand.raised.slash").foregroundColor(.accentColor) + Label("Unblock for all", systemImage: "hand.raised.slash").foregroundColor(theme.colors.primary) } } else { Button { alert = .blockForAllAlert(mem: member) } label: { - Label("Block for all", systemImage: "hand.raised").foregroundColor(.secondary) + Label("Block for all", systemImage: "hand.raised").foregroundColor(theme.colors.secondary) } } } @@ -318,24 +488,19 @@ struct GroupChatInfoView: View { } } } - } - private func memberInfoView(_ groupMember: GMember) -> some View { - GroupMemberInfoView(groupInfo: groupInfo, groupMember: groupMember) - .navigationBarHidden(false) + private var memberVerifiedShield: Text { + (Text(Image(systemName: "checkmark.shield")) + textSpace) + .font(.caption) + .baselineOffset(2) + .kerning(-2) + .foregroundColor(theme.colors.secondary) + } } private func groupLinkButton() -> some View { NavigationLink { - GroupLinkView( - groupId: groupInfo.groupId, - groupLink: $groupLink, - groupLinkMemberRole: $groupLinkMemberRole, - showTitle: false, - creatingGroup: false - ) - .navigationBarTitle("Group link") - .navigationBarTitleDisplayMode(.large) + groupLinkDestinationView() } label: { if groupLink == nil { Label("Create group link", systemImage: "link.badge.plus") @@ -345,6 +510,19 @@ struct GroupChatInfoView: View { } } + private func groupLinkDestinationView() -> some View { + GroupLinkView( + groupId: groupInfo.groupId, + groupLink: $groupLink, + groupLinkMemberRole: $groupLinkMemberRole, + showTitle: false, + creatingGroup: false + ) + .navigationBarTitle("Group link") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.large) + } + private func editGroupButton() -> some View { NavigationLink { GroupProfileView( @@ -352,6 +530,7 @@ struct GroupChatInfoView: View { groupProfile: groupInfo.groupProfile ) .navigationBarTitle("Group profile") + .modifier(ThemedBackground()) .navigationBarTitleDisplayMode(.large) } label: { Label("Edit group profile", systemImage: "pencil") @@ -366,6 +545,7 @@ struct GroupChatInfoView: View { welcomeText: groupInfo.groupProfile.description ?? "" ) .navigationTitle("Welcome message") + .modifier(ThemedBackground(grouped: true)) .navigationBarTitleDisplayMode(.large) } label: { groupInfo.groupProfile.description == nil @@ -374,11 +554,12 @@ struct GroupChatInfoView: View { } } - private func deleteGroupButton() -> some View { + @ViewBuilder private func deleteGroupButton() -> some View { + let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Delete group" : "Delete chat" Button(role: .destructive) { alert = .deleteGroupAlert } label: { - Label("Delete group", systemImage: "trash") + Label(label, systemImage: "trash") .foregroundColor(Color.red) } } @@ -393,19 +574,21 @@ struct GroupChatInfoView: View { } private func leaveGroupButton() -> some View { - Button(role: .destructive) { + let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Leave group" : "Leave chat" + return Button(role: .destructive) { alert = .leaveGroupAlert } label: { - Label("Leave group", systemImage: "rectangle.portrait.and.arrow.right") + Label(label, systemImage: "rectangle.portrait.and.arrow.right") .foregroundColor(Color.red) } } // TODO reuse this and clearChatAlert with ChatInfoView private func deleteGroupAlert() -> Alert { + let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Delete group?" : "Delete chat?" return Alert( - title: Text("Delete group?"), - message: deleteGroupAlertMessage(), + title: Text(label), + message: deleteGroupAlertMessage(groupInfo), primaryButton: .destructive(Text("Delete")) { Task { do { @@ -424,10 +607,6 @@ struct GroupChatInfoView: View { ) } - private func deleteGroupAlertMessage() -> Text { - groupInfo.membership.memberCurrent ? Text("Group will be deleted for all members - this cannot be undone!") : Text("Group will be deleted for you - this cannot be undone!") - } - private func clearChatAlert() -> Alert { Alert( title: Text("Clear conversation?"), @@ -443,9 +622,15 @@ struct GroupChatInfoView: View { } private func leaveGroupAlert() -> Alert { - Alert( - title: Text("Leave group?"), - message: Text("You will stop receiving messages from this group. Chat history will be preserved."), + let titleLabel: LocalizedStringKey = groupInfo.businessChat == nil ? "Leave group?" : "Leave chat?" + let messageLabel: LocalizedStringKey = ( + groupInfo.businessChat == nil + ? "You will stop receiving messages from this group. Chat history will be preserved." + : "You will stop receiving messages from this chat. Chat history will be preserved." + ) + return Alert( + title: Text(titleLabel), + message: Text(messageLabel), primaryButton: .destructive(Text("Leave")) { Task { await leaveGroup(chat.chatInfo.apiId) @@ -489,18 +674,25 @@ struct GroupChatInfoView: View { } private func removeMemberAlert(_ mem: GroupMember) -> Alert { - Alert( + let messageLabel: LocalizedStringKey = ( + groupInfo.businessChat == nil + ? "Member will be removed from group - this cannot be undone!" + : "Member will be removed from chat - this cannot be undone!" + ) + return Alert( title: Text("Remove member?"), - message: Text("Member will be removed from group - this cannot be undone!"), + message: Text(messageLabel), primaryButton: .destructive(Text("Remove")) { Task { do { - let updatedMember = try await apiRemoveMember(groupInfo.groupId, mem.groupMemberId) + let updatedMembers = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId]) await MainActor.run { - _ = chatModel.upsertGroupMember(groupInfo, updatedMember) + updatedMembers.forEach { updatedMember in + _ = chatModel.upsertGroupMember(groupInfo, updatedMember) + } } } catch let error { - logger.error("apiRemoveMember error: \(responseError(error))") + logger.error("apiRemoveMembers error: \(responseError(error))") let a = getErrorAlert(error, "Error removing member") alert = .error(title: a.title, error: a.message) } @@ -511,33 +703,80 @@ struct GroupChatInfoView: View { } } -func groupPreferencesButton(_ groupInfo: Binding, _ creatingGroup: Bool = false) -> some View { - NavigationLink { - GroupPreferencesView( - groupInfo: groupInfo, - preferences: groupInfo.wrappedValue.fullGroupPreferences, - currentPreferences: groupInfo.wrappedValue.fullGroupPreferences, - creatingGroup: creatingGroup - ) - .navigationBarTitle("Group preferences") - .navigationBarTitleDisplayMode(.large) - } label: { - if creatingGroup { - Text("Set group preferences") - } else { - Label("Group preferences", systemImage: "switch.2") - } - } +func deleteGroupAlertMessage(_ groupInfo: GroupInfo) -> Text { + groupInfo.businessChat == nil ? ( + groupInfo.membership.memberCurrent ? Text("Group will be deleted for all members - this cannot be undone!") : Text("Group will be deleted for you - this cannot be undone!") + ) : ( + groupInfo.membership.memberCurrent ? Text("Chat will be deleted for all members - this cannot be undone!") : Text("Chat will be deleted for you - this cannot be undone!") + ) } -private var memberVerifiedShield: Text { - (Text(Image(systemName: "checkmark.shield")) + Text(" ")) - .font(.caption) - .baselineOffset(2) - .kerning(-2) - .foregroundColor(.secondary) +struct GroupPreferencesButton: View { + @Binding var groupInfo: GroupInfo + @State var preferences: FullGroupPreferences + @State var currentPreferences: FullGroupPreferences + var creatingGroup: Bool = false + + private var label: LocalizedStringKey { + groupInfo.businessChat == nil ? "Group preferences" : "Chat preferences" + } + + var body: some View { + NavigationLink { + GroupPreferencesView( + groupInfo: $groupInfo, + preferences: $preferences, + currentPreferences: currentPreferences, + creatingGroup: creatingGroup, + savePreferences: savePreferences + ) + .navigationBarTitle(label) + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.large) + .onDisappear { + let saveText = NSLocalizedString( + creatingGroup ? "Save" : "Save and notify group members", + comment: "alert button" + ) + + if groupInfo.fullGroupPreferences != preferences { + showAlert( + title: NSLocalizedString("Save preferences?", comment: "alert title"), + buttonTitle: saveText, + buttonAction: { savePreferences() }, + cancelButton: true + ) + } + } + } label: { + if creatingGroup { + Text("Set group preferences") + } else { + Label(label, systemImage: "switch.2") + } + } + } + + private func savePreferences() { + Task { + do { + var gp = groupInfo.groupProfile + gp.groupPreferences = toGroupPreferences(preferences) + let gInfo = try await apiUpdateGroup(groupInfo.groupId, gp) + await MainActor.run { + groupInfo = gInfo + ChatModel.shared.updateGroup(gInfo) + currentPreferences = preferences + } + } catch { + logger.error("GroupPreferencesView apiUpdateGroup error: \(responseError(error))") + } + } + } + } + func cantInviteIncognitoAlert() -> Alert { Alert( title: Text("Can't invite contacts!"), @@ -556,7 +795,9 @@ struct GroupChatInfoView_Previews: PreviewProvider { static var previews: some View { GroupChatInfoView( chat: Chat(chatInfo: ChatInfo.sampleData.group, chatItems: []), - groupInfo: Binding.constant(GroupInfo.sampleData) + groupInfo: Binding.constant(GroupInfo.sampleData), + onSearch: {}, + localAlias: "" ) } } diff --git a/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift b/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift index c782e2a717..a11c073a42 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift @@ -10,19 +10,21 @@ import SwiftUI import SimpleXChat struct GroupLinkView: View { + @EnvironmentObject var theme: AppTheme var groupId: Int64 - @Binding var groupLink: String? + @Binding var groupLink: CreatedConnLink? @Binding var groupLinkMemberRole: GroupMemberRole var showTitle: Bool = false var creatingGroup: Bool = false var linkCreatedCb: (() -> Void)? = nil + @State private var showShortLink = true @State private var creatingLink = false @State private var alert: GroupLinkAlert? @State private var shouldCreate = true private enum GroupLinkAlert: Identifiable { case deleteLink - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -34,14 +36,13 @@ struct GroupLinkView: View { var body: some View { if creatingGroup { - NavigationView { - groupLinkView() - .toolbar { - ToolbarItem(placement: .navigationBarTrailing) { - Button ("Continue") { linkCreatedCb?() } - } + groupLinkView() + .navigationBarBackButtonHidden() + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button ("Continue") { linkCreatedCb?() } } - } + } } else { groupLinkView() } @@ -70,10 +71,10 @@ struct GroupLinkView: View { } } .frame(height: 36) - SimpleXLinkQRCode(uri: groupLink) - .id("simplex-qrcode-view-for-\(groupLink)") + SimpleXCreatedLinkQRCode(link: groupLink, short: $showShortLink) + .id("simplex-qrcode-view-for-\(groupLink.simplexChatUri(short: showShortLink))") Button { - showShareSheet(items: [simplexChatLink(groupLink)]) + showShareSheet(items: [groupLink.simplexChatUri(short: showShortLink)]) } label: { Label("Share link", systemImage: "square.and.arrow.up") } @@ -94,6 +95,10 @@ struct GroupLinkView: View { .frame(maxWidth: .infinity) } } + } header: { + if let groupLink, groupLink.connShortLink != nil { + ToggleShortLinkHeader(text: Text(""), link: groupLink, short: $showShortLink) + } } .alert(item: $alert) { alert in switch alert { @@ -113,7 +118,7 @@ struct GroupLinkView: View { }, secondaryButton: .cancel() ) case let .error(title, error): - return Alert(title: Text(title), message: Text(error)) + return mkAlert(title: title, message: error) } } .onChange(of: groupLinkMemberRole) { _ in @@ -133,6 +138,7 @@ struct GroupLinkView: View { shouldCreate = false } } + .modifier(ThemedBackground(grouped: true)) } private func createGroupLink() { @@ -158,8 +164,8 @@ struct GroupLinkView: View { struct GroupLinkView_Previews: PreviewProvider { static var previews: some View { - @State var groupLink: String? = "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D" - @State var noGroupLink: String? = nil + @State var groupLink: CreatedConnLink? = CreatedConnLink(connFullLink: "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D", connShortLink: nil) + @State var noGroupLink: CreatedConnLink? = nil return Group { GroupLinkView(groupId: 1, groupLink: $groupLink, groupLinkMemberRole: Binding.constant(.member)) diff --git a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift index 999617dde7..79ad242366 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift @@ -11,12 +11,18 @@ import SimpleXChat struct GroupMemberInfoView: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme @Environment(\.dismiss) var dismiss: DismissAction @State var groupInfo: GroupInfo + @ObservedObject var chat: Chat @ObservedObject var groupMember: GMember var navigation: Bool = false @State private var connectionStats: ConnectionStats? = nil @State private var connectionCode: String? = nil + @State private var connectionLoaded: Bool = false + @State private var knownContactChat: Chat? = nil + @State private var knownContact: Contact? = nil + @State private var knownContactConnectionStats: ConnectionStats? = nil @State private var newRole: GroupMemberRole = .member @State private var alert: GroupMemberInfoViewAlert? @State private var sheet: PlanAndConnectActionSheet? @@ -35,7 +41,9 @@ struct GroupMemberInfoView: View { case abortSwitchAddressAlert case syncConnectionForceAlert case planAndConnectAlert(alert: PlanAndConnectAlert) - case error(title: LocalizedStringKey, error: LocalizedStringKey) + case queueInfo(info: String) + case someAlert(alert: SomeAlert) + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -49,6 +57,8 @@ struct GroupMemberInfoView: View { case .abortSwitchAddressAlert: return "abortSwitchAddressAlert" case .syncConnectionForceAlert: return "syncConnectionForceAlert" case let .planAndConnectAlert(alert): return "planAndConnectAlert \(alert.id)" + case let .queueInfo(info): return "queueInfo \(info)" + case let .someAlert(alert): return "someAlert \(alert.id)" case let .error(title, _): return "error \(title)" } } @@ -62,10 +72,11 @@ struct GroupMemberInfoView: View { } } - private func knownDirectChat(_ contactId: Int64) -> Chat? { + private func knownDirectChat(_ contactId: Int64) -> (Chat, Contact)? { if let chat = chatModel.getContactChat(contactId), - chat.chatInfo.contact?.directOrUsed == true { - return chat + let contact = chat.chatInfo.contact, + contact.directOrUsed == true { + return (chat, contact) } else { return nil } @@ -73,23 +84,25 @@ struct GroupMemberInfoView: View { private func groupMemberInfoView() -> some View { ZStack { - VStack { - let member = groupMember.wrapped - List { - groupMemberInfoHeader(member) - .listRowBackground(Color.clear) + let member = groupMember.wrapped + List { + groupMemberInfoHeader(member) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .padding(.bottom, 18) + + infoActionButtons(member) + .padding(.horizontal) + .frame(maxWidth: .infinity) + .frame(height: infoViewActionButtonHeight) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + + if connectionLoaded { if member.memberActive { Section { - if let contactId = member.memberContactId, let chat = knownDirectChat(contactId) { - knownDirectChatButton(chat) - } else if groupInfo.fullGroupPreferences.directMessages.on { - if let contactId = member.memberContactId { - newDirectChatButton(contactId) - } else if member.activeConn?.peerChatVRange.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) ?? false { - createMemberContactButton() - } - } if let code = connectionCode { verifyCodeButton(code) } if let connStats = connectionStats, connStats.ratchetSyncAllowed { @@ -109,8 +122,8 @@ struct GroupMemberInfoView: View { } label: { Label("Share address", systemImage: "square.and.arrow.up") } - if let contactId = member.memberContactId { - if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on { + if member.memberContactId != nil { + if knownContactChat == nil && !groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) { connectViaAddressButton(contactLink) } } else { @@ -118,13 +131,16 @@ struct GroupMemberInfoView: View { } } header: { Text("Address") + .foregroundColor(theme.colors.secondary) } footer: { Text("You can share this address with your contacts to let them connect with **\(member.displayName)**.") + .foregroundColor(theme.colors.secondary) } } - Section("Member") { - infoRow("Group", groupInfo.displayName) + Section(header: Text("Member").foregroundColor(theme.colors.secondary)) { + let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Group" : "Chat" + infoRow(label, groupInfo.displayName) if let roles = member.canChangeRoleTo(groupInfo: groupInfo) { Picker("Change role", selection: $newRole) { @@ -136,23 +152,17 @@ struct GroupMemberInfoView: View { } else { infoRow("Role", member.memberRole.text) } - - // TODO invited by - need to get contact by contact id - if let conn = member.activeConn { - let connLevelDesc = conn.connLevel == 0 ? NSLocalizedString("direct", comment: "connection level description") : String.localizedStringWithFormat(NSLocalizedString("indirect (%d)", comment: "connection level description"), conn.connLevel) - infoRow("Connection", connLevelDesc) - } } if let connStats = connectionStats { - Section("Servers") { + Section(header: Text("Servers").foregroundColor(theme.colors.secondary)) { // TODO network connection status Button("Change receiving address") { alert = .switchAddressAlert } .disabled( connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil } - || connStats.ratchetSyncSendProhibited + || !member.sendMsgEnabled ) if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) { Button("Abort changing address") { @@ -160,11 +170,11 @@ struct GroupMemberInfoView: View { } .disabled( connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch } - || connStats.ratchetSyncSendProhibited + || !member.sendMsgEnabled ) } - smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }) - smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }) + smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }, theme.colors.secondary) + smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }, theme.colors.secondary) } } @@ -175,41 +185,74 @@ struct GroupMemberInfoView: View { } if developerTools { - Section("For console") { + Section(header: Text("For console").foregroundColor(theme.colors.secondary)) { infoRow("Local name", member.localDisplayName) infoRow("Database ID", "\(member.groupMemberId)") + if let conn = member.activeConn { + let connLevelDesc = conn.connLevel == 0 ? NSLocalizedString("direct", comment: "connection level description") : String.localizedStringWithFormat(NSLocalizedString("indirect (%d)", comment: "connection level description"), conn.connLevel) + infoRow("Connection", connLevelDesc) + } + Button ("Debug delivery") { + Task { + do { + let info = queueInfoText(try await apiGroupMemberQueueInfo(groupInfo.apiId, member.groupMemberId)) + await MainActor.run { alert = .queueInfo(info: info) } + } catch let e { + logger.error("apiContactQueueInfo error: \(responseError(e))") + let a = getErrorAlert(e, "Error") + await MainActor.run { alert = .error(title: a.title, error: a.message) } + } + } + } } } + } - .navigationBarHidden(true) - .onAppear { - if #unavailable(iOS 16) { - // this condition prevents re-setting picker - if !justOpened { return } + } + .navigationBarHidden(true) + .task { + if #unavailable(iOS 16) { + // this condition prevents re-setting picker + if !justOpened { return } + } + justOpened = false + newRole = member.memberRole + do { + let (_, stats) = try await apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId) + let (mem, code) = member.memberActive ? try await apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil) + await MainActor.run { + _ = chatModel.upsertGroupMember(groupInfo, mem) + connectionStats = stats + connectionCode = code + connectionLoaded = true } - justOpened = false - DispatchQueue.main.async { - newRole = member.memberRole - do { - let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId) - let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil) - _ = chatModel.upsertGroupMember(groupInfo, mem) - connectionStats = stats - connectionCode = code - } catch let error { - logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))") + } catch let error { + await MainActor.run { + connectionLoaded = true + } + logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))") + } + if let contactId = member.memberContactId, let (contactChat, contact) = knownDirectChat(contactId) { + knownContactChat = contactChat + knownContact = contact + do { + let (stats, _) = try await apiContactInfo(contactChat.chatInfo.apiId) + await MainActor.run { + knownContactConnectionStats = stats } + } catch let error { + logger.error("apiContactInfo error: \(responseError(error))") } } - .onChange(of: newRole) { newRole in - if newRole != member.memberRole { - alert = .changeMemberRoleAlert(mem: member, role: newRole) - } - } - .onChange(of: member.memberRole) { role in - newRole = role + } + .onChange(of: newRole) { newRole in + if newRole != member.memberRole { + alert = .changeMemberRoleAlert(mem: member, role: newRole) } } + .onChange(of: member.memberRole) { role in + newRole = role + } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) .alert(item: $alert) { alertItem in switch(alertItem) { @@ -223,7 +266,9 @@ struct GroupMemberInfoView: View { case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchMemberAddress) case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncMemberConnection(force: true) }) case let .planAndConnectAlert(alert): return planAndConnectAlert(alert, dismiss: true) - case let .error(title, error): return Alert(title: Text(title), message: Text(error)) + case let .queueInfo(info): return queueInfoAlert(info) + case let .someAlert(a): return a.alert + case let .error(title, error): return mkAlert(title: title, message: error) } } .actionSheet(item: $sheet) { s in planAndConnectActionSheet(s, dismiss: true) } @@ -232,6 +277,68 @@ struct GroupMemberInfoView: View { ProgressView().scaleEffect(2) } } + .onChange(of: chat.chatInfo) { c in + if case let .group(gI) = chat.chatInfo { + groupInfo = gI + } + } + .modifier(ThemedBackground(grouped: true)) + } + + func infoActionButtons(_ member: GroupMember) -> some View { + GeometryReader { g in + let buttonWidth = g.size.width / 4 + HStack(alignment: .center, spacing: 8) { + if let chat = knownContactChat, let contact = knownContact { + knownDirectChatButton(chat, width: buttonWidth) + AudioCallButton(chat: chat, contact: contact, connectionStats: $knownContactConnectionStats, width: buttonWidth) { alert = .someAlert(alert: $0) } + VideoButton(chat: chat, contact: contact, connectionStats: $knownContactConnectionStats, width: buttonWidth) { alert = .someAlert(alert: $0) } + } else if groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) { + if let contactId = member.memberContactId { + newDirectChatButton(contactId, width: buttonWidth) + } else if member.versionRange.maxVersion >= CREATE_MEMBER_CONTACT_VERSION { + createMemberContactButton(member, width: buttonWidth) + } + InfoViewButton(image: "phone.fill", title: "call", disabledLook: true, width: buttonWidth) { showSendMessageToEnableCallsAlert() + } + InfoViewButton(image: "video.fill", title: "video", disabledLook: true, width: buttonWidth) { showSendMessageToEnableCallsAlert() + } + } else { // no known contact chat && directMessages are off + InfoViewButton(image: "message.fill", title: "message", disabledLook: true, width: buttonWidth) { showDirectMessagesProhibitedAlert("Can't message member") + } + InfoViewButton(image: "phone.fill", title: "call", disabledLook: true, width: buttonWidth) { showDirectMessagesProhibitedAlert("Can't call member") + } + InfoViewButton(image: "video.fill", title: "video", disabledLook: true, width: buttonWidth) { showDirectMessagesProhibitedAlert("Can't call member") + } + } + } + .frame(maxWidth: .infinity, alignment: .center) + } + } + + func showSendMessageToEnableCallsAlert() { + alert = .someAlert(alert: SomeAlert( + alert: mkAlert( + title: "Can't call member", + message: "Send message to enable calls." + ), + id: "can't call member, send message" + )) + } + + func showDirectMessagesProhibitedAlert(_ title: LocalizedStringKey) { + let messageLabel: LocalizedStringKey = ( + groupInfo.businessChat == nil + ? "Direct messages between members are prohibited." + : "Direct messages between members are prohibited in this chat." + ) + alert = .someAlert(alert: SomeAlert( + alert: mkAlert( + title: title, + message: messageLabel + ), + id: "can't message member, direct messages prohibited" + )) } func connectViaAddressButton(_ contactLink: String) -> some View { @@ -248,73 +355,102 @@ struct GroupMemberInfoView: View { } } - func knownDirectChatButton(_ chat: Chat) -> some View { - Button { - dismissAllSheets(animated: true) - DispatchQueue.main.async { - chatModel.chatId = chat.id - } - } label: { - Label("Send direct message", systemImage: "message") - } - } - - func newDirectChatButton(_ contactId: Int64) -> some View { - Button { - do { - let chat = try apiGetChat(type: .direct, id: contactId) - chatModel.addChat(chat) + func knownDirectChatButton(_ chat: Chat, width: CGFloat) -> some View { + InfoViewButton(image: "message.fill", title: "message", width: width) { + ItemsModel.shared.loadOpenChat(chat.id) { dismissAllSheets(animated: true) - DispatchQueue.main.async { - chatModel.chatId = chat.id - } - } catch let error { - logger.error("openDirectChatButton apiGetChat error: \(responseError(error))") } - } label: { - Label("Send direct message", systemImage: "message") } } - func createMemberContactButton() -> some View { - Button { - progressIndicator = true + func newDirectChatButton(_ contactId: Int64, width: CGFloat) -> some View { + InfoViewButton(image: "message.fill", title: "message", width: width) { Task { - do { - let memberContact = try await apiCreateMemberContact(groupInfo.apiId, groupMember.groupMemberId) - await MainActor.run { - progressIndicator = false - chatModel.addChat(Chat(chatInfo: .direct(contact: memberContact))) - dismissAllSheets(animated: true) - chatModel.chatId = memberContact.id - chatModel.setContactNetworkStatus(memberContact, .connected) - } - } catch let error { - logger.error("createMemberContactButton apiCreateMemberContact error: \(responseError(error))") - let a = getErrorAlert(error, "Error creating member contact") - await MainActor.run { - progressIndicator = false - alert = .error(title: a.title, error: a.message) - } + ItemsModel.shared.loadOpenChat("@\(contactId)") { + dismissAllSheets(animated: true) + } + } + } + } + + func createMemberContactButton(_ member: GroupMember, width: CGFloat) -> some View { + InfoViewButton( + image: "message.fill", + title: "message", + disabledLook: + !( + member.sendMsgEnabled || + (member.activeConn?.connectionStats?.ratchetSyncAllowed ?? false) + ), + width: width + ) { + if member.sendMsgEnabled { + progressIndicator = true + Task { + do { + let memberContact = try await apiCreateMemberContact(groupInfo.apiId, groupMember.groupMemberId) + await MainActor.run { + progressIndicator = false + chatModel.addChat(Chat(chatInfo: .direct(contact: memberContact))) + ItemsModel.shared.loadOpenChat(memberContact.id) { + dismissAllSheets(animated: true) + } + NetworkModel.shared.setContactNetworkStatus(memberContact, .connected) + } + } catch let error { + logger.error("createMemberContactButton apiCreateMemberContact error: \(responseError(error))") + let a = getErrorAlert(error, "Error creating member contact") + await MainActor.run { + progressIndicator = false + alert = .error(title: a.title, error: a.message) + } + } + } + } else if let connStats = connectionStats { + if connStats.ratchetSyncAllowed { + alert = .someAlert(alert: SomeAlert( + alert: Alert( + title: Text("Fix connection?"), + message: Text("Connection requires encryption renegotiation."), + primaryButton: .default(Text("Fix")) { + syncMemberConnection(force: false) + }, + secondaryButton: .cancel() + ), + id: "can't message member, fix connection" + )) + } else if connStats.ratchetSyncInProgress { + alert = .someAlert(alert: SomeAlert( + alert: mkAlert( + title: "Can't message member", + message: "Encryption renegotiation in progress." + ), + id: "can't message member, encryption renegotiation in progress" + )) + } else { + alert = .someAlert(alert: SomeAlert( + alert: mkAlert( + title: "Can't message member", + message: "Connection not ready." + ), + id: "can't message member, connection not ready" + )) } } - } label: { - Label("Send direct message", systemImage: "message") } } private func groupMemberInfoHeader(_ mem: GroupMember) -> some View { VStack { - ProfileImage(imageStr: mem.image, color: Color(uiColor: .tertiarySystemFill)) - .frame(width: 192, height: 192) + MemberProfileImage(mem, size: 192, color: Color(uiColor: .tertiarySystemFill)) .padding(.top, 12) .padding() if mem.verified { ( Text(Image(systemName: "checkmark.shield")) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .font(.title2) - + Text(" ") + + textSpace + Text(mem.displayName) .font(.largeTitle) ) @@ -360,6 +496,7 @@ struct GroupMemberInfoView: View { ) .navigationBarTitleDisplayMode(.inline) .navigationTitle("Security code") + .modifier(ThemedBackground()) } label: { Label( member.verified ? "View security code" : "Verify security code", @@ -409,7 +546,7 @@ struct GroupMemberInfoView: View { Section { if mem.blockedByAdmin { Label("Blocked by admin", systemImage: "hand.raised") - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } else if mem.memberSettings.showMessages { blockMemberButton(mem) } else { @@ -462,19 +599,26 @@ struct GroupMemberInfoView: View { } private func removeMemberAlert(_ mem: GroupMember) -> Alert { - Alert( + let label: LocalizedStringKey = ( + groupInfo.businessChat == nil + ? "Member will be removed from group - this cannot be undone!" + : "Member will be removed from chat - this cannot be undone!" + ) + return Alert( title: Text("Remove member?"), - message: Text("Member will be removed from group - this cannot be undone!"), + message: Text(label), primaryButton: .destructive(Text("Remove")) { Task { do { - let updatedMember = try await apiRemoveMember(groupInfo.groupId, mem.groupMemberId) + let updatedMembers = try await apiRemoveMembers(groupInfo.groupId, [mem.groupMemberId]) await MainActor.run { - _ = chatModel.upsertGroupMember(groupInfo, updatedMember) + updatedMembers.forEach { updatedMember in + _ = chatModel.upsertGroupMember(groupInfo, updatedMember) + } dismiss() } } catch let error { - logger.error("apiRemoveMember error: \(responseError(error))") + logger.error("apiRemoveMembers error: \(responseError(error))") let a = getErrorAlert(error, "Error removing member") alert = .error(title: a.title, error: a.message) } @@ -487,18 +631,28 @@ struct GroupMemberInfoView: View { private func changeMemberRoleAlert(_ mem: GroupMember) -> Alert { Alert( title: Text("Change member role?"), - message: mem.memberCurrent ? Text("Member role will be changed to \"\(newRole.text)\". All group members will be notified.") : Text("Member role will be changed to \"\(newRole.text)\". The member will receive a new invitation."), + message: ( + mem.memberCurrent + ? ( + groupInfo.businessChat == nil + ? Text("Member role will be changed to \"\(newRole.text)\". All group members will be notified.") + : Text("Member role will be changed to \"\(newRole.text)\". All chat members will be notified.") + ) + : Text("Member role will be changed to \"\(newRole.text)\". The member will receive a new invitation.") + ), primaryButton: .default(Text("Change")) { Task { do { - let updatedMember = try await apiMemberRole(groupInfo.groupId, mem.groupMemberId, newRole) + let updatedMembers = try await apiMembersRole(groupInfo.groupId, [mem.groupMemberId], newRole) await MainActor.run { - _ = chatModel.upsertGroupMember(groupInfo, updatedMember) + updatedMembers.forEach { updatedMember in + _ = chatModel.upsertGroupMember(groupInfo, updatedMember) + } } - + } catch let error { newRole = mem.memberRole - logger.error("apiMemberRole error: \(responseError(error))") + logger.error("apiMembersRole error: \(responseError(error))") let a = getErrorAlert(error, "Error changing role") alert = .error(title: a.title, error: a.message) } @@ -567,6 +721,21 @@ struct GroupMemberInfoView: View { } } +func MemberProfileImage( + _ mem: GroupMember, + size: CGFloat, + color: Color = Color(uiColor: .tertiarySystemGroupedBackground), + backgroundColor: Color? = nil +) -> some View { + ProfileImage( + imageStr: mem.image, + size: size, + color: color, + backgroundColor: backgroundColor, + blurred: mem.blocked + ) +} + func blockMemberAlert(_ gInfo: GroupInfo, _ mem: GroupMember) -> Alert { Alert( title: Text("Block member?"), @@ -635,12 +804,14 @@ func unblockForAllAlert(_ gInfo: GroupInfo, _ mem: GroupMember) -> Alert { func blockMemberForAll(_ gInfo: GroupInfo, _ member: GroupMember, _ blocked: Bool) { Task { do { - let updatedMember = try await apiBlockMemberForAll(gInfo.groupId, member.groupMemberId, blocked) + let updatedMembers = try await apiBlockMembersForAll(gInfo.groupId, [member.groupMemberId], blocked) await MainActor.run { - _ = ChatModel.shared.upsertGroupMember(gInfo, updatedMember) + updatedMembers.forEach { updatedMember in + _ = ChatModel.shared.upsertGroupMember(gInfo, updatedMember) + } } } catch let error { - logger.error("apiBlockMemberForAll error: \(responseError(error))") + logger.error("apiBlockMembersForAll error: \(responseError(error))") } } } @@ -649,6 +820,7 @@ struct GroupMemberInfoView_Previews: PreviewProvider { static var previews: some View { GroupMemberInfoView( groupInfo: GroupInfo.sampleData, + chat: Chat.sampleData, groupMember: GMember.sampleData ) } diff --git a/apps/ios/Shared/Views/Chat/Group/GroupMentions.swift b/apps/ios/Shared/Views/Chat/Group/GroupMentions.swift new file mode 100644 index 0000000000..9bb4a0cc35 --- /dev/null +++ b/apps/ios/Shared/Views/Chat/Group/GroupMentions.swift @@ -0,0 +1,249 @@ +// +// GroupMentions.swift +// SimpleX (iOS) +// +// Created by Diogo Cunha on 30/01/2025. +// Copyright © 2025 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +let MENTION_START: Character = "@" +let QUOTE: Character = "'" +let MEMBER_ROW_SIZE: CGFloat = 60 +let MAX_VISIBLE_MEMBER_ROWS: CGFloat = 4.8 + +struct GroupMentionsView: View { + @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme + var groupInfo: GroupInfo + @Binding var composeState: ComposeState + @Binding var selectedRange: NSRange + @Binding var keyboardVisible: Bool + + @State private var isVisible = false + @State private var currentMessage: String = "" + @State private var mentionName: String = "" + @State private var mentionRange: NSRange? + @State private var mentionMemberId: String? + @State private var sortedMembers: [GMember] = [] + + var body: some View { + ZStack(alignment: .bottom) { + if isVisible { + let filtered = filteredMembers() + if filtered.count > 0 { + Color.white.opacity(0.01) + .edgesIgnoringSafeArea(.all) + .onTapGesture { + isVisible = false + } + VStack(spacing: 0) { + Spacer() + Divider() + let scroll = ScrollView { + LazyVStack(spacing: 0) { + ForEach(Array(filtered.enumerated()), id: \.element.wrapped.groupMemberId) { index, member in + let mentioned = mentionMemberId == member.wrapped.memberId + let disabled = composeState.mentions.count >= MAX_NUMBER_OF_MENTIONS && !mentioned + ZStack(alignment: .bottom) { + memberRowView(member.wrapped, mentioned) + .contentShape(Rectangle()) + .disabled(disabled) + .opacity(disabled ? 0.6 : 1) + .onTapGesture { + memberSelected(member) + } + .padding(.horizontal) + .frame(height: MEMBER_ROW_SIZE) + + Divider() + .padding(.leading) + .padding(.leading, 48) + } + } + } + } + .frame(maxHeight: MEMBER_ROW_SIZE * min(MAX_VISIBLE_MEMBER_ROWS, CGFloat(filtered.count))) + .background(Color(UIColor.systemBackground)) + + if #available(iOS 16.0, *) { + scroll.scrollDismissesKeyboard(.never) + } else { + scroll + } + } + } + } + } + .onChange(of: composeState.parsedMessage) { parsedMsg in + currentMessage = composeState.message + messageChanged(currentMessage, parsedMsg, selectedRange) + } + .onChange(of: selectedRange) { r in + // This condition is needed to prevent messageChanged called twice, + // because composeState.formattedText triggers later when message changes. + // The condition is only true if position changed without text change + if currentMessage == composeState.message { + messageChanged(currentMessage, composeState.parsedMessage, r) + } + } + .onAppear { + currentMessage = composeState.message + } + } + + private func filteredMembers() -> [GMember] { + let s = mentionName.lowercased() + return s.isEmpty + ? sortedMembers + : sortedMembers.filter { $0.wrapped.localAliasAndFullName.localizedLowercase.contains(s) } + } + + private func messageChanged(_ msg: String, _ parsedMsg: [FormattedText], _ range: NSRange) { + removeUnusedMentions(parsedMsg) + if let (ft, r) = selectedMarkdown(parsedMsg, range) { + switch ft.format { + case let .mention(name): + isVisible = true + mentionName = name + mentionRange = r + mentionMemberId = composeState.mentions[name]?.memberId + if !m.membersLoaded { + Task { + await m.loadGroupMembers(groupInfo) + sortMembers() + } + } + return + case .none: () // + let pos = range.location + if range.length == 0, let (at, atRange) = getCharacter(msg, pos - 1), at == "@" { + let prevChar = getCharacter(msg, pos - 2)?.char + if prevChar == nil || prevChar == " " || prevChar == "\n" { + isVisible = true + mentionName = "" + mentionRange = atRange + mentionMemberId = nil + Task { + await m.loadGroupMembers(groupInfo) + sortMembers() + } + return + } + } + default: () + } + } + closeMemberList() + } + + private func sortMembers() { + sortedMembers = m.groupMembers.filter({ m in + let status = m.wrapped.memberStatus + return status != .memLeft && status != .memRemoved && status != .memInvited + }) + .sorted { $0.wrapped.memberRole > $1.wrapped.memberRole } + } + + private func removeUnusedMentions(_ parsedMsg: [FormattedText]) { + let usedMentions: Set = Set(parsedMsg.compactMap { ft in + if case let .mention(name) = ft.format { name } else { nil } + }) + if usedMentions.count < composeState.mentions.count { + composeState = composeState.copy(mentions: composeState.mentions.filter({ usedMentions.contains($0.key) })) + } + } + + private func getCharacter(_ s: String, _ pos: Int) -> (char: String.SubSequence, range: NSRange)? { + if pos < 0 || pos >= s.count { return nil } + let r = NSRange(location: pos, length: 1) + return if let range = Range(r, in: s) { + (s[range], r) + } else { + nil + } + } + + private func selectedMarkdown(_ parsedMsg: [FormattedText], _ range: NSRange) -> (FormattedText, NSRange)? { + if parsedMsg.isEmpty { return nil } + var i = 0 + var pos: Int = 0 + while i < parsedMsg.count && pos + parsedMsg[i].text.count < range.location { + pos += parsedMsg[i].text.count + i += 1 + } + // the second condition will be true when two markdowns are selected + return i >= parsedMsg.count || range.location + range.length > pos + parsedMsg[i].text.count + ? nil + : (parsedMsg[i], NSRange(location: pos, length: parsedMsg[i].text.count)) + } + + private func memberSelected(_ member: GMember) { + if let range = mentionRange, mentionMemberId == nil || mentionMemberId != member.wrapped.memberId { + addMemberMention(member, range) + } + } + + private func addMemberMention(_ member: GMember, _ r: NSRange) { + guard let range = Range(r, in: composeState.message) else { return } + var mentions = composeState.mentions + var newName: String + if let mm = mentions.first(where: { $0.value.memberId == member.wrapped.memberId }) { + newName = mm.key + } else { + newName = composeState.mentionMemberName(member.wrapped.memberProfile.displayName) + } + mentions[newName] = CIMention(groupMember: member.wrapped) + var msgMention = newName.contains(" ") || newName.last?.isPunctuation == true + ? "@'\(newName)'" + : "@\(newName)" + var newPos = r.location + msgMention.count + let newMsgLength = composeState.message.count + msgMention.count - r.length + print(newPos) + print(newMsgLength) + if newPos == newMsgLength { + msgMention += " " + newPos += 1 + } + composeState = composeState.copy( + message: composeState.message.replacingCharacters(in: range, with: msgMention), + mentions: mentions + ) + selectedRange = NSRange(location: newPos, length: 0) + closeMemberList() + keyboardVisible = true + } + + private func closeMemberList() { + isVisible = false + mentionName = "" + mentionRange = nil + mentionMemberId = nil + } + + private func memberRowView(_ member: GroupMember, _ mentioned: Bool) -> some View { + return HStack{ + MemberProfileImage(member, size: 38) + .padding(.trailing, 2) + VStack(alignment: .leading) { + let t = Text(member.localAliasAndFullName).foregroundColor(member.memberIncognito ? .indigo : theme.colors.onBackground) + (member.verified ? memberVerifiedShield() + t : t) + .lineLimit(1) + } + Spacer() + if mentioned { + Image(systemName: "checkmark") + } + } + + func memberVerifiedShield() -> Text { + (Text(Image(systemName: "checkmark.shield")) + textSpace) + .font(.caption) + .baselineOffset(2) + .kerning(-2) + .foregroundColor(theme.colors.secondary) + } + } +} diff --git a/apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift b/apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift index 7ab4bf4ece..ed39c401ce 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupPreferencesView.swift @@ -9,13 +9,21 @@ import SwiftUI import SimpleXChat +private let featureRoles: [(role: GroupMemberRole?, text: LocalizedStringKey)] = [ + (nil, "all members"), + (.admin, "admins"), + (.owner, "owners") +] + struct GroupPreferencesView: View { @Environment(\.dismiss) var dismiss: DismissAction @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme @Binding var groupInfo: GroupInfo - @State var preferences: FullGroupPreferences - @State var currentPreferences: FullGroupPreferences + @Binding var preferences: FullGroupPreferences + var currentPreferences: FullGroupPreferences let creatingGroup: Bool + let savePreferences: () -> Void @State private var showSaveDialogue = false var body: some View { @@ -24,13 +32,15 @@ struct GroupPreferencesView: View { List { featureSection(.timedMessages, $preferences.timedMessages.enable) featureSection(.fullDelete, $preferences.fullDelete.enable) - featureSection(.directMessages, $preferences.directMessages.enable) + featureSection(.directMessages, $preferences.directMessages.enable, $preferences.directMessages.role) featureSection(.reactions, $preferences.reactions.enable) - featureSection(.voice, $preferences.voice.enable) - featureSection(.files, $preferences.files.enable) + featureSection(.voice, $preferences.voice.enable, $preferences.voice.role) + featureSection(.files, $preferences.files.enable, $preferences.files.role) + featureSection(.simplexLinks, $preferences.simplexLinks.enable, $preferences.simplexLinks.role) + featureSection(.reports, $preferences.reports.enable) featureSection(.history, $preferences.history.enable) - if groupInfo.canEdit { + if groupInfo.isOwner { Section { Button("Reset") { preferences = currentPreferences } Button(saveText) { savePreferences() } @@ -60,16 +70,19 @@ struct GroupPreferencesView: View { savePreferences() dismiss() } - Button("Exit without saving") { dismiss() } + Button("Exit without saving") { + preferences = currentPreferences + dismiss() + } } } - private func featureSection(_ feature: GroupFeature, _ enableFeature: Binding) -> some View { + private func featureSection(_ feature: GroupFeature, _ enableFeature: Binding, _ enableForRole: Binding? = nil) -> some View { Section { - let color: Color = enableFeature.wrappedValue == .on ? .green : .secondary + let color: Color = enableFeature.wrappedValue == .on ? .green : theme.colors.secondary let icon = enableFeature.wrappedValue == .on ? feature.iconFilled : feature.icon let timedOn = feature == .timedMessages && enableFeature.wrappedValue == .on - if groupInfo.canEdit { + if groupInfo.isOwner { let enable = Binding( get: { enableFeature.wrappedValue == .on }, set: { on, _ in enableFeature.wrappedValue = on ? .on : .off } @@ -77,6 +90,7 @@ struct GroupPreferencesView: View { settingsRow(icon, color: color) { Toggle(feature.text, isOn: enable) } + .disabled(feature == .reports) // remove in 6.4 if timedOn { DropdownCustomTimePicker( selection: $preferences.timedMessages.ttl, @@ -87,6 +101,14 @@ struct GroupPreferencesView: View { ) .frame(height: 36) } + if enableFeature.wrappedValue == .on, let enableForRole { + Picker("Enabled for", selection: enableForRole) { + ForEach(featureRoles, id: \.role) { fr in + Text(fr.text) + } + } + .frame(height: 36) + } } else { settingsRow(icon, color: color) { infoRow(Text(feature.text), enableFeature.wrappedValue.text) @@ -94,25 +116,25 @@ struct GroupPreferencesView: View { if timedOn { infoRow("Delete after", timeText(preferences.timedMessages.ttl)) } + if enableFeature.wrappedValue == .on, let enableForRole { + HStack { + Text("Enabled for").foregroundColor(theme.colors.secondary) + Spacer() + Text( + featureRoles.first(where: { fr in fr.role == enableForRole.wrappedValue })?.text + ?? "all members" + ) + .foregroundColor(theme.colors.secondary) + } + } } } footer: { - Text(feature.enableDescription(enableFeature.wrappedValue, groupInfo.canEdit)) + Text(feature.enableDescription(enableFeature.wrappedValue, groupInfo.isOwner)) + .foregroundColor(theme.colors.secondary) } - } - - private func savePreferences() { - Task { - do { - var gp = groupInfo.groupProfile - gp.groupPreferences = toGroupPreferences(preferences) - let gInfo = try await apiUpdateGroup(groupInfo.groupId, gp) - await MainActor.run { - groupInfo = gInfo - chatModel.updateGroup(gInfo) - currentPreferences = preferences - } - } catch { - logger.error("GroupPreferencesView apiUpdateGroup error: \(responseError(error))") + .onChange(of: enableFeature.wrappedValue) { enabled in + if case .off = enabled { + enableForRole?.wrappedValue = nil } } } @@ -122,9 +144,10 @@ struct GroupPreferencesView_Previews: PreviewProvider { static var previews: some View { GroupPreferencesView( groupInfo: Binding.constant(GroupInfo.sampleData), - preferences: FullGroupPreferences.sampleData, + preferences: Binding.constant(FullGroupPreferences.sampleData), currentPreferences: FullGroupPreferences.sampleData, - creatingGroup: false + creatingGroup: false, + savePreferences: {} ) } } diff --git a/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift b/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift index 18cc3f4d80..1617edd11f 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift @@ -110,10 +110,13 @@ struct GroupProfileView: View { } } .onChange(of: chosenImage) { image in - if let image = image { - groupProfile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) - } else { - groupProfile.image = nil + Task { + let resized: String? = if let image { + await resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) + } else { + nil + } + await MainActor.run { groupProfile.image = resized } } } .onAppear { diff --git a/apps/ios/Shared/Views/Chat/Group/GroupWelcomeView.swift b/apps/ios/Shared/Views/Chat/Group/GroupWelcomeView.swift index 00d4f8c37b..97bff70efb 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupWelcomeView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupWelcomeView.swift @@ -11,18 +11,20 @@ import SimpleXChat struct GroupWelcomeView: View { @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var theme: AppTheme @Binding var groupInfo: GroupInfo @State var groupProfile: GroupProfile @State var welcomeText: String @State private var editMode = true @FocusState private var keyboardVisible: Bool @State private var showSaveDialog = false + @State private var showSecrets: Set = [] let maxByteCount = 1200 var body: some View { VStack { - if groupInfo.canEdit { + if groupInfo.isOwner && groupInfo.businessChat == nil { editorView() .modifier(BackButton(disabled: Binding.constant(false)) { if welcomeTextUnchanged() { @@ -57,7 +59,8 @@ struct GroupWelcomeView: View { } private func textPreview() -> some View { - messageText(welcomeText, parseSimpleXMarkdown(welcomeText), nil, showSecrets: false) + let r = messageText(welcomeText, parseSimpleXMarkdown(welcomeText), sender: nil, mentions: nil, userMemberId: nil, showSecrets: showSecrets, backgroundColor: UIColor(theme.colors.background)) + return msgTextResultView(r, Text(AttributedString(r.string)), showSecrets: $showSecrets) .frame(minHeight: 130, alignment: .topLeading) .frame(maxWidth: .infinity, alignment: .leading) } @@ -70,7 +73,7 @@ struct GroupWelcomeView: View { Group { if welcomeText.isEmpty { TextEditor(text: Binding.constant(NSLocalizedString("Enter welcome message…", comment: "placeholder"))) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .disabled(true) } TextEditor(text: $welcomeText) diff --git a/apps/ios/Shared/Views/Chat/ScrollViewCells.swift b/apps/ios/Shared/Views/Chat/ScrollViewCells.swift new file mode 100644 index 0000000000..d062627d5b --- /dev/null +++ b/apps/ios/Shared/Views/Chat/ScrollViewCells.swift @@ -0,0 +1,52 @@ +// +// ScrollViewCells.swift +// SimpleX (iOS) +// +// Created by Stanislav Dmitrenko on 27.01.2025. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +protocol ReusableView { + func prepareForReuse() +} + +/// `UIHostingConfiguration` back-port for iOS14 and iOS15 +/// Implemented as a `UIView` that wraps and manages a generic `UIHostingController` +final class HostingCell: UIView, ReusableView { + private let hostingController = UIHostingController(rootView: nil) + + /// Updates content of the cell + /// For reference: https://noahgilmore.com/blog/swiftui-self-sizing-cells/ + func set(content: Hosted, parent: UIViewController) { + hostingController.view.backgroundColor = .clear + hostingController.rootView = content + if let hostingView = hostingController.view { + hostingView.invalidateIntrinsicContentSize() + if hostingController.parent != parent { parent.addChild(hostingController) } + if !subviews.contains(hostingController.view) { + addSubview(hostingController.view) + hostingView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + hostingView.leadingAnchor + .constraint(equalTo: leadingAnchor), + hostingView.trailingAnchor + .constraint(equalTo: trailingAnchor), + hostingView.topAnchor + .constraint(equalTo: topAnchor), + hostingView.bottomAnchor + .constraint(equalTo: bottomAnchor) + ]) + } + if hostingController.parent != parent { hostingController.didMove(toParent: parent) } + } else { + fatalError("Hosting View not loaded \(hostingController)") + } + } + + func prepareForReuse() { + //super.prepareForReuse() + hostingController.rootView = nil + } +} diff --git a/apps/ios/Shared/Views/Chat/SelectableChatItemToolbars.swift b/apps/ios/Shared/Views/Chat/SelectableChatItemToolbars.swift new file mode 100644 index 0000000000..85d6b279c5 --- /dev/null +++ b/apps/ios/Shared/Views/Chat/SelectableChatItemToolbars.swift @@ -0,0 +1,153 @@ +// +// SelectableChatItemToolbars.swift +// SimpleX (iOS) +// +// Created by Stanislav Dmitrenko on 30.07.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct SelectedItemsTopToolbar: View { + @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme + @Binding var selectedChatItems: Set? + + var body: some View { + let count = selectedChatItems?.count ?? 0 + return Text(count == 0 ? "Nothing selected" : "Selected \(count)").font(.headline) + .foregroundColor(theme.colors.onBackground) + .frame(width: 220) + } +} + +struct SelectedItemsBottomToolbar: View { + @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme + let chatItems: [ChatItem] + @Binding var selectedChatItems: Set? + var chatInfo: ChatInfo + // Bool - delete for everyone is possible + var deleteItems: (Bool) -> Void + var archiveItems: () -> Void + var moderateItems: () -> Void + //var shareItems: () -> Void + var forwardItems: () -> Void + @State var deleteEnabled: Bool = false + @State var deleteForEveryoneEnabled: Bool = false + + @State var canArchiveReports: Bool = false + + @State var canModerate: Bool = false + @State var moderateEnabled: Bool = false + + @State var forwardEnabled: Bool = false + + @State var deleteCountProhibited = false + @State var forwardCountProhibited = false + + var body: some View { + VStack(spacing: 0) { + Divider() + + HStack(alignment: .center) { + Button { + if canArchiveReports { + archiveItems() + } else { + deleteItems(deleteForEveryoneEnabled) + } + } label: { + Image(systemName: "trash") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20, alignment: .center) + .foregroundColor(!deleteEnabled || deleteCountProhibited ? theme.colors.secondary: .red) + } + .disabled(!deleteEnabled || deleteCountProhibited) + + Spacer() + Button { + moderateItems() + } label: { + Image(systemName: "flag") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20, alignment: .center) + .foregroundColor(!moderateEnabled || deleteCountProhibited ? theme.colors.secondary : .red) + } + .disabled(!moderateEnabled || deleteCountProhibited) + .opacity(canModerate ? 1 : 0) + + Spacer() + Button { + forwardItems() + } label: { + Image(systemName: "arrowshape.turn.up.forward") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20, alignment: .center) + .foregroundColor(!forwardEnabled || forwardCountProhibited ? theme.colors.secondary : theme.colors.primary) + } + .disabled(!forwardEnabled || forwardCountProhibited) + } + .frame(maxHeight: .infinity) + .padding([.leading, .trailing], 12) + } + .onAppear { + recheckItems(chatInfo, chatItems, selectedChatItems) + } + .onChange(of: chatInfo) { info in + recheckItems(info, chatItems, selectedChatItems) + } + .onChange(of: chatItems) { items in + recheckItems(chatInfo, items, selectedChatItems) + } + .onChange(of: selectedChatItems) { selected in + recheckItems(chatInfo, chatItems, selected) + } + .frame(height: 55.5) + .background(.thinMaterial) + } + + private func recheckItems(_ chatInfo: ChatInfo, _ chatItems: [ChatItem], _ selectedItems: Set?) { + let count = selectedItems?.count ?? 0 + deleteCountProhibited = count == 0 || count > 200 + forwardCountProhibited = count == 0 || count > 20 + canModerate = possibleToModerate(chatInfo) + let groupInfo: GroupInfo? = if case let ChatInfo.group(groupInfo: info) = chatInfo { + info + } else { + nil + } + if let selected = selectedItems { + let me: Bool + let onlyOwnGroupItems: Bool + (deleteEnabled, deleteForEveryoneEnabled, canArchiveReports, me, onlyOwnGroupItems, forwardEnabled, selectedChatItems) = chatItems.reduce((true, true, true, true, true, true, [])) { (r, ci) in + if selected.contains(ci.id) { + var (de, dee, ar, me, onlyOwnGroupItems, fe, sel) = r + de = de && ci.canBeDeletedForSelf + dee = dee && ci.meta.deletable && !ci.localNote && !ci.isReport + ar = ar && ci.isActiveReport && ci.chatDir != .groupSnd && groupInfo != nil && groupInfo!.membership.memberRole >= .moderator + onlyOwnGroupItems = onlyOwnGroupItems && ci.chatDir == .groupSnd && !ci.isReport + me = me && ci.content.msgContent != nil && ci.memberToModerate(chatInfo) != nil && !ci.isReport + fe = fe && ci.content.msgContent != nil && ci.meta.itemDeleted == nil && !ci.isLiveDummy && !ci.isReport + sel.insert(ci.id) // we are collecting new selected items here to account for any changes in chat items list + return (de, dee, ar, me, onlyOwnGroupItems, fe, sel) + } else { + return r + } + } + moderateEnabled = me && !onlyOwnGroupItems + } + } + + private func possibleToModerate(_ chatInfo: ChatInfo) -> Bool { + return switch chatInfo { + case let .group(groupInfo): + groupInfo.membership.memberRole >= .admin + default: false + } + } +} diff --git a/apps/ios/Shared/Views/Chat/VerifyCodeView.swift b/apps/ios/Shared/Views/Chat/VerifyCodeView.swift index 75e31c26ed..7b01fe0300 100644 --- a/apps/ios/Shared/Views/Chat/VerifyCodeView.swift +++ b/apps/ios/Shared/Views/Chat/VerifyCodeView.swift @@ -10,6 +10,7 @@ import SwiftUI struct VerifyCodeView: View { @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var theme: AppTheme var displayName: String @State var connectionCode: String? @State var connectionVerified: Bool @@ -30,7 +31,7 @@ struct VerifyCodeView: View { HStack { if connectionVerified { Image(systemName: "checkmark.shield") - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) Text("\(displayName) is verified") } else { Text("\(displayName) is not verified") @@ -66,6 +67,7 @@ struct VerifyCodeView: View { ScanCodeView(connectionVerified: $connectionVerified, verify: verify) .navigationBarTitleDisplayMode(.large) .navigationTitle("Scan code") + .modifier(ThemedBackground()) } label: { Label("Scan code", systemImage: "qrcode") } @@ -122,5 +124,6 @@ struct VerifyCodeView: View { struct VerifyCodeView_Previews: PreviewProvider { static var previews: some View { VerifyCodeView(displayName: "alice", connectionCode: "12345 67890 12345 67890", connectionVerified: false, verify: {_ in nil}) + .environmentObject(CurrentColors.toAppTheme()) } } diff --git a/apps/ios/Shared/Views/ChatList/ChatHelp.swift b/apps/ios/Shared/Views/ChatList/ChatHelp.swift index 2435c9a4f5..7abab33177 100644 --- a/apps/ios/Shared/Views/ChatList/ChatHelp.swift +++ b/apps/ios/Shared/Views/ChatList/ChatHelp.swift @@ -10,8 +10,7 @@ import SwiftUI struct ChatHelp: View { @EnvironmentObject var chatModel: ChatModel - @Binding var showSettings: Bool - @State private var newChatMenuOption: NewChatMenuOption? = nil + let dismissSettingsSheet: DismissAction var body: some View { ScrollView { chatHelp() } @@ -24,7 +23,7 @@ struct ChatHelp: View { VStack(alignment: .leading, spacing: 0) { Text("To ask any questions and to receive updates:") Button("connect to SimpleX Chat developers.") { - showSettings = false + dismissSettingsSheet() DispatchQueue.main.async { UIApplication.shared.open(simplexTeamURL) } @@ -39,11 +38,12 @@ struct ChatHelp: View { HStack(spacing: 8) { Text("Tap button ") - NewChatMenuButton(newChatMenuOption: $newChatMenuOption) + NewChatMenuButton() Text("above, then choose:") } - Text("**Add contact**: to create a new invitation link, or connect via a link you received.") + Text("**Create 1-time link**: to create and share a new invitation link.") + Text("**Scan / Paste link**: to connect via a link you received.") Text("**Create group**: to create a new group.") } .padding(.top, 24) @@ -62,8 +62,9 @@ struct ChatHelp: View { } struct ChatHelp_Previews: PreviewProvider { + @Environment(\.dismiss) static var mockDismiss + static var previews: some View { - @State var showSettings = false - return ChatHelp(showSettings: $showSettings) + ChatHelp(dismissSettingsSheet: mockDismiss) } } diff --git a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift index 7fbc1e4ac8..81d78fbadd 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift @@ -9,34 +9,58 @@ import SwiftUI import SimpleXChat -private let rowHeights: [DynamicTypeSize: CGFloat] = [ - .xSmall: 68, - .small: 72, - .medium: 76, - .large: 80, - .xLarge: 88, - .xxLarge: 94, - .xxxLarge: 104, - .accessibility1: 90, - .accessibility2: 100, - .accessibility3: 120, - .accessibility4: 130, - .accessibility5: 140 +typealias DynamicSizes = ( + rowHeight: CGFloat, + profileImageSize: CGFloat, + mediaSize: CGFloat, + incognitoSize: CGFloat, + chatInfoSize: CGFloat, + unreadCorner: CGFloat, + unreadPadding: CGFloat +) + +private let dynamicSizes: [DynamicTypeSize: DynamicSizes] = [ + .xSmall: (68, 55, 33, 22, 18, 9, 3), + .small: (72, 57, 34, 22, 18, 9, 3), + .medium: (76, 60, 36, 22, 18, 10, 4), + .large: (80, 63, 38, 24, 20, 10, 4), + .xLarge: (88, 67, 41, 24, 20, 10, 4), + .xxLarge: (100, 71, 44, 27, 22, 11, 4), + .xxxLarge: (110, 75, 48, 30, 24, 12, 5), + .accessibility1: (110, 75, 48, 30, 24, 12, 5), + .accessibility2: (114, 75, 48, 30, 24, 12, 5), + .accessibility3: (124, 75, 48, 30, 24, 12, 5), + .accessibility4: (134, 75, 48, 30, 24, 12, 5), + .accessibility5: (144, 75, 48, 30, 24, 12, 5) ] +private let defaultDynamicSizes: DynamicSizes = dynamicSizes[.large]! + +func dynamicSize(_ font: DynamicTypeSize) -> DynamicSizes { + dynamicSizes[font] ?? defaultDynamicSizes +} + struct ChatListNavLink: View { @EnvironmentObject var chatModel: ChatModel - @Environment(\.dynamicTypeSize) private var dynamicTypeSize + @EnvironmentObject var theme: AppTheme + @EnvironmentObject var chatTagsModel: ChatTagsModel + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize + @AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = false @ObservedObject var chat: Chat + @Binding var parentSheet: SomeSheet? @State private var showContactRequestDialog = false @State private var showJoinGroupDialog = false @State private var showContactConnectionInfo = false @State private var showInvalidJSON = false - @State private var showDeleteContactActionSheet = false + @State private var alert: SomeAlert? = nil + @State private var actionSheet: SomeActionSheet? = nil + @State private var sheet: SomeSheet? = nil @State private var showConnectContactViaAddressDialog = false @State private var inProgress = false @State private var progressByTimeout = false + var dynamicRowHeight: CGFloat { dynamicSize(userFont).rowHeight } + var body: some View { Group { switch chat.chatInfo { @@ -63,18 +87,26 @@ struct ChatListNavLink: View { progressByTimeout = false } } + .actionSheet(item: $actionSheet) { $0.actionSheet } } - - @ViewBuilder private func contactNavLink(_ contact: Contact) -> some View { + + private func contactNavLink(_ contact: Contact) -> some View { Group { - if contact.activeConn == nil && contact.profile.contactLink != nil { + if contact.activeConn == nil && contact.profile.contactLink != nil && contact.active { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) - .frame(height: rowHeights[dynamicTypeSize]) + .frameCompat(height: dynamicRowHeight) .swipeActions(edge: .trailing, allowsFullSwipe: true) { Button { - showDeleteContactActionSheet = true + deleteContactDialog( + chat, + contact, + dismissToChatList: false, + showAlert: { alert = $0 }, + showActionSheet: { actionSheet = $0 }, + showSheetContent: { sheet = $0 } + ) } label: { - Label("Delete", systemImage: "trash") + deleteLabel } .tint(.red) } @@ -85,51 +117,44 @@ struct ChatListNavLink: View { } } else { NavLinkPlain( - tag: chat.chatInfo.id, + chatId: chat.chatInfo.id, selection: $chatModel.chatId, label: { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) } ) + .frameCompat(height: dynamicRowHeight) .swipeActions(edge: .leading, allowsFullSwipe: true) { markReadButton() toggleFavoriteButton() - toggleNtfsButton(chat) + toggleNtfsButton(chat: chat) } .swipeActions(edge: .trailing, allowsFullSwipe: true) { + tagChatButton(chat) if !chat.chatItems.isEmpty { clearChatButton() } Button { - if contact.ready || !contact.active { - showDeleteContactActionSheet = true - } else { - AlertManager.shared.showAlert(deletePendingContactAlert(chat, contact)) - } + deleteContactDialog( + chat, + contact, + dismissToChatList: false, + showAlert: { alert = $0 }, + showActionSheet: { actionSheet = $0 }, + showSheetContent: { sheet = $0 } + ) } label: { - Label("Delete", systemImage: "trash") + deleteLabel } .tint(.red) } - .frame(height: rowHeights[dynamicTypeSize]) } } - .actionSheet(isPresented: $showDeleteContactActionSheet) { - if contact.ready && contact.active { - return ActionSheet( - title: Text("Delete contact?\nThis cannot be undone!"), - buttons: [ - .destructive(Text("Delete and notify contact")) { Task { await deleteChat(chat, notify: true) } }, - .destructive(Text("Delete")) { Task { await deleteChat(chat, notify: false) } }, - .cancel() - ] - ) + .alert(item: $alert) { $0.alert } + .sheet(item: $sheet) { + if #available(iOS 16.0, *) { + $0.content + .presentationDetents([.fraction($0.fraction)]) } else { - return ActionSheet( - title: Text("Delete contact?\nThis cannot be undone!"), - buttons: [ - .destructive(Text("Delete")) { Task { await deleteChat(chat) } }, - .cancel() - ] - ) + $0.content } } } @@ -138,7 +163,7 @@ struct ChatListNavLink: View { switch (groupInfo.membership.memberStatus) { case .memInvited: ChatPreviewView(chat: chat, progressByTimeout: $progressByTimeout) - .frame(height: rowHeights[dynamicTypeSize]) + .frameCompat(height: dynamicRowHeight) .swipeActions(edge: .trailing, allowsFullSwipe: true) { joinGroupButton() if groupInfo.canDelete { @@ -158,11 +183,12 @@ struct ChatListNavLink: View { .disabled(inProgress) case .memAccepted: ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) - .frame(height: rowHeights[dynamicTypeSize]) + .frameCompat(height: dynamicRowHeight) .onTapGesture { AlertManager.shared.showAlert(groupInvitationAcceptedAlert()) } .swipeActions(edge: .trailing) { + tagChatButton(chat) if (groupInfo.membership.memberCurrent) { leaveGroupChatButton(groupInfo) } @@ -172,39 +198,59 @@ struct ChatListNavLink: View { } default: NavLinkPlain( - tag: chat.chatInfo.id, + chatId: chat.chatInfo.id, selection: $chatModel.chatId, label: { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) }, disabled: !groupInfo.ready ) - .frame(height: rowHeights[dynamicTypeSize]) + .frameCompat(height: dynamicRowHeight) .swipeActions(edge: .leading, allowsFullSwipe: true) { markReadButton() toggleFavoriteButton() - toggleNtfsButton(chat) + toggleNtfsButton(chat: chat) } .swipeActions(edge: .trailing, allowsFullSwipe: true) { - if !chat.chatItems.isEmpty { + tagChatButton(chat) + let showReportsButton = chat.chatStats.reportsCount > 0 && groupInfo.membership.memberRole >= .moderator + let showClearButton = !chat.chatItems.isEmpty + let showDeleteGroup = groupInfo.canDelete + let showLeaveGroup = groupInfo.membership.memberCurrent + let totalNumberOfButtons = 1 + (showReportsButton ? 1 : 0) + (showClearButton ? 1 : 0) + (showDeleteGroup ? 1 : 0) + (showLeaveGroup ? 1 : 0) + + if showClearButton && totalNumberOfButtons <= 3 { clearChatButton() } - if (groupInfo.membership.memberCurrent) { + + if showReportsButton && totalNumberOfButtons <= 3 { + archiveAllReportsButton() + } + + if showLeaveGroup { leaveGroupChatButton(groupInfo) } - if groupInfo.canDelete { + + if showDeleteGroup && totalNumberOfButtons <= 3 { deleteGroupChatButton(groupInfo) + } else if totalNumberOfButtons > 3 { + if showDeleteGroup && !groupInfo.membership.memberActive { + deleteGroupChatButton(groupInfo) + moreOptionsButton(false, chat, groupInfo) + } else { + moreOptionsButton(true, chat, groupInfo) + } } } } } - @ViewBuilder private func noteFolderNavLink(_ noteFolder: NoteFolder) -> some View { + private func noteFolderNavLink(_ noteFolder: NoteFolder) -> some View { NavLinkPlain( - tag: chat.chatInfo.id, + chatId: chat.chatInfo.id, selection: $chatModel.chatId, label: { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) }, disabled: !noteFolder.ready ) - .frame(height: rowHeights[dynamicTypeSize]) + .frameCompat(height: dynamicRowHeight) .swipeActions(edge: .leading, allowsFullSwipe: true) { markReadButton() } @@ -222,9 +268,9 @@ struct ChatListNavLink: View { await MainActor.run { inProgress = false } } } label: { - Label("Join", systemImage: chat.chatInfo.incognito ? "theatermasks" : "ipad.and.arrow.forward") + SwipeLabel(NSLocalizedString("Join", comment: "swipe action"), systemImage: chat.chatInfo.incognito ? "theatermasks" : "ipad.and.arrow.forward", inverted: oneHandUI) } - .tint(chat.chatInfo.incognito ? .indigo : .accentColor) + .tint(chat.chatInfo.incognito ? .indigo : theme.colors.primary) } @ViewBuilder private func markReadButton() -> some View { @@ -232,16 +278,16 @@ struct ChatListNavLink: View { Button { Task { await markChatRead(chat) } } label: { - Label("Read", systemImage: "checkmark") + SwipeLabel(NSLocalizedString("Read", comment: "swipe action"), systemImage: "checkmark", inverted: oneHandUI) } - .tint(Color.accentColor) + .tint(theme.colors.primary) } else { Button { Task { await markChatUnread(chat) } } label: { - Label("Unread", systemImage: "circlebadge.fill") + SwipeLabel(NSLocalizedString("Unread", comment: "swipe action"), systemImage: "circlebadge.fill", inverted: oneHandUI) } - .tint(Color.accentColor) + .tint(theme.colors.primary) } } @@ -251,33 +297,118 @@ struct ChatListNavLink: View { Button { toggleChatFavorite(chat, favorite: false) } label: { - Label("Unfav.", systemImage: "star.slash") + SwipeLabel(NSLocalizedString("Unfav.", comment: "swipe action"), systemImage: "star.slash.fill", inverted: oneHandUI) } .tint(.green) } else { Button { toggleChatFavorite(chat, favorite: true) } label: { - Label("Favorite", systemImage: "star.fill") + SwipeLabel(NSLocalizedString("Favorite", comment: "swipe action"), systemImage: "star.fill", inverted: oneHandUI) } .tint(.green) } } + @ViewBuilder private func toggleNtfsButton(chat: Chat) -> some View { + if let nextMode = chat.chatInfo.nextNtfMode { + Button { + toggleNotifications(chat, enableNtfs: nextMode) + } label: { + SwipeLabel(nextMode.text(mentions: chat.chatInfo.hasMentions), systemImage: nextMode.iconFilled, inverted: oneHandUI) + } + } else { + EmptyView() + } + } + + private func archiveAllReportsButton() -> some View { + Button { + AlertManager.shared.showAlert(archiveAllReportsAlert()) + } label: { + SwipeLabel(NSLocalizedString("Archive reports", comment: "swipe action"), systemImage: "archivebox", inverted: oneHandUI) + } + } + private func clearChatButton() -> some View { Button { AlertManager.shared.showAlert(clearChatAlert()) } label: { - Label("Clear", systemImage: "gobackward") + SwipeLabel(NSLocalizedString("Clear", comment: "swipe action"), systemImage: "gobackward", inverted: oneHandUI) } .tint(Color.orange) } + + private func tagChatButton(_ chat: Chat) -> some View { + Button { + setTagChatSheet(chat) + } label: { + SwipeLabel(NSLocalizedString("List", comment: "swipe action"), systemImage: "tag.fill", inverted: oneHandUI) + } + .tint(.mint) + } + + private func setTagChatSheet(_ chat: Chat) { + let screenHeight = UIScreen.main.bounds.height + let reservedSpace: Double = 4 * 44 // 2 for padding, 1 for "Create list" and another for extra tag + let tagsSpace = Double(max(chatTagsModel.userTags.count, 3)) * 44 + let fraction = min((reservedSpace + tagsSpace) / screenHeight, 0.62) + + parentSheet = SomeSheet( + content: { + AnyView( + NavigationView { + if chatTagsModel.userTags.isEmpty { + TagListEditor(chat: chat) + } else { + TagListView(chat: chat) + } + } + ) + }, + id: "lists sheet", + fraction: fraction + ) + } + + private func moreOptionsButton(_ canShowGroupDelete: Bool, _ chat: Chat, _ groupInfo: GroupInfo?) -> some View { + Button { + var buttons: [Alert.Button] = [] + buttons.append(.default(Text("Clear")) { + AlertManager.shared.showAlert(clearChatAlert()) + }) + if let groupInfo, chat.chatStats.reportsCount > 0 && groupInfo.membership.memberRole >= .moderator && groupInfo.ready { + buttons.append(.default(Text("Archive reports")) { + AlertManager.shared.showAlert(archiveAllReportsAlert()) + }) + } + + if canShowGroupDelete, let gi = groupInfo, gi.canDelete { + buttons.append(.destructive(Text("Delete")) { + AlertManager.shared.showAlert(deleteGroupAlert(gi)) + }) + } + + buttons.append(.cancel()) + + actionSheet = SomeActionSheet( + actionSheet: ActionSheet( + title: canShowGroupDelete ? Text("Clear or delete group?") : Text("Clear group?"), + buttons: buttons + ), + id: "other options" + ) + } label: { + SwipeLabel(NSLocalizedString("More", comment: "swipe action"), systemImage: "ellipsis", inverted: oneHandUI) + } + } + private func clearNoteFolderButton() -> some View { Button { AlertManager.shared.showAlert(clearNoteFolderAlert()) } label: { - Label("Clear", systemImage: "gobackward") + SwipeLabel(NSLocalizedString("Clear", comment: "swipe action"), systemImage: "gobackward", inverted: oneHandUI) } .tint(Color.orange) } @@ -286,7 +417,7 @@ struct ChatListNavLink: View { Button { AlertManager.shared.showAlert(leaveGroupAlert(groupInfo)) } label: { - Label("Leave", systemImage: "rectangle.portrait.and.arrow.right") + SwipeLabel(NSLocalizedString("Leave", comment: "swipe action"), systemImage: "rectangle.portrait.and.arrow.right.fill", inverted: oneHandUI) } .tint(Color.yellow) } @@ -295,32 +426,33 @@ struct ChatListNavLink: View { Button { AlertManager.shared.showAlert(deleteGroupAlert(groupInfo)) } label: { - Label("Delete", systemImage: "trash") + deleteLabel } .tint(.red) } private func contactRequestNavLink(_ contactRequest: UserContactRequest) -> some View { ContactRequestView(contactRequest: contactRequest, chat: chat) + .frameCompat(height: dynamicRowHeight) .swipeActions(edge: .trailing, allowsFullSwipe: true) { Button { Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) } - } label: { Label("Accept", systemImage: "checkmark") } - .tint(.accentColor) + } label: { SwipeLabel(NSLocalizedString("Accept", comment: "swipe action"), systemImage: "checkmark", inverted: oneHandUI) } + .tint(theme.colors.primary) Button { Task { await acceptContactRequest(incognito: true, contactRequest: contactRequest) } } label: { - Label("Accept incognito", systemImage: "theatermasks") + SwipeLabel(NSLocalizedString("Accept incognito", comment: "swipe action"), systemImage: "theatermasks.fill", inverted: oneHandUI) } .tint(.indigo) Button { AlertManager.shared.showAlert(rejectContactRequestAlert(contactRequest)) } label: { - Label("Reject", systemImage: "multiply") + SwipeLabel(NSLocalizedString("Reject", comment: "swipe action"), systemImage: "multiply.fill", inverted: oneHandUI) } .tint(.red) } - .frame(height: rowHeights[dynamicTypeSize]) + .contentShape(Rectangle()) .onTapGesture { showContactRequestDialog = true } .confirmationDialog("Accept connection request?", isPresented: $showContactRequestDialog, titleVisibility: .visible) { Button("Accept") { Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) } } @@ -331,38 +463,47 @@ struct ChatListNavLink: View { private func contactConnectionNavLink(_ contactConnection: PendingContactConnection) -> some View { ContactConnectionView(chat: chat) + .frameCompat(height: dynamicRowHeight) .swipeActions(edge: .trailing, allowsFullSwipe: true) { Button { AlertManager.shared.showAlert(deleteContactConnectionAlert(contactConnection) { a in AlertManager.shared.showAlertMsg(title: a.title, message: a.message) }) } label: { - Label("Delete", systemImage: "trash") + deleteLabel } .tint(.red) Button { showContactConnectionInfo = true } label: { - Label("Name", systemImage: "pencil") + SwipeLabel(NSLocalizedString("Name", comment: "swipe action"), systemImage: "pencil", inverted: oneHandUI) } - .tint(.accentColor) + .tint(theme.colors.primary) } - .frame(height: rowHeights[dynamicTypeSize]) - .sheet(isPresented: $showContactConnectionInfo) { - if case let .contactConnection(contactConnection) = chat.chatInfo { - ContactConnectionInfo(contactConnection: contactConnection) - .environment(\EnvironmentValues.refresh as! WritableKeyPath, nil) + .appSheet(isPresented: $showContactConnectionInfo) { + Group { + if case let .contactConnection(contactConnection) = chat.chatInfo { + ContactConnectionInfo(contactConnection: contactConnection) + .environment(\EnvironmentValues.refresh as! WritableKeyPath, nil) + .modifier(ThemedBackground(grouped: true)) + } } } + .contentShape(Rectangle()) .onTapGesture { showContactConnectionInfo = true } } + private var deleteLabel: some View { + SwipeLabel(NSLocalizedString("Delete", comment: "swipe action"), systemImage: "trash.fill", inverted: oneHandUI) + } + private func deleteGroupAlert(_ groupInfo: GroupInfo) -> Alert { - Alert( - title: Text("Delete group?"), + let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Delete group?" : "Delete chat?" + return Alert( + title: Text(label), message: deleteGroupAlertMessage(groupInfo), primaryButton: .destructive(Text("Delete")) { Task { await deleteChat(chat) } @@ -371,8 +512,25 @@ struct ChatListNavLink: View { ) } - private func deleteGroupAlertMessage(_ groupInfo: GroupInfo) -> Text { - groupInfo.membership.memberCurrent ? Text("Group will be deleted for all members - this cannot be undone!") : Text("Group will be deleted for you - this cannot be undone!") + private func archiveAllReportsAlert() -> Alert { + Alert( + title: Text("Archive all reports?"), + message: Text("All reports will be archived for you."), + primaryButton: .destructive(Text("Archive")) { + Task { await archiveAllReportsForMe(chat.chatInfo.apiId) } + }, + secondaryButton: .cancel() + ) + } + + private func archiveAllReportsForMe(_ apiId: Int64) async { + do { + if case let .groupChatItemsDeleted(user, groupInfo, chatItemIDs, _, member) = try await apiArchiveReceivedReports(groupId: apiId) { + await groupChatItemsDeleted(user, groupInfo, chatItemIDs, member) + } + } catch { + logger.error("archiveAllReportsForMe error: \(responseError(error))") + } } private func clearChatAlert() -> Alert { @@ -398,9 +556,15 @@ struct ChatListNavLink: View { } private func leaveGroupAlert(_ groupInfo: GroupInfo) -> Alert { - Alert( - title: Text("Leave group?"), - message: Text("You will stop receiving messages from this group. Chat history will be preserved."), + let titleLabel: LocalizedStringKey = groupInfo.businessChat == nil ? "Leave group?" : "Leave chat?" + let messageLabel: LocalizedStringKey = ( + groupInfo.businessChat == nil + ? "You will stop receiving messages from this group. Chat history will be preserved." + : "You will stop receiving messages from this chat. Chat history will be preserved." + ) + return Alert( + title: Text(titleLabel), + message: Text(messageLabel), primaryButton: .destructive(Text("Leave")) { Task { await leaveGroup(groupInfo.groupId) } }, @@ -408,28 +572,6 @@ struct ChatListNavLink: View { ) } - private func rejectContactRequestAlert(_ contactRequest: UserContactRequest) -> Alert { - Alert( - title: Text("Reject contact request"), - message: Text("The sender will NOT be notified"), - primaryButton: .destructive(Text("Reject")) { - Task { await rejectContactRequest(contactRequest) } - }, - secondaryButton: .cancel() - ) - } - - private func pendingContactAlert(_ chat: Chat, _ contact: Contact) -> Alert { - Alert( - title: Text("Contact is not connected yet!"), - message: Text("Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)."), - primaryButton: .cancel(), - secondaryButton: .destructive(Text("Delete Contact")) { - removePendingContact(chat, contact) - } - ) - } - private func groupInvitationAcceptedAlert() -> Alert { Alert( title: Text("Joining group"), @@ -437,54 +579,59 @@ struct ChatListNavLink: View { ) } - private func deletePendingContactAlert(_ chat: Chat, _ contact: Contact) -> Alert { - Alert( - title: Text("Delete pending connection"), - message: Text("Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)."), - primaryButton: .destructive(Text("Delete")) { - removePendingContact(chat, contact) - }, - secondaryButton: .cancel() - ) - } - - private func removePendingContact(_ chat: Chat, _ contact: Contact) { - Task { - do { - try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId) - DispatchQueue.main.async { - chatModel.removeChat(contact.id) - } - } catch let error { - logger.error("ChatListNavLink.removePendingContact apiDeleteChat error: \(responseError(error))") - } - } - } - - private func invalidJSONPreview(_ json: String) -> some View { + private func invalidJSONPreview(_ json: Data?) -> some View { Text("invalid chat data") .foregroundColor(.red) .padding(4) - .frame(height: rowHeights[dynamicTypeSize]) + .frameCompat(height: dynamicRowHeight) .onTapGesture { showInvalidJSON = true } - .sheet(isPresented: $showInvalidJSON) { - invalidJSONView(json) + .appSheet(isPresented: $showInvalidJSON) { + invalidJSONView(dataToString(json)) .environment(\EnvironmentValues.refresh as! WritableKeyPath, nil) } } private func connectContactViaAddress_(_ contact: Contact, _ incognito: Bool) { Task { - let ok = await connectContactViaAddress(contact.contactId, incognito) + let ok = await connectContactViaAddress(contact.contactId, incognito, showAlert: { AlertManager.shared.showAlert($0) }) if ok { - await MainActor.run { - chatModel.chatId = contact.id + ItemsModel.shared.loadOpenChat(contact.id) { + AlertManager.shared.showAlert(connReqSentAlert(.contact)) } } } } } +extension View { + @inline(__always) + @ViewBuilder fileprivate func frameCompat(height: CGFloat) -> some View { + if #available(iOS 16, *) { + self.frame(height: height) + } else { + VStack(spacing: 0) { + Divider() + .padding(.leading, 16) + self + .frame(height: height) + .padding(.horizontal, 8) + .padding(.vertical, 8) + } + } + } +} + +func rejectContactRequestAlert(_ contactRequest: UserContactRequest) -> Alert { + Alert( + title: Text("Reject contact request"), + message: Text("The sender will NOT be notified"), + primaryButton: .destructive(Text("Reject")) { + Task { await rejectContactRequest(contactRequest) } + }, + secondaryButton: .cancel() + ) +} + func deleteContactConnectionAlert(_ contactConnection: PendingContactConnection, showError: @escaping (ErrorAlert) -> Void, success: @escaping () -> Void = {}) -> Alert { Alert( title: Text("Delete pending connection?"), @@ -511,15 +658,14 @@ func deleteContactConnectionAlert(_ contactConnection: PendingContactConnection, ) } -func connectContactViaAddress(_ contactId: Int64, _ incognito: Bool) async -> Bool { +func connectContactViaAddress(_ contactId: Int64, _ incognito: Bool, showAlert: (Alert) -> Void) async -> Bool { let (contact, alert) = await apiConnectContactViaAddress(incognito: incognito, contactId: contactId) if let alert = alert { - AlertManager.shared.showAlert(alert) + showAlert(alert) return false } else if let contact = contact { await MainActor.run { ChatModel.shared.updateContact(contact) - AlertManager.shared.showAlert(connReqSentAlert(.contact)) } return true } @@ -560,18 +706,11 @@ func joinGroup(_ groupId: Int64, _ onComplete: @escaping () async -> Void) { } } -struct ErrorAlert { - var title: LocalizedStringKey - var message: LocalizedStringKey -} - func getErrorAlert(_ error: Error, _ title: LocalizedStringKey) -> ErrorAlert { - switch error as? ChatResponse { - case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TIMEOUT))): - return ErrorAlert(title: "Connection timeout", message: "Please check your network connection with \(serverHostname(addr)) and try again.") - case let .chatCmdError(_, .errorAgent(.BROKER(addr, .NETWORK))): - return ErrorAlert(title: "Connection error", message: "Please check your network connection with \(serverHostname(addr)) and try again.") - default: + if let r = error as? ChatError, + let alert = getNetworkErrorAlert(r) { + return alert + } else { return ErrorAlert(title: title, message: "Error: \(responseError(error))") } } @@ -583,15 +722,15 @@ struct ChatListNavLink_Previews: PreviewProvider { ChatListNavLink(chat: Chat( chatInfo: ChatInfo.sampleData.direct, chatItems: [ChatItem.getSample(1, .directSnd, .now, "hello")] - )) + ), parentSheet: .constant(nil)) ChatListNavLink(chat: Chat( chatInfo: ChatInfo.sampleData.direct, chatItems: [ChatItem.getSample(1, .directSnd, .now, "hello")] - )) + ), parentSheet: .constant(nil)) ChatListNavLink(chat: Chat( chatInfo: ChatInfo.sampleData.contactRequest, chatItems: [] - )) + ), parentSheet: .constant(nil)) } .previewLayout(.fixed(width: 360, height: 82)) } diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index 38aabdc21d..f34f930c6f 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -9,19 +9,154 @@ import SwiftUI import SimpleXChat +enum UserPickerSheet: Identifiable { + case address + case chatPreferences + case chatProfiles + case currentProfile + case useFromDesktop + case settings + + var id: Self { self } + + var navigationTitle: LocalizedStringKey { + switch self { + case .address: "SimpleX address" + case .chatPreferences: "Your preferences" + case .chatProfiles: "Your chat profiles" + case .currentProfile: "Your current profile" + case .useFromDesktop: "Connect to desktop" + case .settings: "Your settings" + } + } +} + +enum PresetTag: Int, Identifiable, CaseIterable, Equatable { + case groupReports = 0 + case favorites = 1 + case contacts = 2 + case groups = 3 + case business = 4 + case notes = 5 + + var id: Int { rawValue } + + var сollapse: Bool { + self != .groupReports + } +} + +enum ActiveFilter: Identifiable, Equatable { + case presetTag(PresetTag) + case userTag(ChatTag) + case unread + + var id: String { + switch self { + case let .presetTag(tag): "preset \(tag.id)" + case let .userTag(tag): "user \(tag.chatTagId)" + case .unread: "unread" + } + } +} + +class SaveableSettings: ObservableObject { + @Published var servers: ServerSettings = ServerSettings(currUserServers: [], userServers: [], serverErrors: []) +} + +struct ServerSettings { + public var currUserServers: [UserOperatorServers] + public var userServers: [UserOperatorServers] + public var serverErrors: [UserServersError] +} + +struct UserPickerSheetView: View { + let sheet: UserPickerSheet + @EnvironmentObject var chatModel: ChatModel + @StateObject private var ss = SaveableSettings() + + @State private var loaded = false + + var body: some View { + NavigationView { + ZStack { + if loaded, let currentUser = chatModel.currentUser { + switch sheet { + case .address: + UserAddressView(shareViaProfile: currentUser.addressShared) + case .chatPreferences: + PreferencesView( + profile: currentUser.profile, + preferences: currentUser.fullPreferences, + currentPreferences: currentUser.fullPreferences + ) + case .chatProfiles: + UserProfilesView() + case .currentProfile: + UserProfile() + case .useFromDesktop: + ConnectDesktopView() + case .settings: + SettingsView() + } + } + Color.clear // Required for list background to be rendered during loading + } + .navigationTitle(sheet.navigationTitle) + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } + .overlay { + if let la = chatModel.laRequest { + LocalAuthView(authRequest: la) + } + } + .task { + withAnimation( + .easeOut(duration: 0.1), + { loaded = true } + ) + } + .onDisappear { + if serversCanBeSaved( + ss.servers.currUserServers, + ss.servers.userServers, + ss.servers.serverErrors + ) { + showAlert( + title: NSLocalizedString("Save servers?", comment: "alert title"), + buttonTitle: NSLocalizedString("Save", comment: "alert button"), + buttonAction: { saveServers($ss.servers.currUserServers, $ss.servers.userServers) }, + cancelButton: true + ) + } + } + .environmentObject(ss) + } +} + struct ChatListView: View { @EnvironmentObject var chatModel: ChatModel - @Binding var showSettings: Bool + @EnvironmentObject var theme: AppTheme + @Binding var activeUserPickerSheet: UserPickerSheet? @State private var searchMode = false @FocusState private var searchFocussed @State private var searchText = "" @State private var searchShowingSimplexLink = false @State private var searchChatFilteredBySimplexLink: String? = nil - @State private var newChatMenuOption: NewChatMenuOption? = nil - @State private var userPickerVisible = false - @State private var showConnectDesktop = false - @AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false + @State private var scrollToSearchBar = false + @State private var userPickerShown: Bool = false + @State private var sheet: SomeSheet? = nil + @StateObject private var chatTagsModel = ChatTagsModel.shared + // iOS 15 is required it to show/hide toolbar while chat is hidden/visible + @State private var viewOnScreen = true + + @AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true + @AppStorage(DEFAULT_ONE_HAND_UI_CARD_SHOWN) private var oneHandUICardShown = false + @AppStorage(DEFAULT_ADDRESS_CREATION_CARD_SHOWN) private var addressCreationCardShown = false + @AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial + var body: some View { if #available(iOS 16.0, *) { viewBody.scrollDismissesKeyboard(.immediately) @@ -29,46 +164,59 @@ struct ChatListView: View { viewBody } } - + private var viewBody: some View { - ZStack(alignment: .topLeading) { + ZStack(alignment: oneHandUI ? .bottomLeading : .topLeading) { NavStackCompat( isActive: Binding( get: { chatModel.chatId != nil }, - set: { _ in } + set: { active in + if !active { chatModel.chatId = nil } + } ), destination: chatView - ) { - VStack { - if chatModel.chats.isEmpty { - onboardingButtons() - } - chatListView + ) { chatListView } + } + .modifier( + Sheet(isPresented: $userPickerShown) { + UserPicker(userPickerShown: $userPickerShown, activeSheet: $activeUserPickerSheet) + } + ) + .appSheet( + item: $activeUserPickerSheet, + onDismiss: { chatModel.laRequest = nil }, + content: { UserPickerSheetView(sheet: $0) } + ) + .onChange(of: activeUserPickerSheet) { + if $0 != nil { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + userPickerShown = false } } - if userPickerVisible { - Rectangle().fill(.white.opacity(0.001)).onTapGesture { - withAnimation { - userPickerVisible.toggle() - } - } - } - UserPicker( - showSettings: $showSettings, - showConnectDesktop: $showConnectDesktop, - userPickerVisible: $userPickerVisible - ) - } - .sheet(isPresented: $showConnectDesktop) { - ConnectDesktopView() } + .environmentObject(chatTagsModel) } - + private var chatListView: some View { - VStack { + let tm = ToolbarMaterial.material(toolbarMaterial) + return withToolbar(tm) { chatList + .background(theme.colors.background) + .navigationBarTitleDisplayMode(.inline) + .navigationBarHidden(searchMode || oneHandUI) + } + .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) + .onAppear { + if #unavailable(iOS 16.0), !viewOnScreen { + viewOnScreen = true + } + } + .onDisappear { + activeUserPickerSheet = nil + if #unavailable(iOS 16.0) { + viewOnScreen = false + } } - .onDisappear() { withAnimation { userPickerVisible = false } } .refreshable { AlertManager.shared.showAlert(Alert( title: Text("Reconnect servers?"), @@ -85,66 +233,112 @@ struct ChatListView: View { secondaryButton: .cancel() )) } - .listStyle(.plain) - .navigationBarTitleDisplayMode(.inline) - .navigationBarHidden(searchMode) - .toolbar { - ToolbarItem(placement: .navigationBarLeading) { - let user = chatModel.currentUser ?? User.sampleData - ZStack(alignment: .topTrailing) { - ProfileImage(imageStr: user.image, color: Color(uiColor: .quaternaryLabel)) - .frame(width: 32, height: 32) - .padding(.trailing, 4) - let allRead = chatModel.users - .filter { u in !u.user.activeUser && !u.user.hidden } - .allSatisfy { u in u.unreadCount == 0 } - if !allRead { - unreadBadge(size: 12) - } - } - .onTapGesture { - if chatModel.users.filter({ u in u.user.activeUser || !u.user.hidden }).count > 1 { - withAnimation { - userPickerVisible.toggle() - } - } else { - showSettings = true - } - } + .safeAreaInset(edge: .top) { + if oneHandUI { Divider().background(tm) } + } + .safeAreaInset(edge: .bottom) { + if oneHandUI { + Divider().padding(.bottom, Self.hasHomeIndicator ? 0 : 8).background(tm) } - ToolbarItem(placement: .principal) { - HStack(spacing: 4) { - Text("Chats") - .font(.headline) - if chatModel.chats.count > 0 { - toggleFilterButton() - } - } - .frame(maxWidth: .infinity, alignment: .center) - } - ToolbarItem(placement: .navigationBarTrailing) { - switch chatModel.chatRunning { - case .some(true): NewChatMenuButton(newChatMenuOption: $newChatMenuOption) - case .some(false): chatStoppedIcon() - case .none: EmptyView() - } + } + .sheet(item: $sheet) { sheet in + if #available(iOS 16.0, *) { + sheet.content.presentationDetents([.fraction(sheet.fraction)]) + } else { + sheet.content } } } - - private func toggleFilterButton() -> some View { - Button { - showUnreadAndFavorites = !showUnreadAndFavorites - } label: { - Image(systemName: "line.3.horizontal.decrease.circle" + (showUnreadAndFavorites ? ".fill" : "")) - .foregroundColor(.accentColor) + + static var hasHomeIndicator: Bool = { + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, + let window = windowScene.windows.first { + window.safeAreaInsets.bottom > 0 + } else { false } + }() + + @ViewBuilder func withToolbar(_ material: Material, content: () -> some View) -> some View { + if #available(iOS 16.0, *) { + if oneHandUI { + content() + .toolbarBackground(.hidden, for: .bottomBar) + .toolbar { bottomToolbar } + } else { + content() + .toolbarBackground(.automatic, for: .navigationBar) + .toolbarBackground(material) + .toolbar { topToolbar } + } + } else { + if oneHandUI { + content().toolbar { bottomToolbarGroup() } + } else { + content().toolbar { topToolbar } + } } } - - @ViewBuilder private var chatList: some View { + + @ToolbarContentBuilder var topToolbar: some ToolbarContent { + ToolbarItem(placement: .topBarLeading) { leadingToolbarItem } + ToolbarItem(placement: .principal) { SubsStatusIndicator() } + ToolbarItem(placement: .topBarTrailing) { trailingToolbarItem } + } + + @ToolbarContentBuilder var bottomToolbar: some ToolbarContent { + let padding: Double = Self.hasHomeIndicator ? 0 : 14 + ToolbarItem(placement: .bottomBar) { + HStack { + leadingToolbarItem.padding(.bottom, padding) + Spacer() + SubsStatusIndicator().padding(.bottom, padding) + Spacer() + trailingToolbarItem.padding(.bottom, padding) + } + .contentShape(Rectangle()) + .onTapGesture { scrollToSearchBar = true } + } + } + + @ToolbarContentBuilder func bottomToolbarGroup() -> some ToolbarContent { + let padding: Double = Self.hasHomeIndicator ? 0 : 14 + ToolbarItemGroup(placement: viewOnScreen ? .bottomBar : .principal) { + leadingToolbarItem.padding(.bottom, padding) + Spacer() + SubsStatusIndicator().padding(.bottom, padding) + Spacer() + trailingToolbarItem.padding(.bottom, padding) + } + } + + @ViewBuilder var leadingToolbarItem: some View { + let user = chatModel.currentUser ?? User.sampleData + ZStack(alignment: .topTrailing) { + ProfileImage(imageStr: user.image, size: 32, color: Color(uiColor: .quaternaryLabel)) + .padding([.top, .trailing], 3) + let allRead = chatModel.users + .filter { u in !u.user.activeUser && !u.user.hidden } + .allSatisfy { u in u.unreadCount == 0 } + if !allRead { + unreadBadge(size: 12) + } + } + .onTapGesture { + userPickerShown = true + } + } + + @ViewBuilder var trailingToolbarItem: some View { + switch chatModel.chatRunning { + case .some(true): NewChatMenuButton() + case .some(false): chatStoppedIcon() + case .none: EmptyView() + } + } + + private var chatList: some View { let cs = filteredChats() - ZStack { - VStack { + return ZStack { + ScrollViewReader { scrollProxy in List { if !chatModel.chats.isEmpty { ChatListSearchBar( @@ -152,148 +346,242 @@ struct ChatListView: View { searchFocussed: $searchFocussed, searchText: $searchText, searchShowingSimplexLink: $searchShowingSimplexLink, - searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink + searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink, + parentSheet: $sheet ) + .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) .listRowSeparator(.hidden) + .listRowBackground(Color.clear) .frame(maxWidth: .infinity) + .padding(.top, oneHandUI ? 8 : 0) + .id("searchBar") } - ForEach(cs, id: \.viewId) { chat in - ChatListNavLink(chat: chat) - .padding(.trailing, -16) + if #available(iOS 16.0, *) { + ForEach(cs, id: \.viewId) { chat in + ChatListNavLink(chat: chat, parentSheet: $sheet) + .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) + .padding(.trailing, -16) + .disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id)) + .listRowBackground(Color.clear) + } + .offset(x: -8) + } else { + ForEach(cs, id: \.viewId) { chat in + ChatListNavLink(chat: chat, parentSheet: $sheet) + .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) + .listRowSeparator(.hidden) + .listRowInsets(EdgeInsets()) + .background { theme.colors.background } // Hides default list selection colour .disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id)) + } + } + if !oneHandUICardShown { + OneHandUICard() + .padding(.vertical, 6) + .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + } + if !addressCreationCardShown { + AddressCreationCard() + .padding(.vertical, 6) + .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) } - .offset(x: -8) } - } - .onChange(of: chatModel.chatId) { _ in - if chatModel.chatId == nil, let chatId = chatModel.chatToTop { - chatModel.chatToTop = nil - chatModel.popChat(chatId) + .listStyle(.plain) + .onChange(of: chatModel.chatId) { currentChatId in + if let chatId = chatModel.chatToTop, currentChatId != chatId { + chatModel.chatToTop = nil + chatModel.popChat(chatId) + } + stopAudioPlayer() + } + .onChange(of: chatModel.currentUser?.userId) { _ in + stopAudioPlayer() + } + .onChange(of: scrollToSearchBar) { scrollToSearchBar in + if scrollToSearchBar { + Task { self.scrollToSearchBar = false } + withAnimation { scrollProxy.scrollTo("searchBar") } + } } } if cs.isEmpty && !chatModel.chats.isEmpty { - Text("No filtered chats").foregroundColor(.secondary) + noChatsView() + .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) + .foregroundColor(.secondary) } } } + + @ViewBuilder private func noChatsView() -> some View { + if searchString().isEmpty { + switch chatTagsModel.activeFilter { + case .presetTag: Text("No filtered chats") // this should not happen + case let .userTag(tag): Text("No chats in list \(tag.chatTagText)") + case .unread: + Button { + chatTagsModel.activeFilter = nil + } label: { + HStack { + Image(systemName: "line.3.horizontal.decrease") + Text("No unread chats") + } + } + case .none: Text("No chats") + } + } else { + Text("No chats found") + } + } - private func unreadBadge(_ text: Text? = Text(" "), size: CGFloat = 18) -> some View { + + private func unreadBadge(size: CGFloat = 18) -> some View { Circle() .frame(width: size, height: size) - .foregroundColor(.accentColor) + .foregroundColor(theme.colors.primary) } - - private func onboardingButtons() -> some View { - VStack(alignment: .trailing, spacing: 0) { - Path { p in - p.move(to: CGPoint(x: 8, y: 0)) - p.addLine(to: CGPoint(x: 16, y: 10)) - p.addLine(to: CGPoint(x: 0, y: 10)) - p.addLine(to: CGPoint(x: 8, y: 0)) - } - .fill(Color.accentColor) - .frame(width: 20, height: 10) - .padding(.trailing, 12) - - connectButton("Tap to start a new chat") { - newChatMenuOption = .newContact - } - - Spacer() - Text("You have no chats") - .foregroundColor(.secondary) - .frame(maxWidth: .infinity) - } - .padding(.trailing, 6) - .frame(maxHeight: .infinity) - } - - private func connectButton(_ label: LocalizedStringKey, action: @escaping () -> Void) -> some View { - Button(action: action) { - Text(label) - .padding(.vertical, 10) - .padding(.horizontal, 20) - } - .background(Color.accentColor) - .foregroundColor(.white) - .clipShape(RoundedRectangle(cornerRadius: 16)) - } - + @ViewBuilder private func chatView() -> some View { if let chatId = chatModel.chatId, let chat = chatModel.getChat(chatId) { - ChatView(chat: chat).onAppear { - loadChat(chat: chat) - } + ChatView(chat: chat) } } - + + func stopAudioPlayer() { + VoiceItemState.smallView.values.forEach { $0.audioPlayer?.stop() } + VoiceItemState.smallView = [:] + } + private func filteredChats() -> [Chat] { if let linkChatId = searchChatFilteredBySimplexLink { return chatModel.chats.filter { $0.id == linkChatId } } else { let s = searchString() - return s == "" && !showUnreadAndFavorites - ? chatModel.chats + return s == "" + ? chatModel.chats.filter { chat in + !chat.chatInfo.chatDeleted && !chat.chatInfo.contactCard && filtered(chat) + } : chatModel.chats.filter { chat in let cInfo = chat.chatInfo - switch cInfo { + return switch cInfo { case let .direct(contact): - return s == "" - ? filtered(chat) - : (viewNameContains(cInfo, s) || - contact.profile.displayName.localizedLowercase.contains(s) || - contact.fullName.localizedLowercase.contains(s)) - case let .group(gInfo): - return s == "" - ? (filtered(chat) || gInfo.membership.memberStatus == .memInvited) - : viewNameContains(cInfo, s) - case .local: - return s == "" || viewNameContains(cInfo, s) - case .contactRequest: - return s == "" || viewNameContains(cInfo, s) - case let .contactConnection(conn): - return s != "" && conn.localAlias.localizedLowercase.contains(s) - case .invalidJSON: - return false + !contact.chatDeleted && !chat.chatInfo.contactCard && ( + ( viewNameContains(cInfo, s) || + contact.profile.displayName.localizedLowercase.contains(s) || + contact.fullName.localizedLowercase.contains(s) + ) + ) + case .group: viewNameContains(cInfo, s) + case .local: viewNameContains(cInfo, s) + case .contactRequest: viewNameContains(cInfo, s) + case let .contactConnection(conn): conn.localAlias.localizedLowercase.contains(s) + case .invalidJSON: false } } } - - func searchString() -> String { - searchShowingSimplexLink ? "" : searchText.trimmingCharacters(in: .whitespaces).localizedLowercase - } - + func filtered(_ chat: Chat) -> Bool { - (chat.chatInfo.chatSettings?.favorite ?? false) || - chat.chatStats.unreadChat || - (chat.chatInfo.ntfsEnabled && chat.chatStats.unreadCount > 0) + switch chatTagsModel.activeFilter { + case let .presetTag(tag): presetTagMatchesChat(tag, chat.chatInfo, chat.chatStats) + case let .userTag(tag): chat.chatInfo.chatTags?.contains(tag.chatTagId) == true + case .unread: chat.unreadTag + case .none: true + } } - + func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool { cInfo.chatViewName.localizedLowercase.contains(s) } } + + func searchString() -> String { + searchShowingSimplexLink ? "" : searchText.trimmingCharacters(in: .whitespaces).localizedLowercase + } +} + +struct SubsStatusIndicator: View { + @State private var subs: SMPServerSubs = SMPServerSubs.newSMPServerSubs + @State private var hasSess: Bool = false + @State private var task: Task? + @State private var showServersSummary = false + + @AppStorage(DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE) private var showSubscriptionPercentage = false + + var body: some View { + Button { + showServersSummary = true + } label: { + HStack(spacing: 4) { + Text("Chats").foregroundStyle(Color.primary).fixedSize().font(.headline) + SubscriptionStatusIndicatorView(subs: subs, hasSess: hasSess) + if showSubscriptionPercentage { + SubscriptionStatusPercentageView(subs: subs, hasSess: hasSess) + } + } + } + .disabled(ChatModel.shared.chatRunning != true) + .onAppear { + startTask() + } + .onDisappear { + stopTask() + } + .appSheet(isPresented: $showServersSummary) { + ServersSummaryView() + .environment(\EnvironmentValues.refresh as! WritableKeyPath, nil) + } + } + + private func startTask() { + task = Task { + while !Task.isCancelled { + if AppChatState.shared.value == .active, ChatModel.shared.chatRunning == true { + do { + let (subs, hasSess) = try await getAgentSubsTotal() + await MainActor.run { + self.subs = subs + self.hasSess = hasSess + } + } catch let error { + logger.error("getSubsTotal error: \(responseError(error))") + } + } + try? await Task.sleep(nanoseconds: 1_000_000_000) // Sleep for 1 second + } + } + } + + func stopTask() { + task?.cancel() + task = nil + } } struct ChatListSearchBar: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme + @EnvironmentObject var chatTagsModel: ChatTagsModel @Binding var searchMode: Bool @FocusState.Binding var searchFocussed: Bool @Binding var searchText: String @Binding var searchShowingSimplexLink: Bool @Binding var searchChatFilteredBySimplexLink: String? + @Binding var parentSheet: SomeSheet? @State private var ignoreSearchTextChange = false - @State private var showScanCodeSheet = false @State private var alert: PlanAndConnectAlert? @State private var sheet: PlanAndConnectActionSheet? var body: some View { VStack(spacing: 12) { + ScrollView([.horizontal], showsIndicators: false) { TagsView(parentSheet: $parentSheet, searchText: $searchText) } HStack(spacing: 12) { HStack(spacing: 4) { Image(systemName: "magnifyingglass") TextField("Search or paste SimpleX link", text: $searchText) - .foregroundColor(searchShowingSimplexLink ? .secondary : .primary) + .foregroundColor(searchShowingSimplexLink ? theme.colors.secondary : theme.colors.onBackground) .disabled(searchShowingSimplexLink) .focused($searchFocussed) .frame(maxWidth: .infinity) @@ -302,47 +590,24 @@ struct ChatListSearchBar: View { .onTapGesture { searchText = "" } - } else if !searchFocussed { - HStack(spacing: 24) { - if m.pasteboardHasStrings { - Image(systemName: "doc") - .onTapGesture { - if let str = UIPasteboard.general.string { - searchText = str - } - } - } - - Image(systemName: "qrcode") - .resizable() - .scaledToFit() - .frame(width: 20, height: 20) - .onTapGesture { - showScanCodeSheet = true - } - } - .padding(.trailing, 2) } } .padding(EdgeInsets(top: 7, leading: 7, bottom: 7, trailing: 7)) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .background(Color(.tertiarySystemFill)) .cornerRadius(10.0) if searchFocussed { Text("Cancel") - .foregroundColor(.accentColor) + .foregroundColor(theme.colors.primary) .onTapGesture { searchText = "" searchFocussed = false } + } else if m.chats.count > 0 { + toggleFilterButton() } } - Divider() - } - .sheet(isPresented: $showScanCodeSheet) { - NewChatView(selection: .connect, showQRCodeScanner: true) - .environment(\EnvironmentValues.refresh as! WritableKeyPath, nil) // fixes .refreshable in ChatListView affecting nested view } .onChange(of: searchFocussed) { sf in withAnimation { searchMode = sf } @@ -369,6 +634,9 @@ struct ChatListSearchBar: View { } } } + .onChange(of: chatTagsModel.activeFilter) { _ in + searchText = "" + } .alert(item: $alert) { a in planAndConnectAlert(a, dismiss: true, cleanup: { searchText = "" }) } @@ -377,6 +645,26 @@ struct ChatListSearchBar: View { } } + private func toggleFilterButton() -> some View { + let showUnread = chatTagsModel.activeFilter == .unread + return ZStack { + Color.clear + .frame(width: 22, height: 22) + Image(systemName: showUnread ? "line.3.horizontal.decrease.circle.fill" : "line.3.horizontal.decrease") + .resizable() + .scaledToFit() + .foregroundColor(showUnread ? theme.colors.primary : theme.colors.secondary) + .frame(width: showUnread ? 22 : 16, height: showUnread ? 22 : 16) + .onTapGesture { + if chatTagsModel.activeFilter == .unread { + chatTagsModel.activeFilter = nil + } else { + chatTagsModel.activeFilter = .unread + } + } + } + } + private func connect(_ link: String) { planAndConnect( link, @@ -390,6 +678,198 @@ struct ChatListSearchBar: View { } } +struct TagsView: View { + @EnvironmentObject var chatTagsModel: ChatTagsModel + @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme + @Binding var parentSheet: SomeSheet? + @Binding var searchText: String + + var body: some View { + HStack { + tagsView() + } + } + + @ViewBuilder private func tagsView() -> some View { + if chatTagsModel.presetTags.count > 1 { + if chatTagsModel.presetTags.count + chatTagsModel.userTags.count <= 3 { + expandedPresetTagsFiltersView() + } else { + collapsedTagsFilterView() + ForEach(PresetTag.allCases, id: \.id) { (tag: PresetTag) in + if !tag.сollapse && (chatTagsModel.presetTags[tag] ?? 0) > 0 { + expandedTagFilterView(tag) + } + } + } + } + let selectedTag: ChatTag? = if case let .userTag(tag) = chatTagsModel.activeFilter { + tag + } else { + nil + } + ForEach(chatTagsModel.userTags, id: \.id) { tag in + let current = tag == selectedTag + let color: Color = current ? .accentColor : .secondary + ZStack { + HStack(spacing: 4) { + if let emoji = tag.chatTagEmoji { + Text(emoji) + } else { + Image(systemName: current ? "tag.fill" : "tag") + .foregroundColor(color) + } + ZStack { + let badge = Text(verbatim: (chatTagsModel.unreadTags[tag.chatTagId] ?? 0) > 0 ? " ●" : "").font(.footnote) + (Text(tag.chatTagText).fontWeight(.semibold) + badge).foregroundColor(.clear) + Text(tag.chatTagText).fontWeight(current ? .semibold : .regular).foregroundColor(color) + badge.foregroundColor(theme.colors.primary) + } + } + .onTapGesture { + setActiveFilter(filter: .userTag(tag)) + } + .onLongPressGesture { + let screenHeight = UIScreen.main.bounds.height + let reservedSpace: Double = 4 * 44 // 2 for padding, 1 for "Create list" and another for extra tag + let tagsSpace = Double(max(chatTagsModel.userTags.count, 3)) * 44 + let fraction = min((reservedSpace + tagsSpace) / screenHeight, 0.62) + + parentSheet = SomeSheet( + content: { + AnyView( + NavigationView { + TagListView(chat: nil) + .modifier(ThemedBackground(grouped: true)) + } + ) + }, + id: "tag list", + fraction: fraction + ) + } + } + } + + Button { + parentSheet = SomeSheet( + content: { + AnyView( + NavigationView { + TagListEditor() + } + ) + }, + id: "tag create" + ) + } label: { + if chatTagsModel.userTags.isEmpty { + HStack(spacing: 4) { + Image(systemName: "plus") + Text("Add list") + } + } else { + Image(systemName: "plus") + } + } + .foregroundColor(.secondary) + } + + @ViewBuilder private func expandedTagFilterView(_ tag: PresetTag) -> some View { + let selectedPresetTag: PresetTag? = if case let .presetTag(tag) = chatTagsModel.activeFilter { + tag + } else { + nil + } + let active = tag == selectedPresetTag + let (icon, text) = presetTagLabel(tag: tag, active: active) + let color: Color = active ? .accentColor : .secondary + + HStack(spacing: 4) { + Image(systemName: icon) + .foregroundColor(color) + ZStack { + Text(text).fontWeight(.semibold).foregroundColor(.clear) + Text(text).fontWeight(active ? .semibold : .regular).foregroundColor(color) + } + } + .onTapGesture { + setActiveFilter(filter: .presetTag(tag)) + } + } + + private func expandedPresetTagsFiltersView() -> some View { + ForEach(PresetTag.allCases, id: \.id) { tag in + if (chatTagsModel.presetTags[tag] ?? 0) > 0 { + expandedTagFilterView(tag) + } + } + } + + @ViewBuilder private func collapsedTagsFilterView() -> some View { + let selectedPresetTag: PresetTag? = if case let .presetTag(tag) = chatTagsModel.activeFilter { + tag + } else { + nil + } + Menu { + if chatTagsModel.activeFilter != nil || !searchText.isEmpty { + Button { + chatTagsModel.activeFilter = nil + searchText = "" + } label: { + HStack { + Image(systemName: "list.bullet") + Text("All") + } + } + } + ForEach(PresetTag.allCases, id: \.id) { tag in + if (chatTagsModel.presetTags[tag] ?? 0) > 0 && tag.сollapse { + Button { + setActiveFilter(filter: .presetTag(tag)) + } label: { + let (systemName, text) = presetTagLabel(tag: tag, active: tag == selectedPresetTag) + HStack { + Image(systemName: systemName) + Text(text) + } + } + } + } + } label: { + if let tag = selectedPresetTag, tag.сollapse { + let (systemName, _) = presetTagLabel(tag: tag, active: true) + Image(systemName: systemName) + .foregroundColor(.accentColor) + } else { + Image(systemName: "list.bullet") + .foregroundColor(.secondary) + } + } + .frame(minWidth: 28) + } + + private func presetTagLabel(tag: PresetTag, active: Bool) -> (String, LocalizedStringKey) { + switch tag { + case .groupReports: (active ? "flag.fill" : "flag", "Reports") + case .favorites: (active ? "star.fill" : "star", "Favorites") + case .contacts: (active ? "person.fill" : "person", "Contacts") + case .groups: (active ? "person.2.fill" : "person.2", "Groups") + case .business: (active ? "briefcase.fill" : "briefcase", "Businesses") + case .notes: (active ? "folder.fill" : "folder", "Notes") + } + } + + private func setActiveFilter(filter: ActiveFilter) { + if filter != chatTagsModel.activeFilter { + chatTagsModel.activeFilter = filter + } else { + chatTagsModel.activeFilter = nil + } + } +} + func chatStoppedIcon() -> some View { Button { AlertManager.shared.showAlertMsg( @@ -401,28 +881,59 @@ func chatStoppedIcon() -> some View { } } +func presetTagMatchesChat(_ tag: PresetTag, _ chatInfo: ChatInfo, _ chatStats: ChatStats) -> Bool { + switch tag { + case .groupReports: + chatStats.reportsCount > 0 + case .favorites: + chatInfo.chatSettings?.favorite == true + case .contacts: + switch chatInfo { + case let .direct(contact): !(contact.activeConn == nil && contact.profile.contactLink != nil && contact.active) && !contact.chatDeleted + case .contactRequest: true + case .contactConnection: true + case let .group(groupInfo): groupInfo.businessChat?.chatType == .customer + default: false + } + case .groups: + switch chatInfo { + case let .group(groupInfo): groupInfo.businessChat == nil + default: false + } + case .business: + chatInfo.groupInfo?.businessChat?.chatType == .business + case .notes: + switch chatInfo { + case .local: true + default: false + } + } +} + struct ChatListView_Previews: PreviewProvider { + @State static var userPickerSheet: UserPickerSheet? = .none + static var previews: some View { let chatModel = ChatModel() - chatModel.chats = [ - Chat( + chatModel.updateChats([ + ChatData( chatInfo: ChatInfo.sampleData.direct, chatItems: [ChatItem.getSample(1, .directSnd, .now, "hello")] ), - Chat( + ChatData( chatInfo: ChatInfo.sampleData.group, chatItems: [ChatItem.getSample(1, .directSnd, .now, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")] ), - Chat( + ChatData( chatInfo: ChatInfo.sampleData.contactRequest, chatItems: [] ) - ] + ]) return Group { - ChatListView(showSettings: Binding.constant(false)) + ChatListView(activeUserPickerSheet: $userPickerSheet) .environmentObject(chatModel) - ChatListView(showSettings: Binding.constant(false)) + ChatListView(activeUserPickerSheet: $userPickerSheet) .environmentObject(ChatModel()) } } diff --git a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift index 8bfc8fec03..b8c8233e6e 100644 --- a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift @@ -11,53 +11,125 @@ import SimpleXChat struct ChatPreviewView: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize @ObservedObject var chat: Chat @Binding var progressByTimeout: Bool @State var deleting: Bool = false - @Environment(\.colorScheme) var colorScheme var darkGreen = Color(red: 0, green: 0.5, blue: 0) + @State private var activeContentPreview: ActiveContentPreview? = nil + @State private var showFullscreenGallery: Bool = false @AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true + var dynamicMediaSize: CGFloat { dynamicSize(userFont).mediaSize } + var dynamicChatInfoSize: CGFloat { dynamicSize(userFont).chatInfoSize } + var body: some View { let cItem = chat.chatItems.last - return HStack(spacing: 8) { - ZStack(alignment: .bottomTrailing) { - ChatInfoImage(chat: chat) - .frame(width: 63, height: 63) - chatPreviewImageOverlayIcon() - .padding([.bottom, .trailing], 1) - } - .padding(.leading, 4) - - VStack(spacing: 0) { - HStack(alignment: .top) { - chatPreviewTitle() - Spacer() - (cItem?.timestampText ?? formatTimestampText(chat.chatInfo.chatTs)) - .font(.subheadline) - .frame(minWidth: 60, alignment: .trailing) - .foregroundColor(.secondary) - .padding(.top, 4) + return ZStack { + HStack(spacing: 8) { + ZStack(alignment: .bottomTrailing) { + ChatInfoImage(chat: chat, size: dynamicSize(userFont).profileImageSize) + chatPreviewImageOverlayIcon() + .padding([.bottom, .trailing], 1) } - .padding(.bottom, 4) - .padding(.horizontal, 8) - - ZStack(alignment: .topTrailing) { - chatMessagePreview(cItem) - chatStatusImage() - .padding(.top, 26) - .frame(maxWidth: .infinity, alignment: .trailing) - } - .padding(.trailing, 8) + .padding(.leading, 4) - Spacer() + let chatTs = if let cItem { + cItem.meta.itemTs + } else { + chat.chatInfo.chatTs + } + VStack(spacing: 0) { + HStack(alignment: .top) { + chatPreviewTitle() + Spacer() + (formatTimestampText(chatTs)) + .font(.subheadline) + .frame(minWidth: 60, alignment: .trailing) + .foregroundColor(theme.colors.secondary) + .padding(.top, 4) + } + .padding(.bottom, 4) + .padding(.horizontal, 8) + + ZStack(alignment: .topTrailing) { + let chat = activeContentPreview?.chat ?? chat + let ci = activeContentPreview?.ci ?? chat.chatItems.last + let mc = ci?.content.msgContent + HStack(alignment: .top) { + let deleted = ci?.isDeletedContent == true || ci?.meta.itemDeleted != nil + let showContentPreview = (showChatPreviews && chatModel.draftChatId != chat.id && !deleted) || activeContentPreview != nil + if let ci, showContentPreview { + chatItemContentPreview(chat, ci) + } + let mcIsVoice = switch mc { case .voice: true; default: false } + if !mcIsVoice || !showContentPreview || mc?.text != "" || chatModel.draftChatId == chat.id { + let hasFilePreview = if case .file = mc { true } else { false } + chatMessagePreview(cItem, hasFilePreview) + } else { + Spacer() + chatInfoIcon(chat).frame(minWidth: 37, alignment: .trailing) + } + } + .onChange(of: chatModel.stopPreviousRecPlay?.path) { _ in + checkActiveContentPreview(chat, ci, mc) + } + .onChange(of: activeContentPreview) { _ in + checkActiveContentPreview(chat, ci, mc) + } + .onChange(of: showFullscreenGallery) { _ in + checkActiveContentPreview(chat, ci, mc) + } + chatStatusImage() + .padding(.top, dynamicChatInfoSize * 1.44) + .frame(maxWidth: .infinity, alignment: .trailing) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.trailing, 8) + + Spacer() + } + .frame(maxHeight: .infinity) + } + .opacity(deleting ? 0.4 : 1) + .padding(.bottom, -8) + + if deleting { + ProgressView() + .scaleEffect(2) } - .frame(maxHeight: .infinity) } - .padding(.bottom, -8) .onChange(of: chatModel.deletedChats.contains(chat.chatInfo.id)) { contains in deleting = contains + // Stop voice when deleting the chat + if contains, let ci = activeContentPreview?.ci { + VoiceItemState.stopVoiceInSmallView(chat.chatInfo, ci) + } + } + + func checkActiveContentPreview(_ chat: Chat, _ ci: ChatItem?, _ mc: MsgContent?) { + let playing = chatModel.stopPreviousRecPlay + if case .voice = activeContentPreview?.mc, playing == nil { + activeContentPreview = nil + } else if activeContentPreview == nil { + if case .image = mc, let ci, let mc, showFullscreenGallery { + activeContentPreview = ActiveContentPreview(chat: chat, ci: ci, mc: mc) + } + if case .video = mc, let ci, let mc, showFullscreenGallery { + activeContentPreview = ActiveContentPreview(chat: chat, ci: ci, mc: mc) + } + if case .voice = mc, let ci, let mc, let fileSource = ci.file?.fileSource, playing?.path.hasSuffix(fileSource.filePath) == true { + activeContentPreview = ActiveContentPreview(chat: chat, ci: ci, mc: mc) + } + } else if case .voice = activeContentPreview?.mc { + if let playing, let fileSource = ci?.file?.fileSource, !playing.path.hasSuffix(fileSource.filePath) { + activeContentPreview = nil + } + } else if !showFullscreenGallery { + activeContentPreview = nil + } } } @@ -71,6 +143,7 @@ struct ChatPreviewView: View { } case let .group(groupInfo): switch (groupInfo.membership.memberStatus) { + case .memRejected: inactiveIcon() case .memLeft: inactiveIcon() case .memRemoved: inactiveIcon() case .memGroupDeleted: inactiveIcon() @@ -81,7 +154,7 @@ struct ChatPreviewView: View { } } - @ViewBuilder private func inactiveIcon() -> some View { + private func inactiveIcon() -> some View { Image(systemName: "multiply.circle.fill") .foregroundColor(.secondary.opacity(0.65)) .background(Circle().foregroundColor(Color(uiColor: .systemBackground))) @@ -95,9 +168,9 @@ struct ChatPreviewView: View { case let .group(groupInfo): let v = previewTitle(t) switch (groupInfo.membership.memberStatus) { - case .memInvited: v.foregroundColor(deleting ? .secondary : chat.chatInfo.incognito ? .indigo : .accentColor) - case .memAccepted: v.foregroundColor(.secondary) - default: if deleting { v.foregroundColor(.secondary) } else { v } + case .memInvited: v.foregroundColor(deleting ? theme.colors.secondary : chat.chatInfo.incognito ? .indigo : theme.colors.primary) + case .memAccepted, .memRejected: v.foregroundColor(theme.colors.secondary) + default: if deleting { v.foregroundColor(theme.colors.secondary) } else { v } } default: previewTitle(t) } @@ -108,61 +181,100 @@ struct ChatPreviewView: View { } private var verifiedIcon: Text { - (Text(Image(systemName: "checkmark.shield")) + Text(" ")) - .foregroundColor(.secondary) + (Text(Image(systemName: "checkmark.shield")) + textSpace) + .foregroundColor(theme.colors.secondary) .baselineOffset(1) .kerning(-2) } - private func chatPreviewLayout(_ text: Text, draft: Bool = false) -> some View { + private func chatPreviewLayout(_ text: Text?, draft: Bool = false, hasFilePreview: Bool = false, hasSecrets: Bool) -> some View { ZStack(alignment: .topTrailing) { + let s = chat.chatStats + let mentionWidth: CGFloat = if s.unreadMentions > 0 && s.unreadCount > 1 { dynamicSize(userFont).unreadCorner } else { 0 } let t = text - .lineLimit(2) + .lineLimit(userFont <= .xxxLarge ? 2 : 1) .multilineTextAlignment(.leading) + .if(hasSecrets, transform: hiddenSecretsView) .frame(maxWidth: .infinity, alignment: .topLeading) - .padding(.leading, 8) - .padding(.trailing, 36) + .padding(.leading, hasFilePreview ? 0 : 8) + .padding(.trailing, mentionWidth + (hasFilePreview ? 38 : 36)) + .offset(x: hasFilePreview ? -2 : 0) + .fixedSize(horizontal: false, vertical: true) if !showChatPreviews && !draft { t.privacySensitive(true).redacted(reason: .privacy) } else { t } - let s = chat.chatStats - if s.unreadCount > 0 || s.unreadChat { - unreadCountText(s.unreadCount) - .font(.caption) - .foregroundColor(.white) - .padding(.horizontal, 4) - .frame(minWidth: 18, minHeight: 18) - .background(chat.chatInfo.ntfsEnabled || chat.chatInfo.chatType == .local ? Color.accentColor : Color.secondary) - .cornerRadius(10) - } else if !chat.chatInfo.ntfsEnabled && chat.chatInfo.chatType != .local { - Image(systemName: "speaker.slash.fill") - .foregroundColor(.secondary) - } else if chat.chatInfo.chatSettings?.favorite ?? false { - Image(systemName: "star.fill") - .resizable() - .scaledToFill() - .frame(width: 18, height: 18) - .padding(.trailing, 1) - .foregroundColor(.secondary.opacity(0.65)) - } + chatInfoIcon(chat).frame(minWidth: 37, alignment: .trailing) } } - private func messageDraft(_ draft: ComposeState) -> Text { + @ViewBuilder private func chatInfoIcon(_ chat: Chat) -> some View { + let s = chat.chatStats + if s.unreadCount > 0 || s.unreadChat { + let mentionColor = mentionColor(chat) + HStack(alignment: .center, spacing: 2) { + if s.unreadMentions > 0 && s.unreadCount > 1 { + Text("\(MENTION_START)") + .font(userFont <= .xxxLarge ? .body : .callout) + .foregroundColor(mentionColor) + .frame(minWidth: dynamicChatInfoSize, minHeight: dynamicChatInfoSize) + .cornerRadius(dynamicSize(userFont).unreadCorner) + .padding(.bottom, 1) + } + let singleUnreadIsMention = s.unreadMentions > 0 && s.unreadCount == 1 + (singleUnreadIsMention ? Text("\(MENTION_START)") : unreadCountText(s.unreadCount)) + .font(userFont <= .xxxLarge ? .caption : .caption2) + .foregroundColor(.white) + .padding(.horizontal, dynamicSize(userFont).unreadPadding) + .frame(minWidth: dynamicChatInfoSize, minHeight: dynamicChatInfoSize) + .background(singleUnreadIsMention ? mentionColor : chat.chatInfo.ntfsEnabled(false) || chat.chatInfo.chatType == .local ? theme.colors.primary : theme.colors.secondary) + .cornerRadius(dynamicSize(userFont).unreadCorner) + } + .frame(height: dynamicChatInfoSize) + } else if let ntfMode = chat.chatInfo.chatSettings?.enableNtfs, ntfMode != .all { + let iconSize = ntfMode == .mentions ? dynamicChatInfoSize * 0.8 : dynamicChatInfoSize + let iconColor = ntfMode == .mentions ? theme.colors.secondary.opacity(0.7) : theme.colors.secondary + Image(systemName: ntfMode.iconFilled) + .resizable() + .scaledToFill() + .frame(width: iconSize, height: iconSize) + .foregroundColor(iconColor) + } else if chat.chatInfo.chatSettings?.favorite ?? false { + Image(systemName: "star.fill") + .resizable() + .scaledToFill() + .frame(width: dynamicChatInfoSize, height: dynamicChatInfoSize) + .padding(.trailing, 1) + .foregroundColor(theme.colors.secondary.opacity(0.65)) + } else { + Color.clear.frame(width: 0) + } + } + + private func mentionColor(_ chat: Chat) -> Color { + switch chat.chatInfo.chatSettings?.enableNtfs { + case .all: theme.colors.primary + case .mentions: theme.colors.primary + default: theme.colors.secondary + } + } + + private func messageDraft(_ draft: ComposeState) -> (Text, Bool) { let msg = draft.message - return image("rectangle.and.pencil.and.ellipsis", color: .accentColor) - + attachment() - + messageText(msg, parseSimpleXMarkdown(msg), nil, preview: true, showSecrets: false) + let r = messageText(msg, parseSimpleXMarkdown(msg), sender: nil, preview: true, mentions: draft.mentions, userMemberId: nil, showSecrets: nil, backgroundColor: UIColor(theme.colors.background)) + return (image("rectangle.and.pencil.and.ellipsis", color: theme.colors.primary) + + attachment() + + Text(AttributedString(r.string)), + r.hasSecrets) func image(_ s: String, color: Color = Color(uiColor: .tertiaryLabel)) -> Text { - Text(Image(systemName: s)).foregroundColor(color) + Text(" ") + Text(Image(systemName: s)).foregroundColor(color) + textSpace } func attachment() -> Text { switch draft.preview { - case let .filePreview(fileName, _): return image("doc.fill") + Text(fileName) + Text(" ") + case let .filePreview(fileName, _): return image("doc.fill") + Text(fileName) + textSpace case .mediaPreviews: return image("photo") case let .voicePreview(_, duration): return image("play.fill") + Text(durationText(duration)) default: return Text("") @@ -170,19 +282,24 @@ struct ChatPreviewView: View { } } - func chatItemPreview(_ cItem: ChatItem) -> Text { + func chatItemPreview(_ cItem: ChatItem) -> (Text, Bool) { let itemText = cItem.meta.itemDeleted == nil ? cItem.text : markedDeletedText() let itemFormattedText = cItem.meta.itemDeleted == nil ? cItem.formattedText : nil - return messageText(itemText, itemFormattedText, cItem.memberDisplayName, icon: attachment(), preview: true, showSecrets: false) + let r = messageText(itemText, itemFormattedText, sender: cItem.memberDisplayName, preview: true, mentions: cItem.mentions, userMemberId: chat.chatInfo.groupInfo?.membership.memberId, showSecrets: nil, backgroundColor: UIColor(theme.colors.background), prefix: prefix()) + return (Text(AttributedString(r.string)), r.hasSecrets) // same texts are in markedDeletedText in MarkedDeletedItemView, but it returns LocalizedStringKey; // can be refactored into a single function if functions calling these are changed to return same type func markedDeletedText() -> String { - switch cItem.meta.itemDeleted { - case let .moderated(_, byGroupMember): String.localizedStringWithFormat(NSLocalizedString("moderated by %@", comment: "marked deleted chat item preview text"), byGroupMember.displayName) - case .blocked: NSLocalizedString("blocked", comment: "marked deleted chat item preview text") - case .blockedByAdmin: NSLocalizedString("blocked by admin", comment: "marked deleted chat item preview text") - case .deleted, nil: NSLocalizedString("marked deleted", comment: "marked deleted chat item preview text") + if cItem.meta.itemDeleted != nil, cItem.isReport { + "archived report" + } else { + switch cItem.meta.itemDeleted { + case let .moderated(_, byGroupMember): String.localizedStringWithFormat(NSLocalizedString("moderated by %@", comment: "marked deleted chat item preview text"), byGroupMember.displayName) + case .blocked: NSLocalizedString("blocked", comment: "marked deleted chat item preview text") + case .blockedByAdmin: NSLocalizedString("blocked by admin", comment: "marked deleted chat item preview text") + case .deleted, nil: NSLocalizedString("marked deleted", comment: "marked deleted chat item preview text") + } } } @@ -195,20 +312,29 @@ struct ChatPreviewView: View { default: return nil } } + + func prefix() -> NSAttributedString? { + switch cItem.content.msgContent { + case let .report(_, reason): reason.attrString + default: nil + } + } } - @ViewBuilder private func chatMessagePreview(_ cItem: ChatItem?) -> some View { + @ViewBuilder private func chatMessagePreview(_ cItem: ChatItem?, _ hasFilePreview: Bool = false) -> some View { if chatModel.draftChatId == chat.id, let draft = chatModel.draft { - chatPreviewLayout(messageDraft(draft), draft: true) + let (t, hasSecrets) = messageDraft(draft) + chatPreviewLayout(t, draft: true, hasFilePreview: hasFilePreview, hasSecrets: hasSecrets) } else if let cItem = cItem { - chatPreviewLayout(itemStatusMark(cItem) + chatItemPreview(cItem)) + let (t, hasSecrets) = chatItemPreview(cItem) + chatPreviewLayout(itemStatusMark(cItem) + t, hasFilePreview: hasFilePreview, hasSecrets: hasSecrets) } else { switch (chat.chatInfo) { case let .direct(contact): - if contact.activeConn == nil && contact.profile.contactLink != nil { + if contact.activeConn == nil && contact.profile.contactLink != nil && contact.active { chatPreviewInfoText("Tap to Connect") - .foregroundColor(.accentColor) - } else if !contact.ready && contact.activeConn != nil { + .foregroundColor(theme.colors.primary) + } else if !contact.sndReady && contact.activeConn != nil { if contact.nextSendGrpInv { chatPreviewInfoText("send direct message") } else if contact.active { @@ -217,6 +343,7 @@ struct ChatPreviewView: View { } case let .group(groupInfo): switch (groupInfo.membership.memberStatus) { + case .memRejected: chatPreviewInfoText("rejected") case .memInvited: groupInvitationPreviewText(groupInfo) case .memAccepted: chatPreviewInfoText("connecting…") default: EmptyView() @@ -226,13 +353,59 @@ struct ChatPreviewView: View { } } + @ViewBuilder func chatItemContentPreview(_ chat: Chat, _ ci: ChatItem) -> some View { + let mc = ci.content.msgContent + switch mc { + case let .link(_, preview): + smallContentPreview(size: dynamicMediaSize) { + ZStack(alignment: .topTrailing) { + Image(uiImage: imageFromBase64(preview.image) ?? UIImage(systemName: "arrow.up.right")!) + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: dynamicMediaSize, height: dynamicMediaSize) + ZStack { + Image(systemName: "arrow.up.right") + .resizable() + .foregroundColor(Color.white) + .font(.system(size: 15, weight: .black)) + .frame(width: 8, height: 8) + } + .frame(width: 16, height: 16) + .background(Color.black.opacity(0.25)) + .cornerRadius(8) + } + .onTapGesture { + openBrowserAlert(uri: preview.uri) + } + } + case let .image(_, image): + smallContentPreview(size: dynamicMediaSize) { + CIImageView(chatItem: ci, preview: imageFromBase64(image), maxWidth: dynamicMediaSize, smallView: true, showFullScreenImage: $showFullscreenGallery) + } + case let .video(_,image, duration): + smallContentPreview(size: dynamicMediaSize) { + CIVideoView(chatItem: ci, preview: imageFromBase64(image), duration: duration, maxWidth: dynamicMediaSize, videoWidth: nil, smallView: true, showFullscreenPlayer: $showFullscreenGallery) + } + case let .voice(_, duration): + smallContentPreviewVoice(size: dynamicMediaSize) { + CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, allowMenu: Binding.constant(true), smallViewSize: dynamicMediaSize) + } + case .file: + smallContentPreviewFile(size: dynamicMediaSize) { + CIFileView(file: ci.file, edited: ci.meta.itemEdited, smallViewSize: dynamicMediaSize) + } + default: EmptyView() + } + } + + @ViewBuilder private func groupInvitationPreviewText(_ groupInfo: GroupInfo) -> some View { groupInfo.membership.memberIncognito ? chatPreviewInfoText("join as \(groupInfo.membership.memberProfile.displayName)") : chatPreviewInfoText("you are invited to group") } - @ViewBuilder private func chatPreviewInfoText(_ text: LocalizedStringKey) -> some View { + private func chatPreviewInfoText(_ text: LocalizedStringKey) -> some View { Text(text) .frame(maxWidth: .infinity, minHeight: 44, maxHeight: 44, alignment: .topLeading) .padding([.leading, .trailing], 8) @@ -241,64 +414,124 @@ struct ChatPreviewView: View { private func itemStatusMark(_ cItem: ChatItem) -> Text { switch cItem.meta.itemStatus { - case .sndErrorAuth: + case .sndErrorAuth, .sndError: return Text(Image(systemName: "multiply")) .font(.caption) - .foregroundColor(.red) + Text(" ") - case .sndError: + .foregroundColor(.red) + textSpace + case .sndWarning: return Text(Image(systemName: "exclamationmark.triangle.fill")) .font(.caption) - .foregroundColor(.yellow) + Text(" ") + .foregroundColor(.orange) + textSpace default: return Text("") } } @ViewBuilder private func chatStatusImage() -> some View { + let size = dynamicSize(userFont).incognitoSize switch chat.chatInfo { case let .direct(contact): if contact.active && contact.activeConn != nil { - switch (chatModel.contactNetworkStatus(contact)) { - case .connected: incognitoIcon(chat.chatInfo.incognito) - case .error: - Image(systemName: "exclamationmark.circle") - .resizable() - .scaledToFit() - .frame(width: 17, height: 17) - .foregroundColor(.secondary) - default: - ProgressView() - } + NetworkStatusView(contact: contact, size: size) } else { - incognitoIcon(chat.chatInfo.incognito) + incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size) } case .group: if progressByTimeout { ProgressView() + } else if chat.chatStats.reportsCount > 0 { + groupReportsIcon(size: size * 0.8) } else { - incognitoIcon(chat.chatInfo.incognito) + incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size) } default: - incognitoIcon(chat.chatInfo.incognito) + incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size) + } + } + + struct NetworkStatusView: View { + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize + @EnvironmentObject var theme: AppTheme + @ObservedObject var networkModel = NetworkModel.shared + + let contact: Contact + let size: CGFloat + + var body: some View { + let dynamicChatInfoSize = dynamicSize(userFont).chatInfoSize + switch (networkModel.contactNetworkStatus(contact)) { + case .connected: incognitoIcon(contact.contactConnIncognito, theme.colors.secondary, size: size) + case .error: + Image(systemName: "exclamationmark.circle") + .resizable() + .scaledToFit() + .frame(width: dynamicChatInfoSize, height: dynamicChatInfoSize) + .foregroundColor(theme.colors.secondary) + default: + ProgressView() + } } } } -@ViewBuilder func incognitoIcon(_ incognito: Bool) -> some View { +@ViewBuilder func incognitoIcon(_ incognito: Bool, _ secondaryColor: Color, size: CGFloat) -> some View { if incognito { Image(systemName: "theatermasks") .resizable() .scaledToFit() - .frame(width: 22, height: 22) - .foregroundColor(.secondary) + .frame(width: size, height: size) + .foregroundColor(secondaryColor) } else { EmptyView() } } +func groupReportsIcon(size: CGFloat) -> some View { + Image(systemName: "flag") + .resizable() + .scaledToFit() + .frame(width: size, height: size) + .foregroundColor(.red) +} + +func smallContentPreview(size: CGFloat, _ view: @escaping () -> some View) -> some View { + view() + .frame(width: size, height: size) + .cornerRadius(8) + .overlay(RoundedRectangle(cornerSize: CGSize(width: 8, height: 8)) + .strokeBorder(.secondary, lineWidth: 0.3, antialiased: true)) + .padding(.vertical, size / 6) + .padding(.leading, 3) + .offset(x: 6) +} + +func smallContentPreviewVoice(size: CGFloat, _ view: @escaping () -> some View) -> some View { + view() + .frame(height: voiceMessageSizeBasedOnSquareSize(size)) + .padding(.vertical, size / 6) + .padding(.leading, 8) +} + +func smallContentPreviewFile(size: CGFloat, _ view: @escaping () -> some View) -> some View { + view() + .frame(width: size, height: size) + .padding(.vertical, size / 7) + .padding(.leading, 5) +} + func unreadCountText(_ n: Int) -> Text { Text(n > 999 ? "\(n / 1000)k" : n > 0 ? "\(n)" : "") } +private struct ActiveContentPreview: Equatable { + var chat: Chat + var ci: ChatItem + var mc: MsgContent + + static func == (lhs: ActiveContentPreview, rhs: ActiveContentPreview) -> Bool { + lhs.chat.id == rhs.chat.id && lhs.ci.id == rhs.ci.id && lhs.mc == rhs.mc + } +} + struct ChatPreviewView_Previews: PreviewProvider { static var previews: some View { Group { diff --git a/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift b/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift index 42e90232d6..b9f5b984e1 100644 --- a/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift +++ b/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift @@ -11,8 +11,10 @@ import SimpleXChat struct ContactConnectionInfo: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @Environment(\.dismiss) var dismiss: DismissAction @State var contactConnection: PendingContactConnection + @State private var showShortLink: Bool = true @State private var alert: CCInfoAlert? @State private var localAlias = "" @State private var showIncognitoSheet = false @@ -20,7 +22,7 @@ struct ContactConnectionInfo: View { enum CCInfoAlert: Identifiable { case deleteInvitationAlert - case error(title: LocalizedStringKey, error: LocalizedStringKey) + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -48,7 +50,7 @@ struct ContactConnectionInfo: View { Section { if contactConnection.groupLinkId == nil { - settingsRow("pencil") { + settingsRow("pencil", color: theme.colors.secondary) { TextField("Set contact name…", text: $localAlias) .autocapitalization(.none) .autocorrectionDisabled(true) @@ -60,17 +62,23 @@ struct ContactConnectionInfo: View { } if contactConnection.initiated, - let connReqInv = contactConnection.connReqInv { - SimpleXLinkQRCode(uri: simplexChatLink(connReqInv)) + let connLinkInv = contactConnection.connLinkInv { + SimpleXCreatedLinkQRCode(link: connLinkInv, short: $showShortLink) + .id("simplex-invitation-qrcode-\(connLinkInv.simplexChatUri(short: showShortLink))") incognitoEnabled() - shareLinkButton(connReqInv) + shareLinkButton(connLinkInv, short: showShortLink) oneTimeLinkLearnMoreButton() } else { incognitoEnabled() oneTimeLinkLearnMoreButton() } + } header: { + if let connLinkInv = contactConnection.connLinkInv, connLinkInv.connShortLink != nil { + ToggleShortLinkHeader(text: Text(""), link: connLinkInv, short: $showShortLink) + } } footer: { sharedProfileInfo(contactConnection.incognito) + .foregroundColor(theme.colors.secondary) } Section { @@ -82,6 +90,7 @@ struct ContactConnectionInfo: View { } } } + .modifier(ThemedBackground(grouped: true)) if #available(iOS 16, *) { v } else { @@ -99,7 +108,7 @@ struct ContactConnectionInfo: View { } success: { dismiss() } - case let .error(title, error): return Alert(title: Text(title), message: Text(error)) + case let .error(title, error): return mkAlert(title: title, message: error) } } .onAppear { @@ -149,7 +158,7 @@ struct ContactConnectionInfo: View { HStack(spacing: 6) { Text("Incognito") Image(systemName: "info.circle") - .foregroundColor(.accentColor) + .foregroundColor(theme.colors.primary) .font(.system(size: 14)) } .onTapGesture { @@ -164,13 +173,11 @@ struct ContactConnectionInfo: View { } } -private func shareLinkButton(_ connReqInvitation: String) -> some View { +private func shareLinkButton(_ connLinkInvitation: CreatedConnLink, short: Bool) -> some View { Button { - showShareSheet(items: [simplexChatLink(connReqInvitation)]) + showShareSheet(items: [connLinkInvitation.simplexChatUri(short: short)]) } label: { - settingsRow("square.and.arrow.up") { - Text("Share 1-time link") - } + Label("Share 1-time link", systemImage: "square.and.arrow.up") } } @@ -178,11 +185,10 @@ private func oneTimeLinkLearnMoreButton() -> some View { NavigationLink { AddContactLearnMore(showTitle: false) .navigationTitle("One-time invitation link") + .modifier(ThemedBackground()) .navigationBarTitleDisplayMode(.large) } label: { - settingsRow("info.circle") { - Text("Learn more") - } + Label("Learn more", systemImage: "info.circle") } } diff --git a/apps/ios/Shared/Views/ChatList/ContactConnectionView.swift b/apps/ios/Shared/Views/ChatList/ContactConnectionView.swift index d21f347881..f5156d86b8 100644 --- a/apps/ios/Shared/Views/ChatList/ContactConnectionView.swift +++ b/apps/ios/Shared/Views/ChatList/ContactConnectionView.swift @@ -12,9 +12,10 @@ import SimpleXChat struct ContactConnectionView: View { @EnvironmentObject var m: ChatModel @ObservedObject var chat: Chat + @EnvironmentObject var theme: AppTheme + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize @State private var localAlias = "" @FocusState private var aliasTextFieldFocused: Bool - @State private var showContactConnectionInfo = false var body: some View { if case let .contactConnection(conn) = chat.chatInfo { @@ -29,8 +30,7 @@ struct ContactConnectionView: View { .resizable() .scaledToFill() .frame(width: 48, height: 48) - .foregroundColor(Color(uiColor: .secondarySystemBackground)) - .onTapGesture { showContactConnectionInfo = true } + .foregroundColor(Color(uiColor: .tertiarySystemGroupedBackground).asAnotherColorFromSecondaryVariant(theme)) } .frame(width: 63, height: 63) .padding(.leading, 4) @@ -41,7 +41,7 @@ struct ContactConnectionView: View { .font(.title3) .bold() .allowsTightening(false) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .padding(.horizontal, 8) .padding(.top, 1) .padding(.bottom, 0.5) @@ -54,14 +54,14 @@ struct ContactConnectionView: View { .padding(.trailing, 8) .padding(.vertical, 4) .frame(minWidth: 60, alignment: .trailing) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } .padding(.bottom, 2) ZStack(alignment: .topTrailing) { Text(contactConnection.description) .frame(maxWidth: .infinity, alignment: .leading) - incognitoIcon(contactConnection.incognito) + incognitoIcon(contactConnection.incognito, theme.colors.secondary, size: dynamicSize(userFont).incognitoSize) .padding(.top, 26) .frame(maxWidth: .infinity, alignment: .trailing) } @@ -70,9 +70,6 @@ struct ContactConnectionView: View { Spacer() } .frame(maxHeight: .infinity) - .appSheet(isPresented: $showContactConnectionInfo) { - ContactConnectionInfo(contactConnection: contactConnection) - } } } } diff --git a/apps/ios/Shared/Views/ChatList/ContactRequestView.swift b/apps/ios/Shared/Views/ChatList/ContactRequestView.swift index c5c062a6ec..9276bbfc78 100644 --- a/apps/ios/Shared/Views/ChatList/ContactRequestView.swift +++ b/apps/ios/Shared/Views/ChatList/ContactRequestView.swift @@ -11,20 +11,21 @@ import SimpleXChat struct ContactRequestView: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize var contactRequest: UserContactRequest @ObservedObject var chat: Chat var body: some View { HStack(spacing: 8) { - ChatInfoImage(chat: chat) - .frame(width: 63, height: 63) + ChatInfoImage(chat: chat, size: dynamicSize(userFont).profileImageSize) .padding(.leading, 4) VStack(alignment: .leading, spacing: 0) { HStack(alignment: .top) { Text(contactRequest.chatViewName) .font(.title3) .fontWeight(.bold) - .foregroundColor(.accentColor) + .foregroundColor(theme.colors.primary) .padding(.leading, 8) .frame(alignment: .topLeading) Spacer() @@ -33,7 +34,7 @@ struct ContactRequestView: View { .padding(.trailing, 8) .padding(.top, 4) .frame(minWidth: 60, alignment: .trailing) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) } .padding(.bottom, 2) diff --git a/apps/ios/Shared/Views/ChatList/OneHandUICard.swift b/apps/ios/Shared/Views/ChatList/OneHandUICard.swift new file mode 100644 index 0000000000..059f24cc82 --- /dev/null +++ b/apps/ios/Shared/Views/ChatList/OneHandUICard.swift @@ -0,0 +1,51 @@ +// +// OneHandUICard.swift +// SimpleX (iOS) +// +// Created by EP on 06/08/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct OneHandUICard: View { + @EnvironmentObject var theme: AppTheme + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize + @AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true + @AppStorage(DEFAULT_ONE_HAND_UI_CARD_SHOWN) private var oneHandUICardShown = false + @State private var showOneHandUIAlert = false + + var body: some View { + ZStack(alignment: .topTrailing) { + VStack(alignment: .leading, spacing: 8) { + Text("Toggle chat list:").font(.title3) + Toggle("Reachable chat toolbar", isOn: $oneHandUI) + } + Image(systemName: "multiply") + .foregroundColor(theme.colors.secondary) + .onTapGesture { + showOneHandUIAlert = true + } + } + .padding() + .background(theme.appColors.sentMessage) + .cornerRadius(12) + .frame(height: dynamicSize(userFont).rowHeight) + .alert(isPresented: $showOneHandUIAlert) { + Alert( + title: Text("Reachable chat toolbar"), + message: Text("You can change it in Appearance settings."), + dismissButton: .default(Text("Ok")) { + withAnimation { + oneHandUICardShown = true + } + } + ) + } + } +} + +#Preview { + OneHandUICard() +} diff --git a/apps/ios/Shared/Views/ChatList/ServersSummaryView.swift b/apps/ios/Shared/Views/ChatList/ServersSummaryView.swift new file mode 100644 index 0000000000..8b0a8af888 --- /dev/null +++ b/apps/ios/Shared/Views/ChatList/ServersSummaryView.swift @@ -0,0 +1,746 @@ +// +// ServersSummaryView.swift +// SimpleX (iOS) +// +// Created by spaced4ndy on 25.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct ServersSummaryView: View { + @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme + @State private var serversSummary: PresentedServersSummary? = nil + @State private var selectedUserCategory: PresentedUserCategory = .allUsers + @State private var selectedServerType: PresentedServerType = .smp + @State private var selectedSMPServer: String? = nil + @State private var selectedXFTPServer: String? = nil + @State private var timer: Timer? = nil + @State private var alert: SomeAlert? + + @AppStorage(DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE) private var showSubscriptionPercentage = false + + enum PresentedUserCategory { + case currentUser + case allUsers + } + + enum PresentedServerType { + case smp + case xftp + } + + var body: some View { + NavigationView { + viewBody() + .navigationTitle("Servers info") + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + shareButton() + } + } + } + .onAppear { + if m.users.filter({ u in u.user.activeUser || !u.user.hidden }).count == 1 { + selectedUserCategory = .currentUser + } + getServersSummary() + startTimer() + } + .onDisappear { + stopTimer() + } + .alert(item: $alert) { $0.alert } + } + + private func startTimer() { + timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in + if AppChatState.shared.value == .active { + getServersSummary() + } + } + } + + private func getServersSummary() { + do { + serversSummary = try getAgentServersSummary() + } catch let error { + logger.error("getAgentServersSummary error: \(responseError(error))") + } + } + + private func stopTimer() { + timer?.invalidate() + timer = nil + } + + private func shareButton() -> some View { + Button { + if let serversSummary = serversSummary { + showShareSheet(items: [encodePrettyPrinted(serversSummary)]) + } + } label: { + Image(systemName: "square.and.arrow.up") + } + .disabled(serversSummary == nil) + } + + public func encodePrettyPrinted(_ value: T) -> String { + let encoder = jsonEncoder + encoder.outputFormatting = .prettyPrinted + let data = try! encoder.encode(value) + return String(decoding: data, as: UTF8.self) + } + + @ViewBuilder private func viewBody() -> some View { + if let summ = serversSummary { + List { + Group { + if m.users.filter({ u in u.user.activeUser || !u.user.hidden }).count > 1 { + Picker("User selection", selection: $selectedUserCategory) { + Text("All profiles").tag(PresentedUserCategory.allUsers) + Text("Current profile").tag(PresentedUserCategory.currentUser) + } + .pickerStyle(.segmented) + } + + Picker("Server type", selection: $selectedServerType) { + Text("Messages").tag(PresentedServerType.smp) + Text("Files").tag(PresentedServerType.xftp) + } + .pickerStyle(.segmented) + } + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + + switch (selectedUserCategory, selectedServerType) { + case (.allUsers, .smp): + let smpSumm = summ.allUsersSMP + let (totals, curr, prev, prox) = (smpSumm.smpTotals, smpSumm.currentlyUsedSMPServers, smpSumm.previouslyUsedSMPServers, smpSumm.onlyProxiedSMPServers) + + SMPStatsView(stats: totals.stats, statsStartedAt: summ.statsStartedAt) + + smpSubsSection(totals) + + if curr.count > 0 { + smpServersListView(curr, summ.statsStartedAt, "Connected servers") + } + if prev.count > 0 { + smpServersListView(prev, summ.statsStartedAt, "Previously connected servers") + } + if prox.count > 0 { + smpServersListView(prox, summ.statsStartedAt, "Proxied servers", "You are not connected to these servers. Private routing is used to deliver messages to them.") + } + + ServerSessionsView(sess: totals.sessions) + case (.currentUser, .smp): + let smpSumm = summ.currentUserSMP + let (totals, curr, prev, prox) = (smpSumm.smpTotals, smpSumm.currentlyUsedSMPServers, smpSumm.previouslyUsedSMPServers, smpSumm.onlyProxiedSMPServers) + + SMPStatsView(stats: totals.stats, statsStartedAt: summ.statsStartedAt) + + smpSubsSection(totals) + + if curr.count > 0 { + smpServersListView(curr, summ.statsStartedAt, "Connected servers") + } + if prev.count > 0 { + smpServersListView(prev, summ.statsStartedAt, "Previously connected servers") + } + if prox.count > 0 { + smpServersListView(prox, summ.statsStartedAt, "Proxied servers", "You are not connected to these servers. Private routing is used to deliver messages to them.") + } + + ServerSessionsView(sess: totals.sessions) + case (.allUsers, .xftp): + let xftpSumm = summ.allUsersXFTP + let (totals, curr, prev) = (xftpSumm.xftpTotals, xftpSumm.currentlyUsedXFTPServers, xftpSumm.previouslyUsedXFTPServers) + + XFTPStatsView(stats: totals.stats, statsStartedAt: summ.statsStartedAt) + + if curr.count > 0 { + xftpServersListView(curr, summ.statsStartedAt, "Connected servers") + } + if prev.count > 0 { + xftpServersListView(prev, summ.statsStartedAt, "Previously connected servers") + } + + ServerSessionsView(sess: totals.sessions) + case (.currentUser, .xftp): + let xftpSumm = summ.currentUserXFTP + let (totals, curr, prev) = (xftpSumm.xftpTotals, xftpSumm.currentlyUsedXFTPServers, xftpSumm.previouslyUsedXFTPServers) + + XFTPStatsView(stats: totals.stats, statsStartedAt: summ.statsStartedAt) + + if curr.count > 0 { + xftpServersListView(curr, summ.statsStartedAt, "Connected servers") + } + if prev.count > 0 { + xftpServersListView(prev, summ.statsStartedAt, "Previously connected servers") + } + + ServerSessionsView(sess: totals.sessions) + } + + Section { + reconnectAllButton() + resetStatsButton() + } + } + } else { + Text("No info, try to reload") + .foregroundColor(theme.colors.secondary) + .background(theme.colors.background) + } + } + + private func smpSubsSection(_ totals: SMPTotals) -> some View { + Section { + infoRow("Active connections", numOrDash(totals.subs.ssActive)) + infoRow("Total", numOrDash(totals.subs.total)) + Toggle("Show percentage", isOn: $showSubscriptionPercentage) + } header: { + HStack { + Text("Message reception") + SubscriptionStatusIndicatorView(subs: totals.subs, hasSess: totals.sessions.hasSess) + if showSubscriptionPercentage { + SubscriptionStatusPercentageView(subs: totals.subs, hasSess: totals.sessions.hasSess) + } + } + } + } + + private func reconnectAllButton() -> some View { + Button { + alert = SomeAlert( + alert: Alert( + title: Text("Reconnect all servers?"), + message: Text("Reconnect all connected servers to force message delivery. It uses additional traffic."), + primaryButton: .default(Text("Ok")) { + Task { + do { + try await reconnectAllServers() + } catch let error { + alert = SomeAlert( + alert: mkAlert( + title: "Error reconnecting servers", + message: "\(responseError(error))" + ), + id: "error reconnecting servers" + ) + } + } + }, + secondaryButton: .cancel() + ), + id: "reconnect servers question" + ) + } label: { + Text("Reconnect all servers") + } + } + + private func smpServersListView( + _ servers: [SMPServerSummary], + _ statsStartedAt: Date, + _ header: LocalizedStringKey? = nil, + _ footer: LocalizedStringKey? = nil + ) -> some View { + let sortedServers = servers.sorted { + $0.hasSubs == $1.hasSubs + ? serverAddress($0.smpServer) < serverAddress($1.smpServer) + : $0.hasSubs && !$1.hasSubs + } + return Section { + ForEach(sortedServers) { server in + smpServerView(server, statsStartedAt) + } + } header: { + if let header = header { + Text(header) + } + } footer: { + if let footer = footer { + Text(footer) + } + } + } + + private func smpServerView(_ srvSumm: SMPServerSummary, _ statsStartedAt: Date) -> some View { + NavigationLink(tag: srvSumm.id, selection: $selectedSMPServer) { + SMPServerSummaryView( + summary: srvSumm, + statsStartedAt: statsStartedAt + ) + .navigationBarTitle("SMP server") + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } label: { + HStack { + Text(serverAddress(srvSumm.smpServer)) + .lineLimit(1) + if let subs = srvSumm.subs { + Spacer() + if showSubscriptionPercentage { + SubscriptionStatusPercentageView(subs: subs, hasSess: srvSumm.sessionsOrNew.hasSess) + } + SubscriptionStatusIndicatorView(subs: subs, hasSess: srvSumm.sessionsOrNew.hasSess) + } else if let sess = srvSumm.sessions { + Spacer() + Image(systemName: "arrow.up.circle") + .symbolRenderingMode(.palette) + .foregroundStyle(sessIconColor(sess), Color.clear) + } + } + } + } + + private func serverAddress(_ server: String) -> String { + parseServerAddress(server)?.hostnames.first ?? server + } + + private func sessIconColor(_ sess: ServerSessions) -> Color { + let online = m.networkInfo.online + return ( + online && sess.ssConnected > 0 + ? sessionActiveColor + : Color(uiColor: .tertiaryLabel) + ) + } + + private var sessionActiveColor: Color { + let onionHosts = networkUseOnionHostsGroupDefault.get() + return onionHosts == .require ? .indigo : .accentColor + } + + private func xftpServersListView( + _ servers: [XFTPServerSummary], + _ statsStartedAt: Date, + _ header: LocalizedStringKey? = nil, + _ footer: LocalizedStringKey? = nil + ) -> some View { + let sortedServers = servers.sorted { serverAddress($0.xftpServer) < serverAddress($1.xftpServer) } + return Section { + ForEach(sortedServers) { server in + xftpServerView(server, statsStartedAt) + } + } header: { + if let header = header { + Text(header) + } + } footer: { + if let footer = footer { + Text(footer) + } + } + } + + private func xftpServerView(_ srvSumm: XFTPServerSummary, _ statsStartedAt: Date) -> some View { + NavigationLink(tag: srvSumm.id, selection: $selectedXFTPServer) { + XFTPServerSummaryView( + summary: srvSumm, + statsStartedAt: statsStartedAt + ) + .navigationBarTitle("XFTP server") + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } label: { + HStack { + Text(serverAddress(srvSumm.xftpServer)) + .lineLimit(1) + if let inProgressIcon = inProgressIcon(srvSumm) { + Spacer() + Image(systemName: inProgressIcon) + .symbolRenderingMode(.palette) + .foregroundStyle(sessionActiveColor, Color.clear) + } + } + } + } + + private func inProgressIcon(_ srvSumm: XFTPServerSummary) -> String? { + switch (srvSumm.rcvInProgress, srvSumm.sndInProgress, srvSumm.delInProgress) { + case (false, false, false): nil + case (true, false, false): "arrow.down.circle" + case (false, true, false): "arrow.up.circle" + case (false, false, true): "trash.circle" + default: "arrow.up.arrow.down.circle" + } + } + + private func resetStatsButton() -> some View { + Button { + alert = SomeAlert( + alert: Alert( + title: Text("Reset all statistics?"), + message: Text("Servers statistics will be reset - this cannot be undone!"), + primaryButton: .destructive(Text("Reset")) { + Task { + do { + try await resetAgentServersStats() + getServersSummary() + } catch let error { + alert = SomeAlert( + alert: mkAlert( + title: "Error resetting statistics", + message: "\(responseError(error))" + ), + id: "error resetting statistics" + ) + } + } + }, + secondaryButton: .cancel() + ), + id: "reset statistics question" + ) + } label: { + Text("Reset all statistics") + } + } +} + +struct SubscriptionStatusIndicatorView: View { + @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme + var subs: SMPServerSubs + var hasSess: Bool + + var body: some View { + let (color, variableValue, opacity, _) = subscriptionStatusColorAndPercentage( + online: m.networkInfo.online, + usesProxy: networkUseOnionHostsGroupDefault.get() != .no || groupDefaults.string(forKey: GROUP_DEFAULT_NETWORK_SOCKS_PROXY) != nil, + subs: subs, + hasSess: hasSess, + primaryColor: theme.colors.primary + ) + if #available(iOS 16.0, *) { + Image(systemName: "dot.radiowaves.up.forward", variableValue: variableValue) + .foregroundColor(color) + } else { + Image(systemName: "dot.radiowaves.up.forward") + .foregroundColor(color.opacity(opacity)) + } + } +} + +struct SubscriptionStatusPercentageView: View { + @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme + var subs: SMPServerSubs + var hasSess: Bool + + var body: some View { + let (_, _, _, statusPercent) = subscriptionStatusColorAndPercentage( + online: m.networkInfo.online, + usesProxy: networkUseOnionHostsGroupDefault.get() != .no || groupDefaults.string(forKey: GROUP_DEFAULT_NETWORK_SOCKS_PROXY) != nil, + subs: subs, + hasSess: hasSess, + primaryColor: theme.colors.primary + ) + Text(verbatim: "\(Int(floor(statusPercent * 100)))%") + .foregroundColor(.secondary) + .font(.caption) + } +} + +func subscriptionStatusColorAndPercentage(online: Bool, usesProxy: Bool, subs: SMPServerSubs, hasSess: Bool, primaryColor: Color) -> (Color, Double, Double, Double) { + func roundedToQuarter(_ n: Double) -> Double { + n >= 1 ? 1 + : n <= 0 ? 0 + : (n * 4).rounded() / 4 + } + + let activeColor: Color = usesProxy ? .indigo : primaryColor + let noConnColorAndPercent: (Color, Double, Double, Double) = (Color(uiColor: .tertiaryLabel), 1, 1, 0) + let activeSubsRounded = roundedToQuarter(subs.shareOfActive) + + return !online + ? noConnColorAndPercent + : ( + subs.total == 0 && !hasSess + ? (activeColor, 0, 0.33, 0) // On freshly installed app (without chats) and on app start + : ( + subs.ssActive == 0 + ? ( + hasSess ? (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) : noConnColorAndPercent + ) + : ( // ssActive > 0 + hasSess + ? (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) + : (.orange, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) // This would mean implementation error + ) + ) + ) +} + +struct SMPServerSummaryView: View { + var summary: SMPServerSummary + var statsStartedAt: Date + @State private var alert: SomeAlert? + + @AppStorage(DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE) private var showSubscriptionPercentage = false + + var body: some View { + List { + Section("Server address") { + Text(summary.smpServer) + .textSelection(.enabled) + } + + if let stats = summary.stats { + SMPStatsView(stats: stats, statsStartedAt: statsStartedAt) + } + + if let subs = summary.subs { + smpSubsSection(subs) + } + + if let sess = summary.sessions { + ServerSessionsView(sess: sess) + } + } + .alert(item: $alert) { $0.alert } + } + + private func smpSubsSection(_ subs: SMPServerSubs) -> some View { + Section { + infoRow("Active connections", numOrDash(subs.ssActive)) + infoRow("Pending", numOrDash(subs.ssPending)) + infoRow("Total", numOrDash(subs.total)) + reconnectButton() + } header: { + HStack { + Text("Message reception") + SubscriptionStatusIndicatorView(subs: subs, hasSess: summary.sessionsOrNew.hasSess) + if showSubscriptionPercentage { + SubscriptionStatusPercentageView(subs: subs, hasSess: summary.sessionsOrNew.hasSess) + } + } + } + } + + private func reconnectButton() -> some View { + Button { + alert = SomeAlert( + alert: Alert( + title: Text("Reconnect server?"), + message: Text("Reconnect server to force message delivery. It uses additional traffic."), + primaryButton: .default(Text("Ok")) { + Task { + do { + try await reconnectServer(smpServer: summary.smpServer) + } catch let error { + alert = SomeAlert( + alert: mkAlert( + title: "Error reconnecting server", + message: "\(responseError(error))" + ), + id: "error reconnecting server" + ) + } + } + }, + secondaryButton: .cancel() + ), + id: "reconnect server question" + ) + } label: { + Text("Reconnect") + } + } +} + +struct ServerSessionsView: View { + var sess: ServerSessions + + var body: some View { + Section("Transport sessions") { + infoRow("Connected", numOrDash(sess.ssConnected)) + infoRow("Errors", numOrDash(sess.ssErrors)) + infoRow("Connecting", numOrDash(sess.ssConnecting)) + } + } +} + +struct SMPStatsView: View { + var stats: AgentSMPServerStatsData + var statsStartedAt: Date + + var body: some View { + Section { + infoRow("Messages sent", numOrDash(stats._sentDirect + stats._sentViaProxy)) + infoRow("Messages received", numOrDash(stats._recvMsgs)) + NavigationLink { + DetailedSMPStatsView(stats: stats, statsStartedAt: statsStartedAt) + .navigationTitle("Detailed statistics") + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } label: { + Text("Details") + } + } header: { + Text("Statistics") + } footer: { + Text("Starting from \(localTimestamp(statsStartedAt)).") + textNewLine + Text("All data is kept private on your device.") + } + } +} + +private func numOrDash(_ n: Int) -> String { + n == 0 ? "-" : "\(n)" +} + +struct DetailedSMPStatsView: View { + var stats: AgentSMPServerStatsData + var statsStartedAt: Date + + var body: some View { + List { + Section("Sent messages") { + infoRow("Sent total", numOrDash(stats._sentDirect + stats._sentViaProxy)) + infoRowTwoValues("Sent directly", "attempts", stats._sentDirect, stats._sentDirectAttempts) + infoRowTwoValues("Sent via proxy", "attempts", stats._sentViaProxy, stats._sentViaProxyAttempts) + infoRowTwoValues("Proxied", "attempts", stats._sentProxied, stats._sentProxiedAttempts) + Text("Send errors") + infoRow(Text(verbatim: "AUTH"), numOrDash(stats._sentAuthErrs)).padding(.leading, 24) + infoRow(Text(verbatim: "QUOTA"), numOrDash(stats._sentQuotaErrs)).padding(.leading, 24) + infoRow("expired", numOrDash(stats._sentExpiredErrs)).padding(.leading, 24) + infoRow("other", numOrDash(stats._sentOtherErrs)).padding(.leading, 24) + } + Section("Received messages") { + infoRow("Received total", numOrDash(stats._recvMsgs)) + Text("Receive errors") + infoRow("duplicates", numOrDash(stats._recvDuplicates)).padding(.leading, 24) + infoRow("decryption errors", numOrDash(stats._recvCryptoErrs)).padding(.leading, 24) + infoRow("other errors", numOrDash(stats._recvErrs)).padding(.leading, 24) + infoRowTwoValues("Acknowledged", "attempts", stats._ackMsgs, stats._ackAttempts) + Text("Acknowledgement errors") + infoRow(Text(verbatim: "NO_MSG errors"), numOrDash(stats._ackNoMsgErrs)).padding(.leading, 24) + infoRow("other errors", numOrDash(stats._ackOtherErrs)).padding(.leading, 24) + } + Section("Connections") { + infoRow("Created", numOrDash(stats._connCreated)) + infoRow("Secured", numOrDash(stats._connCreated)) + infoRow("Completed", numOrDash(stats._connCompleted)) + infoRowTwoValues("Deleted", "attempts", stats._connDeleted, stats._connDelAttempts) + infoRow("Deletion errors", numOrDash(stats._connDelErrs)) + infoRowTwoValues("Subscribed", "attempts", stats._connSubscribed, stats._connSubAttempts) + infoRow("Subscriptions ignored", numOrDash(stats._connSubIgnored)) + infoRow("Subscription errors", numOrDash(stats._connSubErrs)) + } + Section { + infoRowTwoValues("Enabled", "attempts", stats._ntfKey, stats._ntfKeyAttempts) + infoRowTwoValues("Disabled", "attempts", stats._ntfKeyDeleted, stats._ntfKeyDeleteAttempts) + } header: { + Text("Connection notifications") + } footer: { + Text("Starting from \(localTimestamp(statsStartedAt)).") + } + } + } +} + +private func infoRowTwoValues(_ title: LocalizedStringKey, _ title2: LocalizedStringKey, _ value: Int, _ value2: Int) -> some View { + HStack { + Text(title) + Text(verbatim: " / ").font(.caption2) + Text(title2).font(.caption2) + Spacer() + Group { + if value == 0 && value2 == 0 { + Text(verbatim: "-") + } else { + Text(numOrDash(value)) + Text(verbatim: " / ").font(.caption2) + Text(numOrDash(value2)).font(.caption2) + } + } + .foregroundStyle(.secondary) + } +} + +struct XFTPServerSummaryView: View { + var summary: XFTPServerSummary + var statsStartedAt: Date + + var body: some View { + List { + Section("Server address") { + Text(summary.xftpServer) + .textSelection(.enabled) + } + + if let stats = summary.stats { + XFTPStatsView(stats: stats, statsStartedAt: statsStartedAt) + } + + if let sess = summary.sessions { + ServerSessionsView(sess: sess) + } + } + } +} + +struct XFTPStatsView: View { + var stats: AgentXFTPServerStatsData + var statsStartedAt: Date + @State private var expanded = false + + var body: some View { + Section { + infoRow("Uploaded", prettySize(stats._uploadsSize)) + infoRow("Downloaded", prettySize(stats._downloadsSize)) + NavigationLink { + DetailedXFTPStatsView(stats: stats, statsStartedAt: statsStartedAt) + .navigationTitle("Detailed statistics") + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } label: { + Text("Details") + } + } header: { + Text("Statistics") + } footer: { + Text("Starting from \(localTimestamp(statsStartedAt)).") + textNewLine + Text("All data is kept private on your device.") + } + } +} + +private func prettySize(_ sizeInKB: Int64) -> String { + let kb: Int64 = 1024 + return sizeInKB == 0 ? "-" : ByteCountFormatter.string(fromByteCount: sizeInKB * kb, countStyle: .binary) +} + +struct DetailedXFTPStatsView: View { + var stats: AgentXFTPServerStatsData + var statsStartedAt: Date + + var body: some View { + List { + Section("Uploaded files") { + infoRow("Size", prettySize(stats._uploadsSize)) + infoRowTwoValues("Chunks uploaded", "attempts", stats._uploads, stats._uploadAttempts) + infoRow("Upload errors", numOrDash(stats._uploadErrs)) + infoRowTwoValues("Chunks deleted", "attempts", stats._deletions, stats._deleteAttempts) + infoRow("Deletion errors", numOrDash(stats._deleteErrs)) + } + Section { + infoRow("Size", prettySize(stats._downloadsSize)) + infoRowTwoValues("Chunks downloaded", "attempts", stats._downloads, stats._downloadAttempts) + Text("Download errors") + infoRow(Text(verbatim: "AUTH"), numOrDash(stats._downloadAuthErrs)).padding(.leading, 24) + infoRow("other", numOrDash(stats._downloadErrs)).padding(.leading, 24) + } header: { + Text("Downloaded files") + } footer: { + Text("Starting from \(localTimestamp(statsStartedAt)).") + } + } + } +} + +#Preview { + ServersSummaryView() +} diff --git a/apps/ios/Shared/Views/ChatList/TagListView.swift b/apps/ios/Shared/Views/ChatList/TagListView.swift new file mode 100644 index 0000000000..2063fe15de --- /dev/null +++ b/apps/ios/Shared/Views/ChatList/TagListView.swift @@ -0,0 +1,408 @@ +// +// TagListView.swift +// SimpleX (iOS) +// +// Created by Diogo Cunha on 31/12/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat +import ElegantEmojiPicker + +struct TagEditorNavParams { + let chat: Chat? + let chatListTag: ChatTagData? + let tagId: Int64? +} + +struct TagListView: View { + var chat: Chat? = nil + @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var theme: AppTheme + @EnvironmentObject var chatTagsModel: ChatTagsModel + @EnvironmentObject var m: ChatModel + @State private var editMode = EditMode.inactive + @State private var tagEditorNavParams: TagEditorNavParams? = nil + + var chatTagsIds: [Int64] { chat?.chatInfo.contact?.chatTags ?? chat?.chatInfo.groupInfo?.chatTags ?? [] } + + var body: some View { + List { + Section { + ForEach(chatTagsModel.userTags, id: \.id) { tag in + let text = tag.chatTagText + let emoji = tag.chatTagEmoji + let tagId = tag.chatTagId + let selected = chatTagsIds.contains(tagId) + + HStack { + if let emoji { + Text(emoji) + } else { + Image(systemName: "tag") + } + Text(text) + .padding(.leading, 12) + Spacer() + if chat != nil { + radioButton(selected: selected) + } + } + .contentShape(Rectangle()) + .onTapGesture { + if let c = chat { + setChatTag(tagId: selected ? nil : tagId, chat: c) { dismiss() } + } else { + tagEditorNavParams = TagEditorNavParams(chat: nil, chatListTag: ChatTagData(emoji: emoji, text: text), tagId: tagId) + } + } + .swipeActions(edge: .trailing, allowsFullSwipe: true) { + Button { + showAlert( + NSLocalizedString("Delete list?", comment: "alert title"), + message: String.localizedStringWithFormat(NSLocalizedString("All chats will be removed from the list %@, and the list deleted.", comment: "alert message"), text), + actions: {[ + UIAlertAction( + title: NSLocalizedString("Cancel", comment: "alert action"), + style: .default + ), + UIAlertAction( + title: NSLocalizedString("Delete", comment: "alert action"), + style: .destructive, + handler: { _ in + deleteTag(tagId) + } + ) + ]} + ) + } label: { + Label("Delete", systemImage: "trash.fill") + } + .tint(.red) + } + .swipeActions(edge: .leading, allowsFullSwipe: true) { + Button { + tagEditorNavParams = TagEditorNavParams(chat: nil, chatListTag: ChatTagData(emoji: emoji, text: text), tagId: tagId) + } label: { + Label("Edit", systemImage: "pencil") + } + .tint(theme.colors.primary) + } + .background( + // isActive required to navigate to edit view from any possible tag edited in swipe action + NavigationLink(isActive: Binding(get: { tagEditorNavParams != nil }, set: { _ in tagEditorNavParams = nil })) { + if let params = tagEditorNavParams { + TagListEditor( + chat: params.chat, + tagId: params.tagId, + emoji: params.chatListTag?.emoji, + name: params.chatListTag?.text ?? "" + ) + } + } label: { + EmptyView() + } + .opacity(0) + ) + } + .onMove(perform: moveItem) + + NavigationLink { + TagListEditor(chat: chat) + } label: { + Label("Create list", systemImage: "plus") + } + } header: { + if chat == nil { + editTagsButton() + .textCase(nil) + .frame(maxWidth: .infinity, alignment: .trailing) + } + } + } + .modifier(ThemedBackground(grouped: true)) + .environment(\.editMode, $editMode) + } + + private func editTagsButton() -> some View { + if editMode.isEditing { + Button("Done") { + editMode = .inactive + dismiss() + } + } else { + Button("Edit") { + editMode = .active + } + } + } + + private func radioButton(selected: Bool) -> some View { + Image(systemName: selected ? "checkmark.circle.fill" : "circle") + .imageScale(.large) + .foregroundStyle(selected ? Color.accentColor : Color(.tertiaryLabel)) + } + + private func moveItem(from source: IndexSet, to destination: Int) { + Task { + do { + var tags = chatTagsModel.userTags + tags.move(fromOffsets: source, toOffset: destination) + try await apiReorderChatTags(tagIds: tags.map { $0.chatTagId }) + + await MainActor.run { + chatTagsModel.userTags = tags + } + } catch let error { + showAlert( + NSLocalizedString("Error reordering lists", comment: "alert title"), + message: responseError(error) + ) + } + } + } + + private func deleteTag(_ tagId: Int64) { + Task { + try await apiDeleteChatTag(tagId: tagId) + + await MainActor.run { + chatTagsModel.userTags = chatTagsModel.userTags.filter { $0.chatTagId != tagId } + if case let .userTag(tag) = chatTagsModel.activeFilter, tagId == tag.chatTagId { + chatTagsModel.activeFilter = nil + } + m.chats.forEach { c in + if var contact = c.chatInfo.contact, contact.chatTags.contains(tagId) { + contact.chatTags = contact.chatTags.filter({ $0 != tagId }) + m.updateContact(contact) + } else if var group = c.chatInfo.groupInfo, group.chatTags.contains(tagId) { + group.chatTags = group.chatTags.filter({ $0 != tagId }) + m.updateGroup(group) + } + } + } + } + } +} + +private func setChatTag(tagId: Int64?, chat: Chat, closeSheet: @escaping () -> Void) { + Task { + do { + let tagIds: [Int64] = if let t = tagId { [t] } else {[]} + let (userTags, chatTags) = try await apiSetChatTags( + type: chat.chatInfo.chatType, + id: chat.chatInfo.apiId, + tagIds: tagIds + ) + + await MainActor.run { + let m = ChatModel.shared + let tm = ChatTagsModel.shared + tm.userTags = userTags + if chat.unreadTag, let tags = chat.chatInfo.chatTags { + tm.decTagsReadCount(tags) + } + if var contact = chat.chatInfo.contact { + contact.chatTags = chatTags + m.updateContact(contact) + } else if var group = chat.chatInfo.groupInfo { + group.chatTags = chatTags + m.updateGroup(group) + } + ChatTagsModel.shared.updateChatTagRead(chat, wasUnread: false) + closeSheet() + } + } catch let error { + showAlert( + NSLocalizedString("Error saving chat list", comment: "alert title"), + message: responseError(error) + ) + } + } +} + +struct EmojiPickerView: UIViewControllerRepresentable { + @Binding var selectedEmoji: String? + @Binding var showingPicker: Bool + @Environment(\.presentationMode) var presentationMode + + class Coordinator: NSObject, ElegantEmojiPickerDelegate, UIAdaptivePresentationControllerDelegate { + var parent: EmojiPickerView + + init(parent: EmojiPickerView) { + self.parent = parent + } + + func emojiPicker(_ picker: ElegantEmojiPicker, didSelectEmoji emoji: Emoji?) { + parent.selectedEmoji = emoji?.emoji + parent.showingPicker = false + picker.dismiss(animated: true) + } + + // Called when the picker is dismissed manually (without selection) + func presentationControllerWillDismiss(_ presentationController: UIPresentationController) { + parent.showingPicker = false + } + } + + func makeCoordinator() -> Coordinator { + return Coordinator(parent: self) + } + + func makeUIViewController(context: Context) -> UIViewController { + let config = ElegantConfiguration(showRandom: false, showReset: true, showClose: false) + let picker = ElegantEmojiPicker(delegate: context.coordinator, configuration: config) + + picker.presentationController?.delegate = context.coordinator + + let viewController = UIViewController() + DispatchQueue.main.async { + if let topVC = getTopViewController() { + topVC.present(picker, animated: true) + } + } + + return viewController + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) { + // No need to update the controller after creation + } +} + +struct TagListEditor: View { + @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var chatTagsModel: ChatTagsModel + @EnvironmentObject var theme: AppTheme + var chat: Chat? = nil + var tagId: Int64? = nil + var emoji: String? + var name: String = "" + @State private var newEmoji: String? + @State private var newName: String = "" + @State private var isPickerPresented = false + @State private var saving: Bool? + + var body: some View { + VStack { + List { + let isDuplicateEmojiOrName = chatTagsModel.userTags.contains { tag in + tag.chatTagId != tagId && + ((newEmoji != nil && tag.chatTagEmoji == newEmoji) || tag.chatTagText == trimmedName) + } + + Section { + HStack { + Button { + isPickerPresented = true + } label: { + if let newEmoji { + Text(newEmoji) + } else { + Image(systemName: "face.smiling") + .foregroundColor(.secondary) + } + } + TextField("List name...", text: $newName) + } + + Button { + saving = true + if let tId = tagId { + updateChatTag(tagId: tId, chatTagData: ChatTagData(emoji: newEmoji, text: trimmedName)) + } else { + createChatTag() + } + } label: { + Text( + chat != nil + ? "Add to list" + : "Save list" + ) + } + .disabled(saving != nil || (trimmedName == name && newEmoji == emoji) || trimmedName.isEmpty || isDuplicateEmojiOrName) + } footer: { + if isDuplicateEmojiOrName && saving != false { // if not saved already, to prevent flickering + HStack { + Image(systemName: "exclamationmark.circle") + .foregroundColor(.red) + Text("List name and emoji should be different for all lists.") + .foregroundColor(theme.colors.secondary) + } + } + } + } + + if isPickerPresented { + EmojiPickerView(selectedEmoji: $newEmoji, showingPicker: $isPickerPresented) + } + } + .modifier(ThemedBackground(grouped: true)) + .onAppear { + newEmoji = emoji + newName = name + } + } + + var trimmedName: String { + newName.trimmingCharacters(in: .whitespaces) + } + + private func createChatTag() { + Task { + do { + let text = trimmedName + let userTags = try await apiCreateChatTag( + tag: ChatTagData(emoji: newEmoji , text: text) + ) + await MainActor.run { + saving = false + chatTagsModel.userTags = userTags + } + if let chat, let tag = userTags.first(where: { $0.chatTagText == text && $0.chatTagEmoji == newEmoji}) { + setChatTag(tagId: tag.chatTagId, chat: chat) { dismiss() } + } else { + await MainActor.run { dismiss() } + } + } catch let error { + await MainActor.run { + saving = nil + showAlert( + NSLocalizedString("Error creating list", comment: "alert title"), + message: responseError(error) + ) + } + } + } + } + + private func updateChatTag(tagId: Int64, chatTagData: ChatTagData) { + Task { + do { + try await apiUpdateChatTag(tagId: tagId, tag: chatTagData) + await MainActor.run { + saving = false + for i in 0.. 375 ? 20 : 16 } + private let sectionShape = RoundedRectangle(cornerRadius: 10, style: .continuous) var body: some View { - VStack { - Spacer().frame(height: 1) + let otherUsers: [UserInfo] = m.users + .filter { u in !u.user.hidden && u.user.userId != m.currentUser?.userId } + .sorted(using: KeyPathComparator(\.user.activeOrder, order: .reverse)) + let sectionWidth = max(frameWidth - sectionHorizontalPadding * 2, 0) + let currentUserWidth = max(frameWidth - sectionHorizontalPadding - rowPadding * 2 - 14 - imageSize, 0) + let stopped = m.chatRunning != true + VStack(spacing: sectionSpacing) { + if let user = m.currentUser { + StickyScrollView(resetScroll: $resetScroll) { + HStack(spacing: rowPadding) { + HStack { + ProfileImage(imageStr: user.image, size: imageSize, color: Color(uiColor: .tertiarySystemGroupedBackground)) + .padding(.trailing, 6) + profileName(user).lineLimit(1) + } + .padding(rowPadding) + .frame(width: otherUsers.isEmpty ? sectionWidth : currentUserWidth, alignment: .leading) + .modifier(ListRow { activeSheet = .currentProfile }) + .clipShape(sectionShape) + .disabled(stopped) + .opacity(stopped ? 0.4 : 1) + ForEach(otherUsers) { u in + userView(u, size: imageSize) + .frame(maxWidth: sectionWidth * 0.618) + .fixedSize() + .disabled(stopped) + .opacity(stopped ? 0.4 : 1) + } + } + .padding(.horizontal, sectionHorizontalPadding) + } + .frame(height: 2 * rowPadding + imageSize) + .padding(.top, sectionSpacing) + .overlay(DetermineWidth()) + .onPreferenceChange(DetermineWidth.Key.self) { frameWidth = $0 } + } VStack(spacing: 0) { - ScrollView { - ScrollViewReader { sp in - let users = m.users - .filter({ u in u.user.activeUser || !u.user.hidden }) - .sorted { u, _ in u.user.activeUser } - VStack(spacing: 0) { - ForEach(users) { u in - userView(u) - Divider() - if u.user.activeUser { Divider() } + openSheetOnTap("qrcode", title: m.userAddress == nil ? "Create SimpleX address" : "Your SimpleX address", sheet: .address, disabled: stopped) + openSheetOnTap("switch.2", title: "Chat preferences", sheet: .chatPreferences, disabled: stopped) + openSheetOnTap("person.crop.rectangle.stack", title: "Your chat profiles", sheet: .chatProfiles, disabled: stopped) + openSheetOnTap("desktopcomputer", title: "Use from desktop", sheet: .useFromDesktop, disabled: stopped) + ZStack(alignment: .trailing) { + openSheetOnTap("gearshape", title: "Settings", sheet: .settings, showDivider: false) + Image(systemName: colorScheme == .light ? "sun.max" : "moon.fill") + .resizable() + .scaledToFit() + .symbolRenderingMode(.monochrome) + .foregroundColor(theme.colors.secondary) + .frame(maxWidth: 20, maxHeight: .infinity) + .padding(.horizontal, rowPadding) + .background(Color(.systemBackground).opacity(0.01)) + .onTapGesture { + if (colorScheme == .light) { + ThemeManager.applyTheme(systemDarkThemeDefault.get()) + } else { + ThemeManager.applyTheme(DefaultTheme.LIGHT.themeName) } } - .overlay { - GeometryReader { geo -> Color in - DispatchQueue.main.async { - scrollViewContentSize = geo.size - let scenes = UIApplication.shared.connectedScenes - if let windowScene = scenes.first as? UIWindowScene { - let layoutFrame = windowScene.windows[0].safeAreaLayoutGuide.layoutFrame - disableScrolling = scrollViewContentSize.height + menuButtonHeight + 10 < layoutFrame.height - } - } - return Color.clear - } + .onLongPressGesture { + ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME) } - .onChange(of: userPickerVisible) { visible in - if visible, let u = users.first { - sp.scrollTo(u.id) - } - } - } - } - .simultaneousGesture(DragGesture(minimumDistance: disableScrolling ? 0 : 10000000)) - .frame(maxHeight: scrollViewContentSize.height) - - menuButton("Use from desktop", icon: "desktopcomputer") { - showConnectDesktop = true - withAnimation { - userPickerVisible.toggle() - } - } - Divider() - menuButton("Settings", icon: "gearshape") { - showSettings = true - withAnimation { - userPickerVisible.toggle() - } } } + .clipShape(sectionShape) + .padding(.horizontal, sectionHorizontalPadding) + .padding(.bottom, sectionSpacing) } - .clipShape(RoundedRectangle(cornerRadius: 16)) - .background( - Rectangle() - .fill(fillColor) - .cornerRadius(16) - .shadow(color: .black.opacity(0.12), radius: 24, x: 0, y: 0) - ) - .onPreferenceChange(DetermineWidth.Key.self) { chatViewNameWidth = $0 } - .frame(maxWidth: chatViewNameWidth > 0 ? min(300, chatViewNameWidth + 130) : 300) - .padding(8) - .opacity(userPickerVisible ? 1.0 : 0.0) .onAppear { - do { - m.users = try listUsers() - } catch let error { - logger.error("Error loading users \(responseError(error))") - } - } - } - - private func userView(_ u: UserInfo) -> some View { - let user = u.user - return Button(action: { - if user.activeUser { - showSettings = true - withAnimation { - userPickerVisible.toggle() - } - } else { + // This check prevents the call of listUsers after the app is suspended, and the database is closed. + if case .active = scenePhase { + currentUser = m.currentUser?.userId Task { do { - try await changeActiveUserAsync_(user.userId, viewPwd: nil) - await MainActor.run { userPickerVisible = false } - } catch { + let users = try await listUsersAsync() await MainActor.run { - AlertManager.shared.showAlertMsg( - title: "Error switching profile!", - message: "Error: \(responseError(error))" - ) + m.users = users + currentUser = m.currentUser?.userId } + } catch { + logger.error("Error loading users \(responseError(error))") } } } - }, label: { - HStack(spacing: 0) { - ProfileImage(imageStr: user.image, color: Color(uiColor: .tertiarySystemFill)) - .frame(width: 44, height: 44) - .padding(.trailing, 12) - Text(user.chatViewName) - .fontWeight(user.activeUser ? .medium : .regular) - .foregroundColor(.primary) - .overlay(DetermineWidth()) - Spacer() - if user.activeUser { - Image(systemName: "checkmark") - } else if u.unreadCount > 0 { - unreadCounter(u.unreadCount, color: user.showNtfs ? .accentColor : .secondary) - } else if !user.showNtfs { - Image(systemName: "speaker.slash") + } + .onChange(of: userPickerShown) { + if !$0 { resetScroll() } + } + .modifier(ThemedBackground(grouped: true)) + .disabled(switchingProfile) + } + + private func userView(_ u: UserInfo, size: CGFloat) -> some View { + HStack { + ZStack(alignment: .topTrailing) { + ProfileImage(imageStr: u.user.image, size: size, color: Color(uiColor: .tertiarySystemGroupedBackground)) + if (u.unreadCount > 0) { + UnreadBadge(userInfo: u).offset(x: 4, y: -4) } } - .padding(.trailing) - .padding([.leading, .vertical], 12) - }) - .buttonStyle(PressedButtonStyle(defaultColor: fillColor, pressedColor: Color(uiColor: .secondarySystemFill))) - } - - private func menuButton(_ title: LocalizedStringKey, icon: String, action: @escaping () -> Void) -> some View { - Button(action: action) { - HStack(spacing: 0) { - Text(title) - .overlay(DetermineWidth()) - Spacer() - Image(systemName: icon) - .symbolRenderingMode(.monochrome) - .foregroundColor(.secondary) - } - .padding(.horizontal) - .padding(.vertical, 22) - .frame(height: menuButtonHeight) + .padding(.trailing, 6) + Text(u.user.displayName).font(.title2).lineLimit(1) + } + .padding(rowPadding) + .modifier(ListRow { + switchingProfile = true + Task { + do { + try await changeActiveUserAsync_(u.user.userId, viewPwd: nil) + await MainActor.run { + switchingProfile = false + userPickerShown = false + } + } catch { + await MainActor.run { + switchingProfile = false + showAlert( + NSLocalizedString("Error switching profile!", comment: "alertTitle"), + message: String.localizedStringWithFormat(NSLocalizedString("Error: %@", comment: "alert message"), responseError(error)) + ) + } + } + } + }) + .clipShape(sectionShape) + } + + private func openSheetOnTap(_ icon: String, title: LocalizedStringKey, sheet: UserPickerSheet, showDivider: Bool = true, disabled: Bool = false) -> some View { + ZStack(alignment: .bottom) { + settingsRow(icon, color: theme.colors.secondary) { + Text(title).foregroundColor(.primary).opacity(disabled ? 0.4 : 1) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, rowPadding) + .padding(.vertical, rowVerticalPadding) + .modifier(ListRow { activeSheet = sheet }) + .disabled(disabled) + if showDivider { + Divider().padding(.leading, 52) + } } - .buttonStyle(PressedButtonStyle(defaultColor: fillColor, pressedColor: Color(uiColor: .secondarySystemFill))) } } -private func unreadCounter(_ unread: Int, color: Color) -> some View { - unreadCountText(unread) - .font(.caption) - .foregroundColor(.white) - .padding(.horizontal, 4) - .frame(minWidth: 18, minHeight: 18) - .background(color) - .cornerRadius(10) +struct UnreadBadge: View { + var userInfo: UserInfo + @EnvironmentObject var theme: AppTheme + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize + + var body: some View { + let size = dynamicSize(userFont).chatInfoSize + unreadCountText(userInfo.unreadCount) + .font(userFont <= .xxxLarge ? .caption : .caption2) + .foregroundColor(.white) + .padding(.horizontal, dynamicSize(userFont).unreadPadding) + .frame(minWidth: size, minHeight: size) + .background(userInfo.user.showNtfs ? theme.colors.primary : theme.colors.secondary) + .cornerRadius(dynamicSize(userFont).unreadCorner) + } } +struct ListRow: ViewModifier { + @Environment(\.colorScheme) private var colorScheme: ColorScheme + @State private var touchDown = false + let action: () -> Void + + func body(content: Content) -> some View { + ZStack { + elevatedSecondarySystemGroupedBackground + Color(.systemGray4).opacity(touchDown ? 1 : 0) + content + TouchOverlay(touchDown: $touchDown, action: action) + } + } + + var elevatedSecondarySystemGroupedBackground: Color { + switch colorScheme { + case .dark: Color(0xFF2C2C2E) + default: Color(0xFFFFFFFF) + } + } + + struct TouchOverlay: UIViewRepresentable { + @Binding var touchDown: Bool + let action: () -> Void + + func makeUIView(context: Context) -> TouchView { + let touchView = TouchView() + let gesture = UILongPressGestureRecognizer( + target: touchView, + action: #selector(touchView.longPress(gesture:)) + ) + gesture.delegate = touchView + gesture.minimumPressDuration = 0 + touchView.addGestureRecognizer(gesture) + return touchView + } + + func updateUIView(_ touchView: TouchView, context: Context) { + touchView.representer = self + } + + class TouchView: UIView, UIGestureRecognizerDelegate { + var representer: TouchOverlay? + private var startLocation: CGPoint? + private var task: Task? + + @objc + func longPress(gesture: UILongPressGestureRecognizer) { + switch gesture.state { + case .began: + startLocation = gesture.location(in: nil) + task = Task { + do { + try await Task.sleep(nanoseconds: 200_000000) + await MainActor.run { representer?.touchDown = true } + } catch { } + } + case .ended: + if hitTest(gesture.location(in: self), with: nil) == self { + representer?.action() + } + task?.cancel() + representer?.touchDown = false + case .changed: + if let startLocation { + let location = gesture.location(in: nil) + let dx = location.x - startLocation.x + let dy = location.y - startLocation.y + if sqrt(pow(dx, 2) + pow(dy, 2)) > 10 { gesture.state = .failed } + } + case .cancelled, .failed: + task?.cancel() + representer?.touchDown = false + default: break + } + } + + func gestureRecognizer( + _: UIGestureRecognizer, + shouldRecognizeSimultaneouslyWith: UIGestureRecognizer + ) -> Bool { true } + } + } +} + + struct UserPicker_Previews: PreviewProvider { static var previews: some View { + @State var activeSheet: UserPickerSheet? + let m = ChatModel() m.users = [UserInfo.sampleData, UserInfo.sampleData] return UserPicker( - showSettings: Binding.constant(false), - showConnectDesktop: Binding.constant(false), - userPickerVisible: Binding.constant(true) + userPickerShown: .constant(true), + activeSheet: $activeSheet ) .environmentObject(m) } diff --git a/apps/ios/Shared/Views/Contacts/ContactListNavLink.swift b/apps/ios/Shared/Views/Contacts/ContactListNavLink.swift new file mode 100644 index 0000000000..456c46d318 --- /dev/null +++ b/apps/ios/Shared/Views/Contacts/ContactListNavLink.swift @@ -0,0 +1,266 @@ +// +// ContactListNavLink.swift +// SimpleX (iOS) +// +// Created by Diogo Cunha on 01/08/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct ContactListNavLink: View { + @EnvironmentObject var theme: AppTheme + @ObservedObject var chat: Chat + var showDeletedChatIcon: Bool + @State private var alert: SomeAlert? = nil + @State private var actionSheet: SomeActionSheet? = nil + @State private var sheet: SomeSheet? = nil + @State private var showConnectContactViaAddressDialog = false + @State private var showContactRequestDialog = false + + var body: some View { + let contactType = chatContactType(chat) + + Group { + switch (chat.chatInfo) { + case let .direct(contact): + switch contactType { + case .recent: + recentContactNavLink(contact) + case .chatDeleted: + deletedChatNavLink(contact) + case .card: + contactCardNavLink(contact) + default: + EmptyView() + } + case let .contactRequest(contactRequest): + contactRequestNavLink(contactRequest) + default: + EmptyView() + } + } + .alert(item: $alert) { $0.alert } + .actionSheet(item: $actionSheet) { $0.actionSheet } + .sheet(item: $sheet) { + if #available(iOS 16.0, *) { + $0.content + .presentationDetents([.fraction(0.4)]) + } else { + $0.content + } + } + } + + func recentContactNavLink(_ contact: Contact) -> some View { + Button { + dismissAllSheets(animated: true) { + ItemsModel.shared.loadOpenChat(contact.id) + } + } label: { + contactPreview(contact, titleColor: theme.colors.onBackground) + } + .swipeActions(edge: .trailing, allowsFullSwipe: true) { + Button { + deleteContactDialog( + chat, + contact, + dismissToChatList: false, + showAlert: { alert = $0 }, + showActionSheet: { actionSheet = $0 }, + showSheetContent: { sheet = $0 } + ) + } label: { + Label("Delete", systemImage: "trash") + } + .tint(.red) + } + } + + func deletedChatNavLink(_ contact: Contact) -> some View { + Button { + Task { + await MainActor.run { + dismissAllSheets(animated: true) { + ItemsModel.shared.loadOpenChat(contact.id) + } + } + } + } label: { + contactPreview(contact, titleColor: theme.colors.onBackground) + } + .swipeActions(edge: .trailing, allowsFullSwipe: true) { + Button { + deleteContactDialog( + chat, + contact, + dismissToChatList: false, + showAlert: { alert = $0 }, + showActionSheet: { actionSheet = $0 }, + showSheetContent: { sheet = $0 } + ) + } label: { + Label("Delete", systemImage: "trash") + } + .tint(.red) + } + } + + func contactPreview(_ contact: Contact, titleColor: Color) -> some View { + HStack{ + ProfileImage(imageStr: contact.image, size: 30) + + previewTitle(contact, titleColor: titleColor) + + Spacer() + + HStack { + if showDeletedChatIcon && contact.chatDeleted { + Image(systemName: "archivebox") + .resizable() + .scaledToFit() + .frame(width: 18, height: 18) + .foregroundColor(.secondary.opacity(0.65)) + } else if chat.chatInfo.chatSettings?.favorite ?? false { + Image(systemName: "star.fill") + .resizable() + .scaledToFill() + .frame(width: 18, height: 18) + .foregroundColor(.secondary.opacity(0.65)) + } + if contact.contactConnIncognito { + Image(systemName: "theatermasks") + .resizable() + .scaledToFit() + .frame(width: 22, height: 22) + .foregroundColor(.secondary) + } + } + } + } + + private func previewTitle(_ contact: Contact, titleColor: Color) -> some View { + let t = Text(chat.chatInfo.chatViewName).foregroundColor(titleColor) + return ( + contact.verified == true + ? verifiedIcon + t + : t + ) + .lineLimit(1) + } + + private var verifiedIcon: Text { + (Text(Image(systemName: "checkmark.shield")) + textSpace) + .foregroundColor(.secondary) + .baselineOffset(1) + .kerning(-2) + } + + func contactCardNavLink(_ contact: Contact) -> some View { + Button { + showConnectContactViaAddressDialog = true + } label: { + contactCardPreview(contact) + } + .swipeActions(edge: .trailing, allowsFullSwipe: true) { + Button { + deleteContactDialog( + chat, + contact, + dismissToChatList: false, + showAlert: { alert = $0 }, + showActionSheet: { actionSheet = $0 }, + showSheetContent: { sheet = $0 } + ) + } label: { + Label("Delete", systemImage: "trash") + } + .tint(.red) + } + .confirmationDialog("Connect with \(contact.chatViewName)", isPresented: $showConnectContactViaAddressDialog, titleVisibility: .visible) { + Button("Use current profile") { connectContactViaAddress_(contact, false) } + Button("Use new incognito profile") { connectContactViaAddress_(contact, true) } + } + } + + private func connectContactViaAddress_(_ contact: Contact, _ incognito: Bool) { + Task { + let ok = await connectContactViaAddress(contact.contactId, incognito, showAlert: { alert = SomeAlert(alert: $0, id: "ContactListNavLink connectContactViaAddress") }) + if ok { + ItemsModel.shared.loadOpenChat(contact.id) { + dismissAllSheets(animated: true) { + AlertManager.shared.showAlert(connReqSentAlert(.contact)) + } + } + } + } + } + + func contactCardPreview(_ contact: Contact) -> some View { + HStack{ + ProfileImage(imageStr: contact.image, size: 30) + + Text(chat.chatInfo.chatViewName) + .foregroundColor(.accentColor) + .lineLimit(1) + + Spacer() + + Image(systemName: "envelope") + .resizable() + .scaledToFill() + .frame(width: 14, height: 14) + .foregroundColor(.accentColor) + } + } + + func contactRequestNavLink(_ contactRequest: UserContactRequest) -> some View { + Button { + showContactRequestDialog = true + } label: { + contactRequestPreview(contactRequest) + } + .swipeActions(edge: .trailing, allowsFullSwipe: true) { + Button { + Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) } + } label: { Label("Accept", systemImage: "checkmark") } + .tint(theme.colors.primary) + Button { + Task { await acceptContactRequest(incognito: true, contactRequest: contactRequest) } + } label: { + Label("Accept incognito", systemImage: "theatermasks") + } + .tint(.indigo) + Button { + alert = SomeAlert(alert: rejectContactRequestAlert(contactRequest), id: "rejectContactRequestAlert") + } label: { + Label("Reject", systemImage: "multiply") + } + .tint(.red) + } + .confirmationDialog("Accept connection request?", isPresented: $showContactRequestDialog, titleVisibility: .visible) { + Button("Accept") { Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) } } + Button("Accept incognito") { Task { await acceptContactRequest(incognito: true, contactRequest: contactRequest) } } + Button("Reject (sender NOT notified)", role: .destructive) { Task { await rejectContactRequest(contactRequest) } } + } + } + + func contactRequestPreview(_ contactRequest: UserContactRequest) -> some View { + HStack{ + ProfileImage(imageStr: contactRequest.image, size: 30) + + Text(chat.chatInfo.chatViewName) + .foregroundColor(.accentColor) + .lineLimit(1) + + Spacer() + + Image(systemName: "checkmark") + .resizable() + .scaledToFill() + .frame(width: 14, height: 14) + .foregroundColor(.accentColor) + } + } +} diff --git a/apps/ios/Shared/Views/Database/ChatArchiveView.swift b/apps/ios/Shared/Views/Database/ChatArchiveView.swift deleted file mode 100644 index 65913343d5..0000000000 --- a/apps/ios/Shared/Views/Database/ChatArchiveView.swift +++ /dev/null @@ -1,65 +0,0 @@ -// -// ChatArchiveView.swift -// SimpleXChat -// -// Created by Evgeny on 23/06/2022. -// Copyright © 2022 SimpleX Chat. All rights reserved. -// - -import SwiftUI -import SimpleXChat - -struct ChatArchiveView: View { - var archiveName: String - @AppStorage(DEFAULT_CHAT_ARCHIVE_NAME) private var chatArchiveName: String? - @AppStorage(DEFAULT_CHAT_ARCHIVE_TIME) private var chatArchiveTime: Double = 0 - @State private var showDeleteAlert = false - - var body: some View { - let fileUrl = getDocumentsDirectory().appendingPathComponent(archiveName) - let fileTs = chatArchiveTimeDefault.get() - List { - Section { - settingsRow("square.and.arrow.up") { - Button { - showShareSheet(items: [fileUrl]) - } label: { - Text("Save archive") - } - } - settingsRow("trash") { - Button { - showDeleteAlert = true - } label: { - Text("Delete archive").foregroundColor(.red) - } - } - } header: { - Text("Chat archive") - } footer: { - Text("Created on \(fileTs)") - } - } - .alert(isPresented: $showDeleteAlert) { - Alert( - title: Text("Delete chat archive?"), - primaryButton: .destructive(Text("Delete")) { - do { - try FileManager.default.removeItem(atPath: fileUrl.path) - chatArchiveName = nil - chatArchiveTime = 0 - } catch let error { - logger.error("removeItem error \(String(describing: error))") - } - }, - secondaryButton: .cancel() - ) - } - } -} - -struct ChatArchiveView_Previews: PreviewProvider { - static var previews: some View { - ChatArchiveView(archiveName: "") - } -} diff --git a/apps/ios/Shared/Views/Database/DatabaseEncryptionView.swift b/apps/ios/Shared/Views/Database/DatabaseEncryptionView.swift index 4031c3e00a..441a164f8a 100644 --- a/apps/ios/Shared/Views/Database/DatabaseEncryptionView.swift +++ b/apps/ios/Shared/Views/Database/DatabaseEncryptionView.swift @@ -35,6 +35,7 @@ enum DatabaseEncryptionAlert: Identifiable { struct DatabaseEncryptionView: View { @EnvironmentObject private var m: ChatModel + @EnvironmentObject private var theme: AppTheme @Binding var useKeychain: Bool var migration: Bool @State private var alert: DatabaseEncryptionAlert? = nil @@ -47,6 +48,8 @@ struct DatabaseEncryptionView: View { @State private var confirmNewKey = "" @State private var currentKeyShown = false + let stopChatRunBlockStartChat: (Binding, @escaping () async throws -> Bool) -> Void + var body: some View { ZStack { List { @@ -63,7 +66,7 @@ struct DatabaseEncryptionView: View { private func databaseEncryptionView() -> some View { Section { - settingsRow(storedKey ? "key.fill" : "key", color: storedKey ? .green : .secondary) { + settingsRow(storedKey ? "key.fill" : "key", color: storedKey ? .green : theme.colors.secondary) { Toggle("Save passphrase in Keychain", isOn: $useKeychainToggle) .onChange(of: useKeychainToggle) { _ in if useKeychainToggle { @@ -85,7 +88,7 @@ struct DatabaseEncryptionView: View { PassphraseField(key: $newKey, placeholder: "New passphrase…", valid: validKey(newKey), showStrength: true) PassphraseField(key: $confirmNewKey, placeholder: "Confirm new passphrase…", valid: confirmNewKey == "" || newKey == confirmNewKey) - settingsRow("lock.rotation") { + settingsRow("lock.rotation", color: theme.colors.secondary) { Button(migration ? "Set passphrase" : "Update database passphrase") { alert = currentKey == "" ? (useKeychain ? .encryptDatabaseSaved : .encryptDatabase) @@ -102,6 +105,7 @@ struct DatabaseEncryptionView: View { ) } header: { Text(migration ? "Database passphrase" : "") + .foregroundColor(theme.colors.secondary) } footer: { VStack(alignment: .leading, spacing: 16) { if m.chatDbEncrypted == false { @@ -125,52 +129,68 @@ struct DatabaseEncryptionView: View { } } } + .foregroundColor(theme.colors.secondary) .padding(.top, 1) .font(.callout) } .onAppear { if initialRandomDBPassphrase { currentKey = kcDatabasePassword.get() ?? "" } } - .disabled(m.chatRunning != false) + .disabled(progressIndicator) .alert(item: $alert) { item in databaseEncryptionAlert(item) } } - private func encryptDatabase() { - progressIndicator = true - Task { - do { - encryptionStartedDefault.set(true) - encryptionStartedAtDefault.set(Date.now) - if !m.chatDbChanged { - try apiSaveAppSettings(settings: AppSettings.current.prepareForExport()) - } - try await apiStorageEncryption(currentKey: currentKey, newKey: newKey) - encryptionStartedDefault.set(false) - initialRandomDBPassphraseGroupDefault.set(false) - if migration { - storeDBPassphraseGroupDefault.set(useKeychain) - } - if useKeychain { - if kcDatabasePassword.set(newKey) { - await resetFormAfterEncryption(true) - await operationEnded(.databaseEncrypted) - } else { - await resetFormAfterEncryption() - await operationEnded(.error(title: "Keychain error", error: "Error saving passphrase to keychain")) - } - } else { - if migration { - removePassphraseFromKeyChain() - } - await resetFormAfterEncryption() + private func encryptDatabaseAsync() async -> Bool { + await MainActor.run { + progressIndicator = true + } + do { + encryptionStartedDefault.set(true) + encryptionStartedAtDefault.set(Date.now) + if !m.chatDbChanged { + try apiSaveAppSettings(settings: AppSettings.current.prepareForExport()) + } + try await apiStorageEncryption(currentKey: currentKey, newKey: newKey) + encryptionStartedDefault.set(false) + initialRandomDBPassphraseGroupDefault.set(false) + if migration { + storeDBPassphraseGroupDefault.set(useKeychain) + } + if useKeychain { + if kcDatabasePassword.set(newKey) { + await resetFormAfterEncryption(true) await operationEnded(.databaseEncrypted) - } - } catch let error { - if case .chatCmdError(_, .errorDatabase(.errorExport(.errorNotADatabase))) = error as? ChatResponse { - await operationEnded(.currentPassphraseError) } else { - await operationEnded(.error(title: "Error encrypting database", error: "\(responseError(error))")) + await resetFormAfterEncryption() + await operationEnded(.error(title: "Keychain error", error: "Error saving passphrase to keychain")) } + } else { + if migration { + removePassphraseFromKeyChain() + } + await resetFormAfterEncryption() + await operationEnded(.databaseEncrypted) + } + return true + } catch let error { + if case .errorDatabase(.errorExport(.errorNotADatabase)) = error as? ChatError { + await operationEnded(.currentPassphraseError) + } else { + await operationEnded(.error(title: "Error encrypting database", error: "\(responseError(error))")) + } + return false + } + } + + private func encryptDatabase() { + // it will try to stop and start the chat in case of: non-migration && successful encryption. In migration the chat will remain stopped + if migration { + Task { + await encryptDatabaseAsync() + } + } else { + stopChatRunBlockStartChat($progressIndicator) { + return await encryptDatabaseAsync() } } } @@ -277,6 +297,7 @@ struct DatabaseEncryptionView: View { struct PassphraseField: View { + @EnvironmentObject var theme: AppTheme @Binding var key: String var placeholder: LocalizedStringKey var valid: Bool @@ -287,7 +308,7 @@ struct PassphraseField: View { var body: some View { ZStack(alignment: .leading) { let iconColor = valid - ? (showStrength && key != "" ? PassphraseStrength(passphrase: key).color : .secondary) + ? (showStrength && key != "" ? PassphraseStrength(passphrase: key).color : theme.colors.secondary) : .red Image(systemName: valid ? (showKey ? "eye.slash" : "eye") : "exclamationmark.circle") .resizable() @@ -367,6 +388,6 @@ func validKey(_ s: String) -> Bool { struct DatabaseEncryptionView_Previews: PreviewProvider { static var previews: some View { - DatabaseEncryptionView(useKeychain: Binding.constant(true), migration: false) + DatabaseEncryptionView(useKeychain: Binding.constant(true), migration: false, stopChatRunBlockStartChat: { _, _ in true }) } } diff --git a/apps/ios/Shared/Views/Database/DatabaseErrorView.swift b/apps/ios/Shared/Views/Database/DatabaseErrorView.swift index f8d282a6d1..02a1b87826 100644 --- a/apps/ios/Shared/Views/Database/DatabaseErrorView.swift +++ b/apps/ios/Shared/Views/Database/DatabaseErrorView.swift @@ -11,6 +11,7 @@ import SimpleXChat struct DatabaseErrorView: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @State var status: DBMigrationResult @State private var dbKey = "" @State private var storedDBKey = kcDatabasePassword.get() @@ -27,24 +28,40 @@ struct DatabaseErrorView: View { } } - @ViewBuilder private func databaseErrorView() -> some View { - VStack(alignment: .leading, spacing: 16) { + private func databaseErrorView() -> some View { + VStack(alignment: .center, spacing: 20) { switch status { case let .errorNotADatabase(dbFile): if useKeychain && storedDBKey != nil && storedDBKey != "" { titleText("Wrong database passphrase") Text("Database passphrase is different from saved in the keychain.") + .font(.callout) + .foregroundColor(theme.colors.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal, 25) + databaseKeyField(onSubmit: saveAndRunChat) - saveAndOpenButton() - fileNameText(dbFile) + Spacer() + VStack(spacing: 10) { + saveAndOpenButton() + fileNameText(dbFile) + } } else { titleText("Encrypted database") Text("Database passphrase is required to open chat.") + .font(.callout) + .foregroundColor(theme.colors.secondary) + .multilineTextAlignment(.center) + .padding(.horizontal, 25) + .padding(.bottom, 5) + if useKeychain { databaseKeyField(onSubmit: saveAndRunChat) + Spacer() saveAndOpenButton() } else { databaseKeyField(onSubmit: { runChat() }) + Spacer() openChatButton() } } @@ -52,82 +69,105 @@ struct DatabaseErrorView: View { switch migrationError { case let .upgrade(upMigrations): titleText("Database upgrade") - Button("Upgrade and open chat") { runChat(confirmMigrations: .yesUp) } - fileNameText(dbFile) migrationsText(upMigrations.map(\.upName)) + Spacer() + VStack(spacing: 10) { + Button("Upgrade and open chat") { + runChat(confirmMigrations: .yesUp) + }.buttonStyle(OnboardingButtonStyle(isDisabled: false)) + fileNameText(dbFile) + } case let .downgrade(downMigrations): titleText("Database downgrade") - Text("Warning: you may lose some data!").bold() - Button("Downgrade and open chat") { runChat(confirmMigrations: .yesUpDown) } - fileNameText(dbFile) + Text("Warning: you may lose some data!") + .bold() + .padding(.horizontal, 25) + .multilineTextAlignment(.center) + migrationsText(downMigrations) + Spacer() + VStack(spacing: 10) { + Button("Downgrade and open chat") { + runChat(confirmMigrations: .yesUpDown) + }.buttonStyle(OnboardingButtonStyle(isDisabled: false)) + fileNameText(dbFile) + } case let .migrationError(mtrError): titleText("Incompatible database version") - fileNameText(dbFile) - Text("Error: ") + Text(DatabaseErrorView.mtrErrorDescription(mtrError)) + fileNameText(dbFile, font: .callout) + errorView(Text(mtrErrorDescription(mtrError))) } case let .errorSQL(dbFile, migrationSQLError): titleText("Database error") - fileNameText(dbFile) - Text("Error: \(migrationSQLError)") + fileNameText(dbFile, font: .callout) + errorView(Text("Error: \(migrationSQLError)")) case .errorKeychain: titleText("Keychain error") - Text("Cannot access keychain to save database password") + errorView(Text("Cannot access keychain to save database password")) case .invalidConfirmation: // this can only happen if incorrect parameter is passed - Text(String("Invalid migration confirmation")).font(.title) + titleText("Invalid migration confirmation") + errorView() + case let .unknown(json): titleText("Database error") - Text("Unknown database error: \(json)") + errorView(Text("Unknown database error: \(json)")) case .ok: EmptyView() } if showRestoreDbButton { - Spacer().frame(height: 10) + Spacer() Text("The attempt to change database passphrase was not completed.") + .multilineTextAlignment(.center) + .padding(.horizontal, 25) + .font(.footnote) + restoreDbButton() } } - .padding() + .padding(.horizontal, 25) + .padding(.top, 75) + .padding(.bottom, 25) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .onAppear() { showRestoreDbButton = shouldShowRestoreDbButton() } } - private func titleText(_ s: LocalizedStringKey) -> Text { - Text(s).font(.title) + private func titleText(_ s: LocalizedStringKey) -> some View { + Text(s).font(.largeTitle).bold().multilineTextAlignment(.center) } - private func fileNameText(_ f: String) -> Text { - Text("File: \((f as NSString).lastPathComponent)") + private func fileNameText(_ f: String, font: Font = .caption) -> Text { + Text("File: \((f as NSString).lastPathComponent)").font(font) } - private func migrationsText(_ ms: [String]) -> Text { - Text("Migrations: \(ms.joined(separator: ", "))") - } - - static func mtrErrorDescription(_ err: MTRError) -> LocalizedStringKey { - switch err { - case let .noDown(dbMigrations): - return "database version is newer than the app, but no down migration for: \(dbMigrations.joined(separator: ", "))" - case let .different(appMigration, dbMigration): - return "different migration in the app/database: \(appMigration) / \(dbMigration)" - } + private func migrationsText(_ ms: [String]) -> some View { + (Text("Migrations:").font(.subheadline) + textNewLine + Text(ms.joined(separator: "\n")).font(.caption)) + .multilineTextAlignment(.center) + .padding(.horizontal, 25) } private func databaseKeyField(onSubmit: @escaping () -> Void) -> some View { PassphraseField(key: $dbKey, placeholder: "Enter passphrase…", valid: validKey(dbKey), onSubmit: onSubmit) + .padding(.vertical, 10) + .padding(.horizontal) + .background( + RoundedRectangle(cornerRadius: 10, style: .continuous) + .fill(Color(uiColor: .tertiarySystemFill)) + ) } private func saveAndOpenButton() -> some View { Button("Save passphrase and open chat") { saveAndRunChat() } + .buttonStyle(OnboardingButtonStyle(isDisabled: false)) } private func openChatButton() -> some View { Button("Open chat") { runChat() } + .buttonStyle(OnboardingButtonStyle(isDisabled: false)) } private func saveAndRunChat() { @@ -201,8 +241,9 @@ struct DatabaseErrorView: View { secondaryButton: .cancel() )) } label: { - Text("Restore database backup").foregroundColor(.red) + Text("Restore database backup") } + .buttonStyle(OnboardingButtonStyle(isDisabled: false)) } private func restoreDb() { @@ -217,6 +258,23 @@ struct DatabaseErrorView: View { )) } } + + private func errorView(_ s: Text? = nil) -> some View { + VStack(spacing: 35) { + Image(systemName: "exclamationmark.triangle.fill") + .resizable() + .frame(width: 50, height: 50) + .foregroundColor(.red) + + if let text = s { + text + .multilineTextAlignment(.center) + .font(.footnote) + } + } + .padding() + .frame(maxWidth: .infinity) + } } struct DatabaseErrorView_Previews: PreviewProvider { diff --git a/apps/ios/Shared/Views/Database/DatabaseView.swift b/apps/ios/Shared/Views/Database/DatabaseView.swift index 2e0cd7738f..59eee1338b 100644 --- a/apps/ios/Shared/Views/Database/DatabaseView.swift +++ b/apps/ios/Shared/Views/Database/DatabaseView.swift @@ -15,6 +15,7 @@ enum DatabaseAlert: Identifiable { case importArchive case archiveImported case archiveImportedWithErrors(archiveErrors: [ArchiveError]) + case archiveExportedWithErrors(archivePath: URL, archiveErrors: [ArchiveError]) case deleteChat case chatDeleted case deleteLegacyDatabase @@ -29,6 +30,7 @@ enum DatabaseAlert: Identifiable { case .importArchive: return "importArchive" case .archiveImported: return "archiveImported" case .archiveImportedWithErrors: return "archiveImportedWithErrors" + case .archiveExportedWithErrors: return "archiveExportedWithErrors" case .deleteChat: return "deleteChat" case .chatDeleted: return "chatDeleted" case .deleteLegacyDatabase: return "deleteLegacyDatabase" @@ -41,8 +43,10 @@ enum DatabaseAlert: Identifiable { struct DatabaseView: View { @EnvironmentObject var m: ChatModel - @Binding var showSettings: Bool + @EnvironmentObject var theme: AppTheme + let dismissSettingsSheet: DismissAction @State private var runChat = false + @State private var stoppingChat = false @State private var alert: DatabaseAlert? = nil @State private var showFileImporter = false @State private var importedArchivePath: URL? @@ -54,6 +58,8 @@ struct DatabaseView: View { @State private var useKeychain = storeDBPassphraseGroupDefault.get() @State private var appFilesCountAndSize: (Int, Int)? + @State private var showDatabaseEncryptionView = false + @State var chatItemTTL: ChatItemTTL @State private var currentChatItemTTL: ChatItemTTL = .none @@ -66,7 +72,20 @@ struct DatabaseView: View { } } + @ViewBuilder private func chatDatabaseView() -> some View { + NavigationLink(isActive: $showDatabaseEncryptionView) { + DatabaseEncryptionView(useKeychain: $useKeychain, migration: false, stopChatRunBlockStartChat: { progressIndicator, block in + stopChatRunBlockStartChat(false, progressIndicator, block) + }) + .navigationTitle("Database passphrase") + .modifier(ThemedBackground(grouped: true)) + } label: { + EmptyView() + } + .frame(width: 1, height: 1) + .hidden() + List { let stopped = m.chatRunning == false Section { @@ -82,8 +101,10 @@ struct DatabaseView: View { .disabled(stopped || progressIndicator) } header: { Text("Messages") + .foregroundColor(theme.colors.secondary) } footer: { Text("This setting applies to messages in your current chat profile **\(m.currentUser?.displayName ?? "")**.") + .foregroundColor(theme.colors.secondary) } Section { @@ -96,78 +117,74 @@ struct DatabaseView: View { isOn: $runChat ) .onChange(of: runChat) { _ in - if (runChat) { - startChat() - } else { + if runChat { + DatabaseView.startChat($runChat, $progressIndicator) + } else if !stoppingChat { + stoppingChat = false alert = .stopChat } } } } header: { Text("Run chat") + .foregroundColor(theme.colors.secondary) } footer: { if case .documents = dbContainer { Text("Database will be migrated when the app restarts") + .foregroundColor(theme.colors.secondary) } } Section { let unencrypted = m.chatDbEncrypted == false - let color: Color = unencrypted ? .orange : .secondary + let color: Color = unencrypted ? .orange : theme.colors.secondary settingsRow(unencrypted ? "lock.open" : useKeychain ? "key" : "lock", color: color) { NavigationLink { - DatabaseEncryptionView(useKeychain: $useKeychain, migration: false) + DatabaseEncryptionView(useKeychain: $useKeychain, migration: false, stopChatRunBlockStartChat: { progressIndicator, block in + stopChatRunBlockStartChat(false, progressIndicator, block) + }) .navigationTitle("Database passphrase") + .modifier(ThemedBackground(grouped: true)) } label: { Text("Database passphrase") } } - settingsRow("square.and.arrow.up") { + settingsRow("square.and.arrow.up", color: theme.colors.secondary) { Button("Export database") { if initialRandomDBPassphraseGroupDefault.get() && !unencrypted { - alert = .exportProhibited + showDatabaseEncryptionView = true + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + alert = .exportProhibited + } } else { - exportArchive() + stopChatRunBlockStartChat(stopped, $progressIndicator) { + await exportArchive() + } } } } - settingsRow("square.and.arrow.down") { + settingsRow("square.and.arrow.down", color: theme.colors.secondary) { Button("Import database", role: .destructive) { showFileImporter = true } } - if let archiveName = chatArchiveName { - let title: LocalizedStringKey = chatArchiveTimeDefault.get() < chatLastStartGroupDefault.get() - ? "Old database archive" - : "New database archive" - settingsRow("archivebox") { - NavigationLink { - ChatArchiveView(archiveName: archiveName) - .navigationTitle(title) - } label: { - Text(title) - } - } - } - settingsRow("trash.slash") { + settingsRow("trash.slash", color: theme.colors.secondary) { Button("Delete database", role: .destructive) { alert = .deleteChat } } } header: { Text("Chat database") + .foregroundColor(theme.colors.secondary) } footer: { - Text( - stopped - ? "You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." - : "Stop chat to enable database actions" - ) + Text("You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts.") + .foregroundColor(theme.colors.secondary) } - .disabled(!stopped) + .disabled(progressIndicator) if case .group = dbContainer, legacyDatabase { - Section("Old database") { - settingsRow("trash") { + Section(header: Text("Old database").foregroundColor(theme.colors.secondary)) { + settingsRow("trash", color: theme.colors.secondary) { Button("Delete old database") { alert = .deleteLegacyDatabase } @@ -179,15 +196,18 @@ struct DatabaseView: View { Button(m.users.count > 1 ? "Delete files for all chat profiles" : "Delete all files", role: .destructive) { alert = .deleteFilesAndMedia } - .disabled(!stopped || appFilesCountAndSize?.0 == 0) + .disabled(progressIndicator || appFilesCountAndSize?.0 == 0) } header: { Text("Files & media") + .foregroundColor(theme.colors.secondary) } footer: { if let (fileCount, size) = appFilesCountAndSize { if fileCount == 0 { Text("No received or sent files") + .foregroundColor(theme.colors.secondary) } else { Text("\(fileCount) file(s) with total size of \(ByteCountFormatter.string(fromByteCount: Int64(size), countStyle: .binary))") + .foregroundColor(theme.colors.secondary) } } } @@ -241,7 +261,9 @@ struct DatabaseView: View { title: Text("Import chat database?"), message: Text("Your current chat database will be DELETED and REPLACED with the imported one.") + Text("This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost."), primaryButton: .destructive(Text("Import")) { - importArchive(fileURL) + stopChatRunBlockStartChat(m.chatRunning == false, $progressIndicator) { + await DatabaseView.importArchive(fileURL, $progressIndicator, $alert, false) + } }, secondaryButton: .cancel() ) @@ -249,29 +271,35 @@ struct DatabaseView: View { return Alert(title: Text("Error: no database file")) } case .archiveImported: + let (title, message) = archiveImportedAlertText() + return Alert(title: Text(title), message: Text(message)) + case let .archiveImportedWithErrors(errs): + let (title, message) = archiveImportedWithErrorsAlertText(errs: errs) + return Alert(title: Text(title), message: Text(message)) + case let .archiveExportedWithErrors(archivePath, errs): return Alert( - title: Text("Chat database imported"), - message: Text("Restart the app to use imported chat database") - ) - case .archiveImportedWithErrors: - return Alert( - title: Text("Chat database imported"), - message: Text("Restart the app to use imported chat database") + Text("\n") + Text("Some non-fatal errors occurred during import - you may see Chat console for more details.") + title: Text("Chat database exported"), + message: Text("You may save the exported archive.") + textNewLine + Text("Some file(s) were not exported:") + Text(archiveErrorsText(errs)), + dismissButton: .default(Text("Continue")) { + showShareSheet(items: [archivePath]) + } ) case .deleteChat: return Alert( title: Text("Delete chat profile?"), message: Text("This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost."), primaryButton: .destructive(Text("Delete")) { - deleteChat() + let wasStopped = m.chatRunning == false + stopChatRunBlockStartChat(wasStopped, $progressIndicator) { + _ = await deleteChat() + return true + } }, secondaryButton: .cancel() ) case .chatDeleted: - return Alert( - title: Text("Chat database deleted"), - message: Text("Restart the app to create a new chat profile") - ) + let (title, message) = chatDeletedAlertText() + return Alert(title: Text(title), message: Text(message)) case .deleteLegacyDatabase: return Alert( title: Text("Delete old database?"), @@ -286,7 +314,10 @@ struct DatabaseView: View { title: Text("Delete files and media?"), message: Text("This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain."), primaryButton: .destructive(Text("Delete")) { - deleteFiles() + stopChatRunBlockStartChat(m.chatRunning == false, $progressIndicator) { + deleteFiles() + return true + } }, secondaryButton: .cancel() ) @@ -306,87 +337,184 @@ struct DatabaseView: View { } } - private func authStopChat() { + private func authStopChat(_ onStop: (() -> Void)? = nil) { if UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA) { authenticate(reason: NSLocalizedString("Stop SimpleX", comment: "authentication reason")) { laResult in switch laResult { - case .success: stopChat() - case .unavailable: stopChat() + case .success: stopChat(onStop) + case .unavailable: stopChat(onStop) case .failed: withAnimation { runChat = true } } } } else { - stopChat() + stopChat(onStop) } } - private func stopChat() { + private func stopChat(_ onStop: (() -> Void)? = nil) { Task { do { try await stopChatAsync() + onStop?() } catch let error { await MainActor.run { runChat = true - alert = .error(title: "Error stopping chat", error: responseError(error)) + showAlert("Error stopping chat", message: responseError(error)) } } } } - private func exportArchive() { - progressIndicator = true - Task { + func stopChatRunBlockStartChat( + _ stopped: Bool, + _ progressIndicator: Binding, + _ block: @escaping () async throws -> Bool + ) { + // if the chat was running, the sequence is: stop chat, run block, start chat. + // Otherwise, just run block and do nothing - the toggle will be visible anyway and the user can start the chat or not + if stopped { + Task { + do { + _ = try await block() + } catch { + logger.error("Error while executing block: \(error)") + } + } + } else { + authStopChat { + stoppingChat = true + runChat = false + Task { + // if it throws, let's start chat again anyway + var canStart = false + do { + canStart = try await block() + } catch { + logger.error("Error executing block: \(error)") + canStart = true + } + if canStart { + await MainActor.run { + DatabaseView.startChat($runChat, $progressIndicator) + } + } + } + } + } + } + + static func startChat(_ runChat: Binding, _ progressIndicator: Binding) { + progressIndicator.wrappedValue = true + let m = ChatModel.shared + if m.chatDbChanged { + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + resetChatCtrl() + do { + let hadDatabase = hasDatabase() + try initializeChat(start: true) + m.chatDbChanged = false + AppChatState.shared.set(.active) + if m.chatDbStatus != .ok || !hadDatabase { + // Hide current view and show `DatabaseErrorView` + dismissAllSheets(animated: true) + } + } catch let error { + fatalError("Error starting chat \(responseError(error))") + } + progressIndicator.wrappedValue = false + } + } else { do { - let archivePath = try await exportChatArchive() + _ = try apiStartChat() + runChat.wrappedValue = true + m.chatRunning = true + ChatReceiver.shared.start() + chatLastStartGroupDefault.set(Date.now) + AppChatState.shared.set(.active) + } catch let error { + runChat.wrappedValue = false + showAlert(NSLocalizedString("Error starting chat", comment: ""), message: responseError(error)) + } + progressIndicator.wrappedValue = false + } + } + + private func exportArchive() async -> Bool { + await MainActor.run { + progressIndicator = true + } + do { + let (archivePath, archiveErrors) = try await exportChatArchive() + if archiveErrors.isEmpty { showShareSheet(items: [archivePath]) await MainActor.run { progressIndicator = false } - } catch let error { + } else { await MainActor.run { - alert = .error(title: "Error exporting chat database", error: responseError(error)) + alert = .archiveExportedWithErrors(archivePath: archivePath, archiveErrors: archiveErrors) progressIndicator = false } } + } catch let error { + await MainActor.run { + alert = .error(title: "Error exporting chat database", error: responseError(error)) + progressIndicator = false + } } + return false } - private func importArchive(_ archivePath: URL) { + static func importArchive( + _ archivePath: URL, + _ progressIndicator: Binding, + _ alert: Binding, + _ migration: Bool + ) async -> Bool { if archivePath.startAccessingSecurityScopedResource() { - progressIndicator = true - Task { - do { - try await apiDeleteStorage() - do { - let config = ArchiveConfig(archivePath: archivePath.path) - let archiveErrors = try await apiImportArchive(config: config) - _ = kcDatabasePassword.remove() - if archiveErrors.isEmpty { - await operationEnded(.archiveImported) - } else { - await operationEnded(.archiveImportedWithErrors(archiveErrors: archiveErrors)) - } - } catch let error { - await operationEnded(.error(title: "Error importing chat database", error: responseError(error))) - } - } catch let error { - await operationEnded(.error(title: "Error deleting chat database", error: responseError(error))) - } + defer { archivePath.stopAccessingSecurityScopedResource() } + await MainActor.run { + progressIndicator.wrappedValue = true + } + do { + try await apiDeleteStorage() + try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true) + do { + let config = ArchiveConfig(archivePath: archivePath.path) + let archiveErrors = try await apiImportArchive(config: config) + shouldImportAppSettingsDefault.set(true) + _ = kcDatabasePassword.remove() + if archiveErrors.isEmpty { + await operationEnded(.archiveImported, progressIndicator, alert) + return true + } else { + await operationEnded(.archiveImportedWithErrors(archiveErrors: archiveErrors), progressIndicator, alert) + return migration + } + } catch let error { + await operationEnded(.error(title: "Error importing chat database", error: responseError(error)), progressIndicator, alert) + } + } catch let error { + await operationEnded(.error(title: "Error deleting chat database", error: responseError(error)), progressIndicator, alert) + } } else { - alert = .error(title: "Error accessing database file") + showAlert("Error accessing database file") } + return false } - private func deleteChat() { - progressIndicator = true - Task { - do { - try await deleteChatAsync() - await operationEnded(.chatDeleted) - appFilesCountAndSize = directoryFileCountAndSize(getAppFilesDirectory()) - } catch let error { - await operationEnded(.error(title: "Error deleting database", error: responseError(error))) - } + private func deleteChat() async -> Bool { + await MainActor.run { + progressIndicator = true + } + do { + try await deleteChatAsync() + appFilesCountAndSize = directoryFileCountAndSize(getAppFilesDirectory()) + await DatabaseView.operationEnded(.chatDeleted, $progressIndicator, $alert) + return true + } catch let error { + await DatabaseView.operationEnded(.error(title: "Error deleting database", error: responseError(error)), $progressIndicator, $alert) + return false } } @@ -398,39 +526,30 @@ struct DatabaseView: View { } } - private func operationEnded(_ dbAlert: DatabaseAlert) async { + private static func operationEnded(_ dbAlert: DatabaseAlert, _ progressIndicator: Binding, _ alert: Binding) async { await MainActor.run { + let m = ChatModel.shared m.chatDbChanged = true m.chatInitialized = false - progressIndicator = false - alert = dbAlert + progressIndicator.wrappedValue = false } - } - - private func startChat() { - if m.chatDbChanged { - showSettings = false - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - resetChatCtrl() - do { - try initializeChat(start: true) - m.chatDbChanged = false - AppChatState.shared.set(.active) - } catch let error { - fatalError("Error starting chat \(responseError(error))") - } - } - } else { - do { - _ = try apiStartChat() - runChat = true - m.chatRunning = true - ChatReceiver.shared.start() - chatLastStartGroupDefault.set(Date.now) - AppChatState.shared.set(.active) - } catch let error { - runChat = false - alert = .error(title: "Error starting chat", error: responseError(error)) + await withCheckedContinuation { cont in + let okAlertActionWaiting = UIAlertAction(title: NSLocalizedString("Ok", comment: "alert button"), style: .default, handler: { _ in cont.resume() }) + // show these alerts globally so they are visible when all sheets will be hidden + if case .archiveImported = dbAlert { + let (title, message) = archiveImportedAlertText() + showAlert(title, message: message, actions: { [okAlertActionWaiting] }) + } else if case .archiveImportedWithErrors(let errs) = dbAlert { + let (title, message) = archiveImportedWithErrorsAlertText(errs: errs) + showAlert(title, message: message, actions: { [okAlertActionWaiting] }) + } else if case .chatDeleted = dbAlert { + let (title, message) = chatDeletedAlertText() + showAlert(title, message: message, actions: { [okAlertActionWaiting] }) + } else if case let .error(title, error) = dbAlert { + showAlert("\(title)", message: error, actions: { [okAlertActionWaiting] }) + } else { + alert.wrappedValue = dbAlert + cont.resume() } } } @@ -461,7 +580,7 @@ struct DatabaseView: View { appFilesCountAndSize = directoryFileCountAndSize(getAppFilesDirectory()) do { let chats = try apiGetChats() - m.updateChats(with: chats) + m.updateChats(chats) } catch let error { logger.error("apiGetChats: cannot update chats \(responseError(error))") } @@ -473,6 +592,37 @@ struct DatabaseView: View { } } +func archiveImportedAlertText() -> (String, String) { + ( + NSLocalizedString("Chat database imported", comment: ""), + NSLocalizedString("Restart the app to use imported chat database", comment: "") + ) +} +func archiveImportedWithErrorsAlertText(errs: [ArchiveError]) -> (String, String) { + ( + NSLocalizedString("Chat database imported", comment: ""), + NSLocalizedString("Restart the app to use imported chat database", comment: "") + "\n" + NSLocalizedString("Some non-fatal errors occurred during import:", comment: "") + archiveErrorsText(errs) + ) +} + +private func chatDeletedAlertText() -> (String, String) { + ( + NSLocalizedString("Chat database deleted", comment: ""), + NSLocalizedString("Restart the app to create a new chat profile", comment: "") + ) +} + +func archiveErrorsText(_ errs: [ArchiveError]) -> String { + return "\n" + errs.map(showArchiveError).joined(separator: "\n") + + func showArchiveError(_ err: ArchiveError) -> String { + switch err { + case let .import(importError): importError + case let .fileError(file, fileError): "\(file): \(fileError)" + } + } +} + func stopChatAsync() async throws { try await apiStopChat() ChatReceiver.shared.stop() @@ -492,7 +642,9 @@ func deleteChatAsync() async throws { } struct DatabaseView_Previews: PreviewProvider { + @Environment(\.dismiss) static var mockDismiss + static var previews: some View { - DatabaseView(showSettings: Binding.constant(false), chatItemTTL: .none) + DatabaseView(dismissSettingsSheet: mockDismiss, chatItemTTL: .none) } } diff --git a/apps/ios/Shared/Views/Database/MigrateToAppGroupView.swift b/apps/ios/Shared/Views/Database/MigrateToAppGroupView.swift index ae6af24f53..79c0a42ae0 100644 --- a/apps/ios/Shared/Views/Database/MigrateToAppGroupView.swift +++ b/apps/ios/Shared/Views/Database/MigrateToAppGroupView.swift @@ -117,7 +117,7 @@ struct MigrateToAppGroupView: View { setV3DBMigration(.migration_error) migrationError = "Error starting chat: \(responseError(error))" } - deleteOldArchive() + deleteOldChatArchive() } label: { Text("Start chat") .font(.title) @@ -189,7 +189,8 @@ struct MigrateToAppGroupView: View { Task { do { try apiSaveAppSettings(settings: AppSettings.current.prepareForExport()) - try await apiExportArchive(config: config) + try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true) + _ = try await apiExportArchive(config: config) await MainActor.run { setV3DBMigration(.exported) } } catch let error { await MainActor.run { @@ -221,7 +222,7 @@ struct MigrateToAppGroupView: View { } } -func exportChatArchive(_ storagePath: URL? = nil) async throws -> URL { +func exportChatArchive(_ storagePath: URL? = nil) async throws -> (URL, [ArchiveError]) { let archiveTime = Date.now let ts = archiveTime.ISO8601Format(Date.ISO8601FormatStyle(timeSeparator: .omitted)) let archiveName = "simplex-chat.\(ts).zip" @@ -231,16 +232,19 @@ func exportChatArchive(_ storagePath: URL? = nil) async throws -> URL { if !ChatModel.shared.chatDbChanged { try apiSaveAppSettings(settings: AppSettings.current.prepareForExport()) } - try await apiExportArchive(config: config) + try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true) + let errs = try await apiExportArchive(config: config) if storagePath == nil { - deleteOldArchive() + deleteOldChatArchive() UserDefaults.standard.set(archiveName, forKey: DEFAULT_CHAT_ARCHIVE_NAME) chatArchiveTimeDefault.set(archiveTime) } - return archivePath + return (archivePath, errs) } -func deleteOldArchive() { +/// Deprecated. Remove in the end of 2025. All unused archives should be deleted for the most users til then. +/// Remove DEFAULT_CHAT_ARCHIVE_NAME and DEFAULT_CHAT_ARCHIVE_TIME as well +func deleteOldChatArchive() { let d = UserDefaults.standard if let archiveName = d.string(forKey: DEFAULT_CHAT_ARCHIVE_NAME) { do { diff --git a/apps/ios/Shared/Views/Helpers/AppSheet.swift b/apps/ios/Shared/Views/Helpers/AppSheet.swift index 0e64776ed6..1e334367e8 100644 --- a/apps/ios/Shared/Views/Helpers/AppSheet.swift +++ b/apps/ios/Shared/Views/Helpers/AppSheet.swift @@ -8,42 +8,24 @@ import SwiftUI -private struct SheetIsPresented: ViewModifier where C: View { - var isPresented: Binding - var onDismiss: (() -> Void)? - var sheetContent: () -> C - @Environment(\.scenePhase) var scenePhase +class AppSheetState: ObservableObject { + static let shared = AppSheetState() + @Published var scenePhaseActive: Bool = false - func body(content: Content) -> some View { - content.sheet(isPresented: isPresented, onDismiss: onDismiss) { - sheetContent().modifier(PrivacySensitive()) - } - } -} - -private struct SheetForItem: ViewModifier where T: Identifiable, C: View { - var item: Binding - var onDismiss: (() -> Void)? - var sheetContent: (T) -> C - @Environment(\.scenePhase) var scenePhase - - func body(content: Content) -> some View { - content.sheet(item: item, onDismiss: onDismiss) { it in - sheetContent(it).modifier(PrivacySensitive()) - } + func redactionReasons(_ protectScreen: Bool) -> RedactionReasons { + !protectScreen || scenePhaseActive + ? RedactionReasons() + : RedactionReasons.placeholder } } private struct PrivacySensitive: ViewModifier { @AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false - @Environment(\.scenePhase) var scenePhase + // Screen protection doesn't work for appSheet on iOS 16 if @Environment(\.scenePhase) is used instead of global state + @ObservedObject var appSheetState: AppSheetState = AppSheetState.shared func body(content: Content) -> some View { - if case .active = scenePhase { - content - } else { - content.privacySensitive(protectScreen).redacted(reason: .privacy) - } + content.redacted(reason: appSheetState.redactionReasons(protectScreen)) } } @@ -53,7 +35,9 @@ extension View { onDismiss: (() -> Void)? = nil, content: @escaping () -> Content ) -> some View where Content: View { - modifier(SheetIsPresented(isPresented: isPresented, onDismiss: onDismiss, sheetContent: content)) + sheet(isPresented: isPresented, onDismiss: onDismiss) { + content().modifier(PrivacySensitive()) + } } func appSheet( @@ -61,6 +45,8 @@ extension View { onDismiss: (() -> Void)? = nil, content: @escaping (T) -> Content ) -> some View where T: Identifiable, Content: View { - modifier(SheetForItem(item: item, onDismiss: onDismiss, sheetContent: content)) + sheet(item: item, onDismiss: onDismiss) { it in + content(it).modifier(PrivacySensitive()) + } } } diff --git a/apps/ios/Shared/Views/Helpers/ChatInfoImage.swift b/apps/ios/Shared/Views/Helpers/ChatInfoImage.swift index e253cdd72c..40d62e009b 100644 --- a/apps/ios/Shared/Views/Helpers/ChatInfoImage.swift +++ b/apps/ios/Shared/Views/Helpers/ChatInfoImage.swift @@ -10,24 +10,17 @@ import SwiftUI import SimpleXChat struct ChatInfoImage: View { - @Environment(\.colorScheme) var colorScheme + @EnvironmentObject var theme: AppTheme @ObservedObject var chat: Chat + var size: CGFloat var color = Color(uiColor: .tertiarySystemGroupedBackground) var body: some View { - var iconName: String - switch chat.chatInfo { - case .direct: iconName = "person.crop.circle.fill" - case .group: iconName = "person.2.circle.fill" - case .local: iconName = "folder.circle.fill" - case .contactRequest: iconName = "person.crop.circle.fill" - default: iconName = "circle.fill" - } - let notesColor = colorScheme == .light ? notesChatColorLight : notesChatColorDark - let iconColor = if case .local = chat.chatInfo { notesColor } else { color } + let iconColor = if case .local = chat.chatInfo { theme.appColors.primaryVariant2 } else { color } return ProfileImage( imageStr: chat.chatInfo.image, - iconName: iconName, + iconName: chatIconName(chat.chatInfo), + size: size, color: iconColor ) } @@ -36,8 +29,9 @@ struct ChatInfoImage: View { struct ChatInfoImage_Previews: PreviewProvider { static var previews: some View { ChatInfoImage( - chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []) - , color: Color(red: 0.9, green: 0.9, blue: 0.9) + chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []), + size: 63, + color: Color(red: 0.9, green: 0.9, blue: 0.9) ) .previewLayout(.fixed(width: 63, height: 63)) } diff --git a/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift b/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift new file mode 100644 index 0000000000..9aa6ac86cf --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift @@ -0,0 +1,175 @@ +// +// ChatItemClipShape.swift +// SimpleX (iOS) +// +// Created by Levitating Pineapple on 04/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +/// Modifier, which provides clipping mask for ``ChatItemWithMenu`` view +/// and it's previews: (drag interaction, context menu, etc.) +/// Supports [Dynamic Type](https://developer.apple.com/documentation/uikit/uifont/scaling_fonts_automatically) +/// by retaining pill shape, even when ``ChatItem``'s height is less that twice its corner radius +struct ChatItemClipped: ViewModifier { + @AppStorage(DEFAULT_CHAT_ITEM_ROUNDNESS) private var roundness = defaultChatItemRoundness + @AppStorage(DEFAULT_CHAT_ITEM_TAIL) private var tailEnabled = true + private let chatItem: (content: CIContent, chatDir: CIDirection)? + private let tailVisible: Bool + + init() { + self.chatItem = nil + self.tailVisible = false + } + + init(_ ci: ChatItem, tailVisible: Bool) { + self.chatItem = (ci.content, ci.chatDir) + self.tailVisible = tailVisible + } + + private func shapeStyle() -> ChatItemShape.Style { + if let ci = chatItem { + switch ci.content { + case + .sndMsgContent, + .rcvMsgContent, + .rcvDecryptionError, + .rcvIntegrityError, + .invalidJSON: + let tail = if let mc = ci.content.msgContent, mc.isImageOrVideo && mc.text.isEmpty { + false + } else { + tailVisible + } + return tailEnabled + ? .bubble( + padding: ci.chatDir.sent ? .trailing : .leading, + tailVisible: tail + ) + : .roundRect(radius: msgRectMaxRadius) + case .rcvGroupInvitation, .sndGroupInvitation: + return .roundRect(radius: msgRectMaxRadius) + default: return .roundRect(radius: 8) + } + } else { + return .roundRect(radius: msgRectMaxRadius) + } + } + + func body(content: Content) -> some View { + let clipShape = ChatItemShape( + roundness: roundness, + style: shapeStyle() + ) + content + .contentShape(.dragPreview, clipShape) + .contentShape(.contextMenuPreview, clipShape) + .clipShape(clipShape) + } +} + +struct ChatTailPadding: ViewModifier { + func body(content: Content) -> some View { + content.padding(.horizontal, -msgTailWidth) + } +} + +private let msgRectMaxRadius: Double = 18 +private let msgBubbleMaxRadius: Double = msgRectMaxRadius * 1.2 +private let msgTailWidth: Double = 9 +private let msgTailMinHeight: Double = msgTailWidth * 1.254 // ~56deg +private let msgTailMaxHeight: Double = msgTailWidth * 1.732 // 60deg + +struct ChatItemShape: Shape { + fileprivate enum Style { + case bubble(padding: HorizontalEdge, tailVisible: Bool) + case roundRect(radius: Double) + } + + fileprivate let roundness: Double + fileprivate let style: Style + + func path(in rect: CGRect) -> Path { + switch style { + case let .bubble(padding, tailVisible): + let w = rect.width + let h = rect.height + let rxMax = min(msgBubbleMaxRadius, w / 2) + let ryMax = min(msgBubbleMaxRadius, h / 2) + let rx = roundness * rxMax + let ry = roundness * ryMax + let tailHeight = min(msgTailMinHeight + roundness * (msgTailMaxHeight - msgTailMinHeight), h / 2) + var path = Path() + // top side + path.move(to: CGPoint(x: rx, y: 0)) + path.addLine(to: CGPoint(x: w - rx, y: 0)) + if roundness > 0 { + // top-right corner + path.addQuadCurve(to: CGPoint(x: w, y: ry), control: CGPoint(x: w, y: 0)) + } + if rect.height > 2 * ry { + // right side + path.addLine(to: CGPoint(x: w, y: h - ry)) + } + if roundness > 0 { + // bottom-right corner + path.addQuadCurve(to: CGPoint(x: w - rx, y: h), control: CGPoint(x: w, y: h)) + } + // bottom side + if tailVisible { + path.addLine(to: CGPoint(x: -msgTailWidth, y: h)) + if roundness > 0 { + // bottom-left tail + // distance of control point from touch point, calculated via ratios + let d = tailHeight - msgTailWidth * msgTailWidth / tailHeight + // tail control point + let tc = CGPoint(x: 0, y: h - tailHeight + d * sqrt(roundness)) + // bottom-left tail curve + path.addQuadCurve(to: CGPoint(x: 0, y: h - tailHeight), control: tc) + } else { + path.addLine(to: CGPoint(x: 0, y: h - tailHeight)) + } + if rect.height > ry + tailHeight { + // left side + path.addLine(to: CGPoint(x: 0, y: ry)) + } + } else { + path.addLine(to: CGPoint(x: rx, y: h)) + path.addQuadCurve(to: CGPoint(x: 0, y: h - ry), control: CGPoint(x: 0 , y: h)) + if rect.height > 2 * ry { + // left side + path.addLine(to: CGPoint(x: 0, y: ry)) + } + } + if roundness > 0 { + // top-left corner + path.addQuadCurve(to: CGPoint(x: rx, y: 0), control: CGPoint(x: 0, y: 0)) + } + path.closeSubpath() + return switch padding { + case .leading: path + case .trailing: path + .scale(x: -1, y: 1, anchor: .center) + .path(in: rect) + } + case let .roundRect(radius): + return Path(roundedRect: rect, cornerRadius: radius * roundness) + } + } + + var offset: Double? { + switch style { + case let .bubble(padding, isTailVisible): + if isTailVisible { + switch padding { + case .leading: -msgTailWidth + case .trailing: msgTailWidth + } + } else { 0 } + case .roundRect: 0 + } + } + +} diff --git a/apps/ios/Shared/Views/Helpers/ChatWallpaper.swift b/apps/ios/Shared/Views/Helpers/ChatWallpaper.swift new file mode 100644 index 0000000000..cc5be9e7bb --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/ChatWallpaper.swift @@ -0,0 +1,113 @@ +// +// ChatWallpaper.swift +// SimpleX (iOS) +// +// Created by Avently on 14.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI +import SimpleXChat + +struct ChatViewBackground: ViewModifier { + @Environment(\.colorScheme) var colorScheme + var image: Image + var imageType: WallpaperType + var background: Color + var tint: Color + + func body(content: Content) -> some View { + // Workaround a problem (SwiftUI bug?) when wallpaper is not updated when user changes global theme in iOS settings from dark to light and vice versa + if colorScheme == .light { + back(content) + } else { + back(content) + } + } + + func back(_ content: Content) -> some View { + content.background( + Canvas { context, size in + var image = context.resolve(image) + let rect = CGRectMake(0, 0, size.width, size.height) + func repeatDraw(_ imageScale: CGFloat) { + // Prevent range bounds crash and dividing by zero + if size.height == 0 || size.width == 0 || image.size.height == 0 || image.size.width == 0 { return } + image.shading = .color(tint) + let scale = imageScale * 2.5 // scale wallpaper for iOS + for h in 0 ... Int(size.height / image.size.height / scale) { + for w in 0 ... Int(size.width / image.size.width / scale) { + let rect = CGRectMake(CGFloat(w) * image.size.width * scale, CGFloat(h) * image.size.height * scale, image.size.width * scale, image.size.height * scale) + context.draw(image, in: rect, style: FillStyle()) + } + } + } + context.fill(Path(rect), with: .color(background)) + switch imageType { + case let WallpaperType.preset(filename, scale): + repeatDraw(CGFloat((scale ?? 1) * (PresetWallpaper.from(filename)?.scale ?? 1))) + case let WallpaperType.image(_, scale, scaleType): + let scaleType = scaleType ?? WallpaperScaleType.fill + switch scaleType { + case WallpaperScaleType.repeat: repeatDraw(CGFloat(scale ?? 1)) + case WallpaperScaleType.fill: fallthrough + case WallpaperScaleType.fit: + let scale = scaleType.computeScaleFactor(image.size, size) + let scaledWidth = (image.size.width * scale.0) + let scaledHeight = (image.size.height * scale.1) + context.draw(image, in: CGRectMake(((size.width - scaledWidth) / 2), ((size.height - scaledHeight) / 2), scaledWidth, scaledHeight), style: FillStyle()) + if case WallpaperScaleType.fit = scaleType { + if scaledWidth < size.width { + // has black lines at left and right sides + var x = (size.width - scaledWidth) / 2 + while x > 0 { + context.draw(image, in: CGRectMake((x - scaledWidth), ((size.height - scaledHeight) / 2), scaledWidth, scaledHeight), style: FillStyle()) + x -= scaledWidth + } + x = size.width - (size.width - scaledWidth) / 2 + while x < size.width { + context.draw(image, in: CGRectMake(x, ((size.height - scaledHeight) / 2), scaledWidth, scaledHeight), style: FillStyle()) + + x += scaledWidth + } + } else { + // has black lines at top and bottom sides + var y = (size.height - scaledHeight) / 2 + while y > 0 { + context.draw(image, in: CGRectMake(((size.width - scaledWidth) / 2), (y - scaledHeight), scaledWidth, scaledHeight), style: FillStyle()) + y -= scaledHeight + } + y = size.height - (size.height - scaledHeight) / 2 + while y < size.height { + context.draw(image, in: CGRectMake(((size.width - scaledWidth) / 2), y, scaledWidth, scaledHeight), style: FillStyle()) + y += scaledHeight + } + } + } + context.fill(Path(rect), with: .color(tint)) + } + case WallpaperType.empty: () + } + } + ).ignoresSafeArea(.all) + } +} + +extension PresetWallpaper { + public func toType(_ base: DefaultTheme, _ scale: Float? = nil) -> WallpaperType { + let scale = if let scale { + scale + } else if let type = ChatModel.shared.currentUser?.uiThemes?.preferredMode(base.mode == DefaultThemeMode.dark)?.wallpaper?.toAppWallpaper().type, type.sameType(WallpaperType.preset(filename, nil)) { + type.scale + } else if let scale = themeOverridesDefault.get().first(where: { $0.wallpaper != nil && $0.wallpaper!.preset == filename && $0.base == base })?.wallpaper?.scale { + scale + } else { + Float(1.0) + } + return WallpaperType.preset( + filename, + scale + ) + } +} diff --git a/apps/ios/Shared/Views/Helpers/ContextMenu.swift b/apps/ios/Shared/Views/Helpers/ContextMenu.swift deleted file mode 100644 index 3b82d6eb95..0000000000 --- a/apps/ios/Shared/Views/Helpers/ContextMenu.swift +++ /dev/null @@ -1,98 +0,0 @@ -// -// ContextMenu2.swift -// SimpleX (iOS) -// -// Created by Evgeny on 09/08/2022. -// Copyright © 2022 SimpleX Chat. All rights reserved. -// - -import Foundation -import UIKit -import SwiftUI - -extension View { - func uiKitContextMenu(maxWidth: CGFloat, menu: Binding, allowMenu: Binding) -> some View { - Group { - if allowMenu.wrappedValue { - InteractionView(content: self, maxWidth: maxWidth, menu: menu) - .fixedSize(horizontal: true, vertical: false) - } else { - self - } - } - } -} - -private class HostingViewHolder: UIView { - var contentSize: CGSize = CGSizeMake(0, 0) - override var intrinsicContentSize: CGSize { get { contentSize } } -} - -struct InteractionView: UIViewRepresentable { - let content: Content - var maxWidth: CGFloat - @Binding var menu: UIMenu - - func makeUIView(context: Context) -> UIView { - let view = HostingViewHolder() - view.contentSize = CGSizeMake(maxWidth, .infinity) - view.backgroundColor = .clear - let hostView = UIHostingController(rootView: content) - hostView.view.translatesAutoresizingMaskIntoConstraints = false - let constraints = [ - hostView.view.topAnchor.constraint(equalTo: view.topAnchor), - hostView.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), - hostView.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), - hostView.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), - hostView.view.widthAnchor.constraint(equalTo: view.widthAnchor), - hostView.view.heightAnchor.constraint(equalTo: view.heightAnchor) - ] - view.addSubview(hostView.view) - view.addConstraints(constraints) - view.layer.cornerRadius = 18 - hostView.view.layer.cornerRadius = 18 - let menuInteraction = UIContextMenuInteraction(delegate: context.coordinator) - view.addInteraction(menuInteraction) - return view - } - - func updateUIView(_ uiView: UIView, context: Context) { - (uiView as! HostingViewHolder).contentSize = uiView.subviews[0].sizeThatFits(CGSizeMake(maxWidth, .infinity)) - } - - func makeCoordinator() -> Coordinator { - Coordinator(self) - } - - class Coordinator: NSObject, UIContextMenuInteractionDelegate { - let parent: InteractionView - - init(_ parent: InteractionView) { - self.parent = parent - } - - func contextMenuInteraction( - _ interaction: UIContextMenuInteraction, - configurationForMenuAtLocation location: CGPoint - ) -> UIContextMenuConfiguration? { - UIContextMenuConfiguration( - identifier: nil, - previewProvider: nil, - actionProvider: { [weak self] _ in - guard let self = self else { return nil } - return self.parent.menu - } - ) - } - - // func contextMenuInteraction( - // _ interaction: UIContextMenuInteraction, - // willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, - // animator: UIContextMenuInteractionCommitAnimating - // ) { - // animator.addCompletion { - // print("user tapped") - // } - // } - } -} diff --git a/apps/ios/Shared/Views/Helpers/DetermineWidth.swift b/apps/ios/Shared/Views/Helpers/DetermineWidth.swift index d2a0aaab1d..b05ab17089 100644 --- a/apps/ios/Shared/Views/Helpers/DetermineWidth.swift +++ b/apps/ios/Shared/Views/Helpers/DetermineWidth.swift @@ -21,6 +21,19 @@ struct DetermineWidth: View { } } +struct DetermineWidthImageVideoItem: View { + typealias Key = MaximumWidthImageVideoPreferenceKey + var body: some View { + GeometryReader { proxy in + Color.clear + .preference( + key: MaximumWidthImageVideoPreferenceKey.self, + value: proxy.size.width + ) + } + } +} + struct MaximumWidthPreferenceKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { @@ -28,6 +41,13 @@ struct MaximumWidthPreferenceKey: PreferenceKey { } } +struct MaximumWidthImageVideoPreferenceKey: PreferenceKey { + static var defaultValue: CGFloat = 0 + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = max(value, nextValue()) + } +} + struct DetermineWidth_Previews: PreviewProvider { static var previews: some View { DetermineWidth() diff --git a/apps/ios/Shared/Views/Helpers/ImagePicker.swift b/apps/ios/Shared/Views/Helpers/ImagePicker.swift index fe8d5bbdd4..d7525027e0 100644 --- a/apps/ios/Shared/Views/Helpers/ImagePicker.swift +++ b/apps/ios/Shared/Views/Helpers/ImagePicker.swift @@ -33,6 +33,7 @@ struct LibraryMediaListPicker: UIViewControllerRepresentable { typealias UIViewControllerType = PHPickerViewController var addMedia: (_ content: UploadContent) async -> Void var selectionLimit: Int + var filter: PHPickerFilter = .any(of: [.images, .videos]) var finishedPreprocessing: () -> Void = {} var didFinishPicking: (_ didSelectItems: Bool) async -> Void @@ -148,7 +149,7 @@ struct LibraryMediaListPicker: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> PHPickerViewController { var config = PHPickerConfiguration() - config.filter = .any(of: [.images, .videos]) + config.filter = filter config.selectionLimit = selectionLimit config.selection = .ordered config.preferredAssetRepresentationMode = .current diff --git a/apps/ios/Shared/Views/Helpers/InvertedForegroundStyle.swift b/apps/ios/Shared/Views/Helpers/InvertedForegroundStyle.swift new file mode 100644 index 0000000000..dca413dafe --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/InvertedForegroundStyle.swift @@ -0,0 +1,21 @@ +// +// Test.swift +// SimpleX (iOS) +// +// Created by Levitating Pineapple on 31/08/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +extension View { + @ViewBuilder + func invertedForegroundStyle(enabled: Bool = true) -> some View { + if enabled { + foregroundStyle(Material.ultraThin) + .environment(\.colorScheme, .dark) + .grayscale(1) + .contrast(-20) + } else { self } + } +} diff --git a/apps/ios/Shared/Views/Helpers/KeyboardPadding.swift b/apps/ios/Shared/Views/Helpers/KeyboardPadding.swift deleted file mode 100644 index 45d766ddfd..0000000000 --- a/apps/ios/Shared/Views/Helpers/KeyboardPadding.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// KeyboardPadding.swift -// SimpleX (iOS) -// -// Created by Evgeny on 10/07/2023. -// Copyright © 2023 SimpleX Chat. All rights reserved. -// - -import SwiftUI - -extension View { - @ViewBuilder func keyboardPadding() -> some View { - if #available(iOS 17.0, *) { - GeometryReader { g in - self.padding(.bottom, max(0, ChatModel.shared.keyboardHeight - g.safeAreaInsets.bottom)) - } - } else { - self - } - } -} diff --git a/apps/ios/Shared/Views/Helpers/NavLinkPlain.swift b/apps/ios/Shared/Views/Helpers/NavLinkPlain.swift index 2d5458b9d3..fdc3f2129f 100644 --- a/apps/ios/Shared/Views/Helpers/NavLinkPlain.swift +++ b/apps/ios/Shared/Views/Helpers/NavLinkPlain.swift @@ -7,16 +7,17 @@ // import SwiftUI +import SimpleXChat -struct NavLinkPlain: View { - @State var tag: V - @Binding var selection: V? +struct NavLinkPlain: View { + let chatId: ChatId + @Binding var selection: ChatId? @ViewBuilder var label: () -> Label var disabled = false var body: some View { ZStack { - Button("") { DispatchQueue.main.async { selection = tag } } + Button("") { ItemsModel.shared.loadOpenChat(chatId) } .disabled(disabled) label() } diff --git a/apps/ios/Shared/Views/Helpers/NavStackCompat.swift b/apps/ios/Shared/Views/Helpers/NavStackCompat.swift index 6e3b89c9b8..e9383fc073 100644 --- a/apps/ios/Shared/Views/Helpers/NavStackCompat.swift +++ b/apps/ios/Shared/Views/Helpers/NavStackCompat.swift @@ -17,7 +17,9 @@ struct NavStackCompat : View { if #available(iOS 16, *) { NavigationStack(path: Binding( get: { isActive.wrappedValue ? [true] : [] }, - set: { _ in } + set: { path in + if path.isEmpty { isActive.wrappedValue = false } + } )) { ZStack { NavigationLink(value: true) { EmptyView() } diff --git a/apps/ios/Shared/Views/Helpers/ProfileImage.swift b/apps/ios/Shared/Views/Helpers/ProfileImage.swift index cc4f09ae3b..3eedd56441 100644 --- a/apps/ios/Shared/Views/Helpers/ProfileImage.swift +++ b/apps/ios/Shared/Views/Helpers/ProfileImage.swift @@ -10,28 +10,54 @@ import SwiftUI import SimpleXChat struct ProfileImage: View { + @EnvironmentObject var theme: AppTheme var imageStr: String? = nil var iconName: String = "person.crop.circle.fill" + var size: CGFloat var color = Color(uiColor: .tertiarySystemGroupedBackground) - + var backgroundColor: Color? = nil + var blurred = false + @AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner + var body: some View { - if let image = imageStr, - let data = Data(base64Encoded: dropImagePrefix(image)), - let uiImage = UIImage(data: data) { - Image(uiImage: uiImage) - .resizable() - .clipShape(Circle()) + if let uiImage = imageFromBase64(imageStr) { + clipProfileImage(Image(uiImage: uiImage), size: size, radius: radius, blurred: blurred) } else { + let c = color.asAnotherColorFromSecondaryVariant(theme) Image(systemName: iconName) .resizable() - .foregroundColor(color) + .foregroundColor(c) + .frame(width: size, height: size) + .background(Circle().fill(backgroundColor != nil ? backgroundColor! : .clear)) + } + } +} + +extension Color { + func asAnotherColorFromSecondary(_ theme: AppTheme) -> Color { + return self + } + + func asAnotherColorFromSecondaryVariant(_ theme: AppTheme) -> Color { + let s = theme.colors.secondaryVariant + let l = theme.colors.isLight + return switch self { + case Color(uiColor: .tertiaryLabel): // ChatView title + l ? s.darker(0.05) : s.lighter(0.2) + case Color(uiColor: .tertiarySystemFill): // SettingsView, ChatInfoView + l ? s.darker(0.065) : s.lighter(0.085) + case Color(uiColor: .quaternaryLabel): // ChatListView user picker + l ? s.darker(0.1) : s.lighter(0.1) + case Color(uiColor: .tertiarySystemGroupedBackground): // ChatListView items, forward view + s.asGroupedBackground(theme.base.mode) + default: self } } } struct ProfileImage_Previews: PreviewProvider { static var previews: some View { - ProfileImage(imageStr: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAKHGlDQ1BJQ0MgUHJvZmlsZQAASImFVgdUVNcWve9Nb7QZeu9NehtAem/Sq6gMQ28OQxWxgAQjEFFEREARNFQFg1KjiIhiIQgoYA9IEFBisCAq6OQNJNH4//r/zDpz9ttzz7n73ffWmg0A6QCDxYqD+QCIT0hmezlYywQEBsngngEYCAIy0AC6DGYSy8rDwxUg8Xf9d7wbAxC33tHgzvrP3/9nCISFJzEBgIIRTGey2MkILkawT1oyi4tnEUxjI6IQvMLFkauYqxjQQtewwuoaHy8bBNMBwJMZDHYkAERbhJdJZUYic4hhCNZOCItOQDB3vjkzioFwxLsIXhcRl5IOAImrRzs+fivCk7QRrIL0shAcwNUW+tX8yH/tFfrPXgxG5D84Pi6F+dc9ck+HHJ7g641UMSQlQATQBHEgBaQDGcACbLAVYaIRJhx5Dv+9j77aZ4OsZIFtSEc0iARRIBnpt/9qlvfqpGSQBhjImnCEcUU+NtxnujZy4fbqVEiU/wuXdQyA9S0cDqfzC+e2F4DzyLkSB79wyi0A8KoBcL2GmcJOXePQ3C8MIAJeQAOiQArIAxXuWwMMgSmwBHbAGbgDHxAINgMmojceUZUGMkEWyAX54AA4DMpAJTgJ6sAZ0ALawQVwGVwDt8AQGAUPwQSYBi/AAngHliEIwkEUiAqJQtKQIqQO6UJ0yByyg1whLygQCoEioQQoBcqE9kD5UBFUBlVB9dBPUCd0GboBDUP3oUloDnoNfYRRMBmmwZKwEqwF02Er2AX2gTfBkXAinAHnwPvhUrgaPg23wZfhW/AoPAG/gBdRAEVCCaFkURooOsoG5Y4KQkWg2KidqDxUCaoa1YTqQvWj7qAmUPOoD2gsmoqWQWugTdGOaF80E52I3okuQJeh69Bt6D70HfQkegH9GUPBSGDUMSYYJ0wAJhKThsnFlGBqMK2Yq5hRzDTmHRaLFcIqY42wjthAbAx2O7YAewzbjO3BDmOnsIs4HE4Up44zw7njGLhkXC7uKO407hJuBDeNe48n4aXxunh7fBA+AZ+NL8E34LvxI/gZ/DKBj6BIMCG4E8II2wiFhFOELsJtwjRhmchPVCaaEX2IMcQsYimxiXiV+Ij4hkQiyZGMSZ6kaNJuUinpLOk6aZL0gSxAViPbkIPJKeT95FpyD/k++Q2FQlGiWFKCKMmU/ZR6yhXKE8p7HiqPJo8TTxjPLp5ynjaeEZ6XvAReRV4r3s28GbwlvOd4b/PO8xH4lPhs+Bh8O/nK+Tr5xvkW+an8Ovzu/PH8BfwN/Df4ZwVwAkoCdgJhAjkCJwWuCExRUVR5qg2VSd1DPUW9Sp2mYWnKNCdaDC2fdoY2SFsQFBDUF/QTTBcsF7woOCGEElISchKKEyoUahEaE/ooLClsJRwuvE+4SXhEeElEXMRSJFwkT6RZZFTko6iMqJ1orOhB0XbRx2JoMTUxT7E0seNiV8XmxWnipuJM8TzxFvEHErCEmoSXxHaJkxIDEouSUpIOkizJo5JXJOelhKQspWKkiqW6peakqdLm0tHSxdKXpJ/LCMpYycTJlMr0ySzISsg6yqbIVskOyi7LKcv5ymXLNcs9lifK0+Uj5Ivle+UXFKQV3BQyFRoVHigSFOmKUYpHFPsVl5SUlfyV9iq1K80qiyg7KWcoNyo/UqGoWKgkqlSr3FXFqtJVY1WPqQ6pwWoGalFq5Wq31WF1Q/Vo9WPqw+sw64zXJayrXjeuQdaw0kjVaNSY1BTSdNXM1mzXfKmloBWkdVCrX+uztoF2nPYp7Yc6AjrOOtk6XTqvddV0mbrlunf1KHr2erv0OvRe6avrh+sf179nQDVwM9hr0GvwydDIkG3YZDhnpGAUYlRhNE6n0T3oBfTrxhhja+NdxheMP5gYmiSbtJj8YaphGmvaYDq7Xnl9+PpT66fM5MwYZlVmE+Yy5iHmJ8wnLGQtGBbVFk8t5S3DLGssZ6xUrWKsTlu9tNa2Zlu3Wi/ZmNjssOmxRdk62ObZDtoJ2Pnaldk9sZezj7RvtF9wMHDY7tDjiHF0cTzoOO4k6cR0qndacDZy3uHc50J28XYpc3nqqubKdu1yg92c3Q65PdqguCFhQ7s7cHdyP+T+2EPZI9HjZ0+sp4dnueczLx2vTK9+b6r3Fu8G73c+1j6FPg99VXxTfHv9eP2C/er9lvxt/Yv8JwK0AnYE3AoUC4wO7AjCBfkF1QQtbrTbeHjjdLBBcG7w2CblTembbmwW2xy3+eIW3i2MLedCMCH+IQ0hKwx3RjVjMdQptCJ0gWnDPMJ8EWYZVhw2F24WXhQ+E2EWURQxG2kWeShyLsoiqiRqPtomuiz6VYxjTGXMUqx7bG0sJ84/rjkeHx8S35kgkBCb0LdVamv61mGWOiuXNZFokng4cYHtwq5JgpI2JXUk05A/0oEUlZTvUiZTzVPLU9+n+aWdS+dPT0gf2Ka2bd+2mQz7jB+3o7czt/dmymZmZU7usNpRtRPaGbqzd5f8rpxd07sddtdlEbNis37J1s4uyn67x39PV45kzu6cqe8cvmvM5cll547vNd1b+T36++jvB/fp7Tu673NeWN7NfO38kvyVAmbBzR90fij9gbM/Yv9goWHh8QPYAwkHxg5aHKwr4i/KKJo65HaorVimOK/47eEth2+U6JdUHiEeSTkyUepa2nFU4eiBoytlUWWj5dblzRUSFfsqlo6FHRs5bnm8qVKyMr/y44noE/eqHKraqpWqS05iT6aefHbK71T/j/Qf62vEavJrPtUm1E7UedX11RvV1zdINBQ2wo0pjXOng08PnbE909Gk0VTVLNScfxacTTn7/KeQn8ZaXFp6z9HPNZ1XPF/RSm3Na4PatrUttEe1T3QEdgx3Onf2dpl2tf6s+XPtBdkL5RcFLxZ2E7tzujmXMi4t9rB65i9HXp7q3dL78ErAlbt9nn2DV12uXr9mf+1Kv1X/petm1y/cMLnReZN+s/2W4a22AYOB1l8MfmkdNBxsu210u2PIeKhreP1w94jFyOU7tneu3XW6e2t0w+jwmO/YvfHg8Yl7Yfdm78fdf/Ug9cHyw92PMI/yHvM9Lnki8aT6V9VfmycMJy5O2k4OPPV++nCKOfXit6TfVqZznlGelcxIz9TP6s5emLOfG3q+8fn0C9aL5fnc3/l/r3ip8vL8H5Z/DCwELEy/Yr/ivC54I/qm9q3+295Fj8Un7+LfLS/lvRd9X/eB/qH/o//HmeW0FdxK6SfVT12fXT4/4sRzOCwGm7FqBVBIwhERALyuBYASCAB1CPEPG9f8119+BvrK2fyNwVndL5jhvubRVsMQgCakeCFp04OsQ1LJEgAe5NodqT6WANbT+yf/iqQIPd21PXgaAcDJcjivtwJAQHLFgcNZ9uBwPlUgYhHf1z37f7V9g9e8ITewiP88wfWIYET6HPg21nzjV2fybQVcxfrg2/onng/F50lD/ccAAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAAABigAwAEAAAAAQAAABgAAAAAwf1XlwAAAaNJREFUSA3FlT1LA0EQQBN/gYUYRTksJZVgEbCR/D+7QMr8ABtttBBCsLGzsLG2sxaxED/ie4d77u0dyaE5HHjczn7MzO7M7nU6/yXz+bwLhzCCjTQO+rZhDH3opuNLdRYN4RHe4RIKJ7R34Ro+4AEGSw2mE1iUwT18gpI74WvkGlccu4XNdH0jnYU7cAUacidn37qR23cOxc4aGU0nYUAn7iSWEHkz46w0ocdQu1X6B/AMQZ5o7KfBqNOfwRH8JB7FajGhnmcpKvQe3MEbvILiDm5gPXaCHnZr4vvFGMoEKudKn8YvQIOOe+YzCPop7dwJ3zRfJ7GDuso4YJGRa0yZgg4tUaNXdGrbuZWKKxzYYEJc2xp9AUUjGt8KC2jvgYadF8+10vJyDnNLXwbdiWUZi0fUK01Eoc+AZhCLZVzK4Vq6sDUdz+0dEcbbTTIOJmAyTVhx/WmvrExbv2jtPhWLKodjCtefZiEeZeVZWWSndgwj6fVf3XON8Qwq15++uoqrfYVrow6dGBpCq79ME291jaB0/Q2CPncyht/99MNO/vr9AqW/CGi8sJqbAAAAAElFTkSuQmCC") + ProfileImage(imageStr: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAKHGlDQ1BJQ0MgUHJvZmlsZQAASImFVgdUVNcWve9Nb7QZeu9NehtAem/Sq6gMQ28OQxWxgAQjEFFEREARNFQFg1KjiIhiIQgoYA9IEFBisCAq6OQNJNH4//r/zDpz9ttzz7n73ffWmg0A6QCDxYqD+QCIT0hmezlYywQEBsngngEYCAIy0AC6DGYSy8rDwxUg8Xf9d7wbAxC33tHgzvrP3/9nCISFJzEBgIIRTGey2MkILkawT1oyi4tnEUxjI6IQvMLFkauYqxjQQtewwuoaHy8bBNMBwJMZDHYkAERbhJdJZUYic4hhCNZOCItOQDB3vjkzioFwxLsIXhcRl5IOAImrRzs+fivCk7QRrIL0shAcwNUW+tX8yH/tFfrPXgxG5D84Pi6F+dc9ck+HHJ7g641UMSQlQATQBHEgBaQDGcACbLAVYaIRJhx5Dv+9j77aZ4OsZIFtSEc0iARRIBnpt/9qlvfqpGSQBhjImnCEcUU+NtxnujZy4fbqVEiU/wuXdQyA9S0cDqfzC+e2F4DzyLkSB79wyi0A8KoBcL2GmcJOXePQ3C8MIAJeQAOiQArIAxXuWwMMgSmwBHbAGbgDHxAINgMmojceUZUGMkEWyAX54AA4DMpAJTgJ6sAZ0ALawQVwGVwDt8AQGAUPwQSYBi/AAngHliEIwkEUiAqJQtKQIqQO6UJ0yByyg1whLygQCoEioQQoBcqE9kD5UBFUBlVB9dBPUCd0GboBDUP3oUloDnoNfYRRMBmmwZKwEqwF02Er2AX2gTfBkXAinAHnwPvhUrgaPg23wZfhW/AoPAG/gBdRAEVCCaFkURooOsoG5Y4KQkWg2KidqDxUCaoa1YTqQvWj7qAmUPOoD2gsmoqWQWugTdGOaF80E52I3okuQJeh69Bt6D70HfQkegH9GUPBSGDUMSYYJ0wAJhKThsnFlGBqMK2Yq5hRzDTmHRaLFcIqY42wjthAbAx2O7YAewzbjO3BDmOnsIs4HE4Up44zw7njGLhkXC7uKO407hJuBDeNe48n4aXxunh7fBA+AZ+NL8E34LvxI/gZ/DKBj6BIMCG4E8II2wiFhFOELsJtwjRhmchPVCaaEX2IMcQsYimxiXiV+Ij4hkQiyZGMSZ6kaNJuUinpLOk6aZL0gSxAViPbkIPJKeT95FpyD/k++Q2FQlGiWFKCKMmU/ZR6yhXKE8p7HiqPJo8TTxjPLp5ynjaeEZ6XvAReRV4r3s28GbwlvOd4b/PO8xH4lPhs+Bh8O/nK+Tr5xvkW+an8Ovzu/PH8BfwN/Df4ZwVwAkoCdgJhAjkCJwWuCExRUVR5qg2VSd1DPUW9Sp2mYWnKNCdaDC2fdoY2SFsQFBDUF/QTTBcsF7woOCGEElISchKKEyoUahEaE/ooLClsJRwuvE+4SXhEeElEXMRSJFwkT6RZZFTko6iMqJ1orOhB0XbRx2JoMTUxT7E0seNiV8XmxWnipuJM8TzxFvEHErCEmoSXxHaJkxIDEouSUpIOkizJo5JXJOelhKQspWKkiqW6peakqdLm0tHSxdKXpJ/LCMpYycTJlMr0ySzISsg6yqbIVskOyi7LKcv5ymXLNcs9lifK0+Uj5Ivle+UXFKQV3BQyFRoVHigSFOmKUYpHFPsVl5SUlfyV9iq1K80qiyg7KWcoNyo/UqGoWKgkqlSr3FXFqtJVY1WPqQ6pwWoGalFq5Wq31WF1Q/Vo9WPqw+sw64zXJayrXjeuQdaw0kjVaNSY1BTSdNXM1mzXfKmloBWkdVCrX+uztoF2nPYp7Yc6AjrOOtk6XTqvddV0mbrlunf1KHr2erv0OvRe6avrh+sf179nQDVwM9hr0GvwydDIkG3YZDhnpGAUYlRhNE6n0T3oBfTrxhhja+NdxheMP5gYmiSbtJj8YaphGmvaYDq7Xnl9+PpT66fM5MwYZlVmE+Yy5iHmJ8wnLGQtGBbVFk8t5S3DLGssZ6xUrWKsTlu9tNa2Zlu3Wi/ZmNjssOmxRdk62ObZDtoJ2Pnaldk9sZezj7RvtF9wMHDY7tDjiHF0cTzoOO4k6cR0qndacDZy3uHc50J28XYpc3nqqubKdu1yg92c3Q65PdqguCFhQ7s7cHdyP+T+2EPZI9HjZ0+sp4dnueczLx2vTK9+b6r3Fu8G73c+1j6FPg99VXxTfHv9eP2C/er9lvxt/Yv8JwK0AnYE3AoUC4wO7AjCBfkF1QQtbrTbeHjjdLBBcG7w2CblTembbmwW2xy3+eIW3i2MLedCMCH+IQ0hKwx3RjVjMdQptCJ0gWnDPMJ8EWYZVhw2F24WXhQ+E2EWURQxG2kWeShyLsoiqiRqPtomuiz6VYxjTGXMUqx7bG0sJ84/rjkeHx8S35kgkBCb0LdVamv61mGWOiuXNZFokng4cYHtwq5JgpI2JXUk05A/0oEUlZTvUiZTzVPLU9+n+aWdS+dPT0gf2Ka2bd+2mQz7jB+3o7czt/dmymZmZU7usNpRtRPaGbqzd5f8rpxd07sddtdlEbNis37J1s4uyn67x39PV45kzu6cqe8cvmvM5cll547vNd1b+T36++jvB/fp7Tu673NeWN7NfO38kvyVAmbBzR90fij9gbM/Yv9goWHh8QPYAwkHxg5aHKwr4i/KKJo65HaorVimOK/47eEth2+U6JdUHiEeSTkyUepa2nFU4eiBoytlUWWj5dblzRUSFfsqlo6FHRs5bnm8qVKyMr/y44noE/eqHKraqpWqS05iT6aefHbK71T/j/Qf62vEavJrPtUm1E7UedX11RvV1zdINBQ2wo0pjXOng08PnbE909Gk0VTVLNScfxacTTn7/KeQn8ZaXFp6z9HPNZ1XPF/RSm3Na4PatrUttEe1T3QEdgx3Onf2dpl2tf6s+XPtBdkL5RcFLxZ2E7tzujmXMi4t9rB65i9HXp7q3dL78ErAlbt9nn2DV12uXr9mf+1Kv1X/petm1y/cMLnReZN+s/2W4a22AYOB1l8MfmkdNBxsu210u2PIeKhreP1w94jFyOU7tneu3XW6e2t0w+jwmO/YvfHg8Yl7Yfdm78fdf/Ug9cHyw92PMI/yHvM9Lnki8aT6V9VfmycMJy5O2k4OPPV++nCKOfXit6TfVqZznlGelcxIz9TP6s5emLOfG3q+8fn0C9aL5fnc3/l/r3ip8vL8H5Z/DCwELEy/Yr/ivC54I/qm9q3+295Fj8Un7+LfLS/lvRd9X/eB/qH/o//HmeW0FdxK6SfVT12fXT4/4sRzOCwGm7FqBVBIwhERALyuBYASCAB1CPEPG9f8119+BvrK2fyNwVndL5jhvubRVsMQgCakeCFp04OsQ1LJEgAe5NodqT6WANbT+yf/iqQIPd21PXgaAcDJcjivtwJAQHLFgcNZ9uBwPlUgYhHf1z37f7V9g9e8ITewiP88wfWIYET6HPg21nzjV2fybQVcxfrg2/onng/F50lD/ccAAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAAABigAwAEAAAAAQAAABgAAAAAwf1XlwAAAaNJREFUSA3FlT1LA0EQQBN/gYUYRTksJZVgEbCR/D+7QMr8ABtttBBCsLGzsLG2sxaxED/ie4d77u0dyaE5HHjczn7MzO7M7nU6/yXz+bwLhzCCjTQO+rZhDH3opuNLdRYN4RHe4RIKJ7R34Ro+4AEGSw2mE1iUwT18gpI74WvkGlccu4XNdH0jnYU7cAUacidn37qR23cOxc4aGU0nYUAn7iSWEHkz46w0ocdQu1X6B/AMQZ5o7KfBqNOfwRH8JB7FajGhnmcpKvQe3MEbvILiDm5gPXaCHnZr4vvFGMoEKudKn8YvQIOOe+YzCPop7dwJ3zRfJ7GDuso4YJGRa0yZgg4tUaNXdGrbuZWKKxzYYEJc2xp9AUUjGt8KC2jvgYadF8+10vJyDnNLXwbdiWUZi0fUK01Eoc+AZhCLZVzK4Vq6sDUdz+0dEcbbTTIOJmAyTVhx/WmvrExbv2jtPhWLKodjCtefZiEeZeVZWWSndgwj6fVf3XON8Qwq15++uoqrfYVrow6dGBpCq79ME291jaB0/Q2CPncyht/99MNO/vr9AqW/CGi8sJqbAAAAAElFTkSuQmCC", size: 63) .previewLayout(.fixed(width: 63, height: 63)) .background(.black) } diff --git a/apps/ios/Shared/Views/Helpers/ShareSheet.swift b/apps/ios/Shared/Views/Helpers/ShareSheet.swift index 936c6cb3ab..b8de0e4ceb 100644 --- a/apps/ios/Shared/Views/Helpers/ShareSheet.swift +++ b/apps/ios/Shared/Views/Helpers/ShareSheet.swift @@ -8,15 +8,63 @@ import SwiftUI -func showShareSheet(items: [Any], completed: (() -> Void)? = nil) { +func getTopViewController() -> UIViewController? { let keyWindowScene = UIApplication.shared.connectedScenes.first { $0.activationState == .foregroundActive } as? UIWindowScene if let keyWindow = keyWindowScene?.windows.filter(\.isKeyWindow).first, - let presentedViewController = keyWindow.rootViewController?.presentedViewController ?? keyWindow.rootViewController { + let rootViewController = keyWindow.rootViewController { + // Find the top-most presented view controller + var topController = rootViewController + while let presentedViewController = topController.presentedViewController { + topController = presentedViewController + } + return topController + } + return nil +} + +func showShareSheet(items: [Any], completed: (() -> Void)? = nil) { + if let topController = getTopViewController() { let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil) if let completed = completed { - let handler: UIActivityViewController.CompletionWithItemsHandler = { _,_,_,_ in completed() } - activityViewController.completionWithItemsHandler = handler - } - presentedViewController.present(activityViewController, animated: true) + activityViewController.completionWithItemsHandler = { _, _, _, _ in + completed() + } + } + topController.present(activityViewController, animated: true) } } + +func showAlert( + title: String, + message: String? = nil, + buttonTitle: String, + buttonAction: @escaping () -> Void, + cancelButton: Bool +) -> Void { + if let topController = getTopViewController() { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: buttonTitle, style: .default) { _ in + buttonAction() + }) + if cancelButton { + alert.addAction(cancelAlertAction) + } + topController.present(alert, animated: true) + } +} + +func showAlert( + _ title: String, + message: String? = nil, + actions: () -> [UIAlertAction] = { [okAlertAction] } +) { + if let topController = getTopViewController() { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + for action in actions() { alert.addAction(action) } + topController.present(alert, animated: true) + } +} + +let okAlertAction = UIAlertAction(title: NSLocalizedString("Ok", comment: "alert button"), style: .default) + +let cancelAlertAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: "alert button"), style: .cancel) diff --git a/apps/ios/Shared/Views/Helpers/SheetRepresentable.swift b/apps/ios/Shared/Views/Helpers/SheetRepresentable.swift new file mode 100644 index 0000000000..841d5c7eda --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/SheetRepresentable.swift @@ -0,0 +1,188 @@ +// +// SwiftUISheet.swift +// SimpleX (iOS) +// +// Created by user on 23/09/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +private let sheetAnimationDuration: Double = 0.35 + +// Refrence: https://easings.net/ +private let easeOutCubic = UICubicTimingParameters( + controlPoint1: CGPoint(x: 0.215, y: 0.61), + controlPoint2: CGPoint(x: 0.355, y: 1) +) + +struct Sheet: ViewModifier { + @Binding var isPresented: Bool + @ViewBuilder let sheetContent: () -> SheetContent + + func body(content: Content) -> some View { + ZStack { + content + SheetRepresentable(isPresented: $isPresented, content: sheetContent()) + .allowsHitTesting(isPresented) + .ignoresSafeArea() + } + } +} + +struct SheetRepresentable: UIViewControllerRepresentable { + @Binding var isPresented: Bool + let content: Content + + func makeUIViewController(context: Context) -> Controller { + Controller(content: content, representer: self) + } + + func updateUIViewController(_ sheetController: Controller, context: Context) { + sheetController.animate(isPresented: isPresented) + } + + class Controller: UIViewController { + let hostingController: UIHostingController + private let animator = UIViewPropertyAnimator( + duration: sheetAnimationDuration, + timingParameters: easeOutCubic + ) + private let representer: SheetRepresentable + private var retainedFraction: CGFloat = 0 + private var sheetHeight: Double { hostingController.view.frame.height } + private var task: Task? + + init(content: C, representer: SheetRepresentable) { + self.representer = representer + self.hostingController = UIHostingController(rootView: content) + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError("init(coder:) missing") } + + deinit { + animator.stopAnimation(true) + animator.finishAnimation(at: .current) + } + + func animate(isPresented: Bool) { + let alreadyAnimating = animator.isRunning && isPresented != animator.isReversed + let sheetFullyDismissed = animator.fractionComplete == (animator.isReversed ? 1 : 0) + let sheetFullyPresented = animator.fractionComplete == (animator.isReversed ? 0 : 1) + + if !isPresented && sheetFullyDismissed || + isPresented && sheetFullyPresented || + alreadyAnimating { + return + } + + animator.pauseAnimation() + animator.isReversed = !isPresented + animator.continueAnimation( + withTimingParameters: isPresented + ? easeOutCubic + : UICubicTimingParameters(animationCurve: .easeIn), + durationFactor: 1 - animator.fractionComplete + ) + handleVisibility() + } + + func handleVisibility() { + if animator.isReversed { + task = Task { + do { + let sleepDuration = UInt64(sheetAnimationDuration * Double(NSEC_PER_SEC)) + try await Task.sleep(nanoseconds: sleepDuration) + view.isHidden = true + } catch { } + } + } else { + task?.cancel() + task = nil + view.isHidden = false + } + } + + override func viewDidLoad() { + view.isHidden = true + view.backgroundColor = .clear + view.addGestureRecognizer( + UITapGestureRecognizer(target: self, action: #selector(tap(gesture:))) + ) + addChild(hostingController) + hostingController.didMove(toParent: self) + if let sheet = hostingController.view { + sheet.isHidden = true + sheet.clipsToBounds = true + sheet.layer.cornerRadius = 10 + sheet.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner] + sheet.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(pan(gesture:)))) + sheet.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(sheet) + NSLayoutConstraint.activate([ + hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), + hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), + ]) + } + } + + override func viewDidAppear(_ animated: Bool) { + // Ensures animations are only setup once + // on some iOS version `viewDidAppear` can get called on each state change. + if hostingController.view.isHidden { + hostingController.view.transform = CGAffineTransform(translationX: 0, y: self.sheetHeight) + hostingController.view.isHidden = false + animator.pausesOnCompletion = true + animator.addAnimations { + self.hostingController.view.transform = .identity + self.view.backgroundColor = UIColor { + switch $0.userInterfaceStyle { + case .dark: .black.withAlphaComponent(0.290) + default: .black.withAlphaComponent(0.121) + } + } + } + animator.startAnimation() + animator.pauseAnimation() + } + } + + @objc + func pan(gesture: UIPanGestureRecognizer) { + switch gesture.state { + case .began: + animator.isReversed = false + animator.pauseAnimation() + retainedFraction = animator.fractionComplete + case .changed: + animator.fractionComplete = retainedFraction - gesture.translation(in: view).y / sheetHeight + case .ended, .cancelled: + let velocity = gesture.velocity(in: view).y + animator.isReversed = (velocity - (animator.fractionComplete - 0.5) * 100).sign == .plus + let defaultVelocity = sheetHeight / sheetAnimationDuration + let fractionRemaining = 1 - animator.fractionComplete + let durationFactor = min(max(fractionRemaining / (abs(velocity) / defaultVelocity), 0.5), 1) + animator.continueAnimation(withTimingParameters: nil, durationFactor: durationFactor * fractionRemaining) + handleVisibility() + DispatchQueue.main.asyncAfter(deadline: .now() + sheetAnimationDuration) { + self.representer.isPresented = !self.animator.isReversed + } + default: break + } + } + + @objc + func tap(gesture: UITapGestureRecognizer) { + switch gesture.state { + case .ended: + if gesture.location(in: view).y < view.frame.height - sheetHeight { + representer.isPresented = false + } + default: break + } + } + } +} diff --git a/apps/ios/Shared/Views/Helpers/StickyScrollView.swift b/apps/ios/Shared/Views/Helpers/StickyScrollView.swift new file mode 100644 index 0000000000..5799962778 --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/StickyScrollView.swift @@ -0,0 +1,61 @@ +// +// StickyScrollView.swift +// SimpleX (iOS) +// +// Created by user on 20/09/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +struct StickyScrollView: UIViewRepresentable { + @Binding var resetScroll: ResetScrollAction + @ViewBuilder let content: () -> Content + + func makeUIView(context: Context) -> UIScrollView { + let hc = context.coordinator.hostingController + hc.view.backgroundColor = .clear + let sv = UIScrollView() + sv.showsHorizontalScrollIndicator = false + sv.addSubview(hc.view) + sv.delegate = context.coordinator + DispatchQueue.main.async { + resetScroll = ResetScrollAction { sv.setContentOffset(.zero, animated: false) } + } + return sv + } + + func updateUIView(_ scrollView: UIScrollView, context: Context) { + let hc = context.coordinator.hostingController + hc.rootView = content() + hc.view.frame.size = hc.view.intrinsicContentSize + scrollView.contentSize = hc.view.intrinsicContentSize + } + + func makeCoordinator() -> Coordinator { + Coordinator(content: content()) + } + + class Coordinator: NSObject, UIScrollViewDelegate { + let hostingController: UIHostingController + + init(content: Content) { + self.hostingController = UIHostingController(rootView: content) + } + + func scrollViewWillEndDragging( + _ scrollView: UIScrollView, + withVelocity velocity: CGPoint, + targetContentOffset: UnsafeMutablePointer + ) { + if targetContentOffset.pointee.x < 32 { + targetContentOffset.pointee.x = 0 + } + } + } +} + +struct ResetScrollAction { + var action = { } + func callAsFunction() { action() } +} diff --git a/apps/ios/Shared/Views/Helpers/SwipeLabel.swift b/apps/ios/Shared/Views/Helpers/SwipeLabel.swift new file mode 100644 index 0000000000..564589be6f --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/SwipeLabel.swift @@ -0,0 +1,80 @@ +// +// SwipeLabel.swift +// SimpleX (iOS) +// +// Created by Levitating Pineapple on 06/08/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +struct SwipeLabel: View { + private let text: String + private let systemImage: String + private let inverted: Bool + + init(_ text: String, systemImage: String, inverted: Bool) { + self.text = text + self.systemImage = systemImage + self.inverted = inverted + } + + var body: some View { + if inverted { + Image( + uiImage: SwipeActionView( + systemName: systemImage, + text: text + ).snapshot(inverted: inverted) + ) + } else { + Label(text, systemImage: systemImage) + } + } + + private class SwipeActionView: UIView { + private let imageView = UIImageView() + private let label = UILabel() + private let fontSize: CGFloat + + init(systemName: String, text: String) { + fontSize = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .subheadline).pointSize + super.init(frame: CGRect(x: 0, y: 0, width: 64, height: 32 + fontSize)) + imageView.image = UIImage(systemName: systemName) + imageView.contentMode = .scaleAspectFit + label.text = text + label.textAlignment = .center + label.font = UIFont.systemFont(ofSize: fontSize, weight: .medium) + addSubview(imageView) + addSubview(label) + } + + override func layoutSubviews() { + imageView.frame = CGRect( + x: 20, + y: 0, + width: 24, + height: 24 + ) + label.frame = CGRect( + x: 0, + y: 32, + width: 64, + height: fontSize + ) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError("not implemented") } + + func snapshot(inverted: Bool) -> UIImage { + UIGraphicsImageRenderer(bounds: bounds).image { context in + if inverted { + context.cgContext.scaleBy(x: 1, y: -1) + context.cgContext.translateBy(x: 0, y: -bounds.height) + } + layer.render(in: context.cgContext) + }.withRenderingMode(.alwaysTemplate) + } + } +} diff --git a/apps/ios/Shared/Views/Helpers/ThemeModeEditor.swift b/apps/ios/Shared/Views/Helpers/ThemeModeEditor.swift new file mode 100644 index 0000000000..9d5ae2e289 --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/ThemeModeEditor.swift @@ -0,0 +1,453 @@ +// +// ThemeModeEditor.swift +// SimpleX (iOS) +// +// Created by Avently on 20.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI +import SimpleXChat + +struct UserWallpaperEditor: View { + @EnvironmentObject var theme: AppTheme + var initialTheme: ThemeModeOverride + @State var themeModeOverride: ThemeModeOverride + @State var applyToMode: DefaultThemeMode? + @State var showMore: Bool = false + @State var showFileImporter: Bool = false + @Binding var globalThemeUsed: Bool + var save: (DefaultThemeMode?, ThemeModeOverride?) async -> Void + + @State private var showImageImporter: Bool = false + + var body: some View { + List { + let wallpaperType = theme.wallpaper.type + + WallpaperPresetSelector( + selectedWallpaper: wallpaperType, + currentColors: { type in + // If applying for : + // - all themes: no overrides needed + // - specific user: only user overrides for currently selected theme are needed, because they will NOT be copied when other wallpaper is selected + let perUserOverride: ThemeModeOverrides? = wallpaperType.sameType(type) ? ChatModel.shared.currentUser?.uiThemes : nil + return ThemeManager.currentColors(type, nil, perUserOverride, themeOverridesDefault.get()) + }, + onChooseType: onChooseType + ) + .padding(.bottom, 10) + .listRowInsets(.init()) + .listRowBackground(Color.clear) + .modifier(WallpaperImporter(showImageImporter: $showImageImporter, onChooseImage: { image in + if let filename = saveWallpaperFile(image: image) { + _ = onTypeCopyFromSameTheme(WallpaperType.image(filename, 1, WallpaperScaleType.fill)) + } + })) + + WallpaperSetupView( + wallpaperType: themeModeOverride.type, + base: theme.base, + initialWallpaper: theme.wallpaper, + editColor: { name in editColor(name, theme) }, + onTypeChange: onTypeChange + ) + + Section { + if !globalThemeUsed { + ResetToGlobalThemeButton(true, theme.colors.primary) { + themeModeOverride = ThemeManager.defaultActiveTheme(ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + globalThemeUsed = true + Task { + await save(applyToMode, nil) + await MainActor.run { + // Change accent color globally + ThemeManager.applyTheme(currentThemeDefault.get()) + } + } + } + } + + SetDefaultThemeButton(theme.colors.primary) { + globalThemeUsed = false + let lightBase = DefaultTheme.LIGHT + let darkBase = if theme.base != DefaultTheme.LIGHT { theme.base } else if systemDarkThemeDefault.get() == DefaultTheme.DARK.themeName { DefaultTheme.DARK } else if systemDarkThemeDefault.get() == DefaultTheme.BLACK.themeName { DefaultTheme.BLACK } else { DefaultTheme.SIMPLEX } + let mode = themeModeOverride.mode + Task { + // Saving for both modes in one place by changing mode once per save + if applyToMode == nil { + let oppositeMode = mode == DefaultThemeMode.light ? DefaultThemeMode.dark : DefaultThemeMode.light + await save(oppositeMode, ThemeModeOverride.withFilledAppDefaults(oppositeMode, oppositeMode == DefaultThemeMode.light ? lightBase : darkBase)) + } + await MainActor.run { + themeModeOverride = ThemeModeOverride.withFilledAppDefaults(mode, mode == DefaultThemeMode.light ? lightBase : darkBase) + } + await save(themeModeOverride.mode, themeModeOverride) + await MainActor.run { + // Change accent color globally + ThemeManager.applyTheme(currentThemeDefault.get()) + } + } + }.onChange(of: initialTheme.mode) { mode in + themeModeOverride = initialTheme + if applyToMode != nil { + applyToMode = mode + } + } + .onChange(of: theme) { _ in + // Applies updated global theme if current one tracks global theme + if globalThemeUsed { + themeModeOverride = ThemeManager.defaultActiveTheme(ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + globalThemeUsed = true + } + } + } + + if showMore { + let values = [ + (nil, "All modes"), + (DefaultThemeMode.light, "Light mode"), + (DefaultThemeMode.dark, "Dark mode") + ] + Picker("Apply to", selection: $applyToMode) { + ForEach(values, id: \.0) { (_, text) in + Text(text) + } + } + .frame(height: 36) + .onChange(of: applyToMode) { mode in + if let mode, mode != theme.base.mode { + let lightBase = DefaultTheme.LIGHT + let darkBase = if theme.base != DefaultTheme.LIGHT { theme.base } else if systemDarkThemeDefault.get() == DefaultTheme.DARK.themeName { DefaultTheme.DARK } else if systemDarkThemeDefault.get() == DefaultTheme.BLACK.themeName { DefaultTheme.BLACK } else { DefaultTheme.SIMPLEX } + ThemeManager.applyTheme(mode == DefaultThemeMode.light ? lightBase.themeName : darkBase.themeName) + } + } + + CustomizeThemeColorsSection(editColor: { name in editColor(name, theme) }) + + ImportExportThemeSection(showFileImporter: $showFileImporter, perChat: nil, perUser: ChatModel.shared.currentUser?.uiThemes) + } else { + AdvancedSettingsButton(theme.colors.primary) { showMore = true } + } + } + .modifier( + ThemeImporter(isPresented: $showFileImporter) { imported in + let importedFromString = imported.wallpaper?.importFromString() + let importedType = importedFromString?.toAppWallpaper().type + let currentTheme = ThemeManager.currentColors(nil, nil, nil, themeOverridesDefault.get()) + let type: WallpaperType? = if importedType?.sameType(currentTheme.wallpaper.type) == true { nil } else { importedType } + let colors = ThemeManager.currentThemeOverridesForExport(type, nil, nil).colors + let res = ThemeModeOverride(mode: imported.base.mode, colors: imported.colors, wallpaper: importedFromString).removeSameColors(imported.base, colorsToCompare: colors) + Task { + await MainActor.run { + themeModeOverride = res + } + await save(applyToMode, res) + } + } + ) + } + + private func onTypeCopyFromSameTheme(_ type: WallpaperType?) -> Bool { + _ = ThemeManager.copyFromSameThemeOverrides(type, nil, $themeModeOverride) + Task { + await save(applyToMode, themeModeOverride) + } + globalThemeUsed = false + return true + } + + private func preApplyGlobalIfNeeded(_ type: WallpaperType?) { + if globalThemeUsed { + _ = onTypeCopyFromSameTheme(type) + } + } + + private func onTypeChange(_ type: WallpaperType?) { + if globalThemeUsed { + preApplyGlobalIfNeeded(type) + // Saves copied static image instead of original from global theme + ThemeManager.applyWallpaper(themeModeOverride.type, $themeModeOverride) + } else { + ThemeManager.applyWallpaper(type, $themeModeOverride) + } + Task { + await save(applyToMode, themeModeOverride) + } + } + + private func currentColors(_ type: WallpaperType?) -> ThemeManager.ActiveTheme { + // If applying for : + // - all themes: no overrides needed + // - specific user: only user overrides for currently selected theme are needed, because they will NOT be copied when other wallpaper is selected + let perUserOverride: ThemeModeOverrides? = theme.wallpaper.type.sameType(type) ? ChatModel.shared.currentUser?.uiThemes : nil + return ThemeManager.currentColors(type, nil, perUserOverride, themeOverridesDefault.get()) + } + + private func onChooseType(_ type: WallpaperType?) { + if let type, case WallpaperType.image = type { + if theme.wallpaper.type.isImage || currentColors(type).wallpaper.type.image == nil { + showImageImporter = true + } else { + _ = onTypeCopyFromSameTheme(currentColors(type).wallpaper.type) + } + } else if themeModeOverride.type != type || theme.wallpaper.type != type { + _ = onTypeCopyFromSameTheme(type) + } else { + onTypeChange(type) + } + } + + private func editColor(_ name: ThemeColor, _ currentTheme: AppTheme) -> Binding { + editColorBinding( + name: name, + wallpaperType: theme.wallpaper.type, + wallpaperImage: theme.wallpaper.type.image, + theme: currentTheme, + onColorChange: { color in + preApplyGlobalIfNeeded(themeModeOverride.type) + ThemeManager.applyThemeColor(name: name, color: color, pref: $themeModeOverride) + Task { await save(applyToMode, themeModeOverride) } + }) + } +} + +struct ChatWallpaperEditor: View { + @EnvironmentObject var theme: AppTheme + @State private var currentTheme: ThemeManager.ActiveTheme + var initialTheme: ThemeModeOverride + @State var themeModeOverride: ThemeModeOverride + @State var applyToMode: DefaultThemeMode? + @State var showMore: Bool = false + @State var showFileImporter: Bool = false + @Binding var globalThemeUsed: Bool + var save: (DefaultThemeMode?, ThemeModeOverride?) async -> Void + + @State private var showImageImporter: Bool = false + + init(initialTheme: ThemeModeOverride, themeModeOverride: ThemeModeOverride, applyToMode: DefaultThemeMode? = nil, globalThemeUsed: Binding, save: @escaping (DefaultThemeMode?, ThemeModeOverride?) async -> Void) { + let cur = ThemeManager.currentColors(nil, globalThemeUsed.wrappedValue ? nil : themeModeOverride, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + self.currentTheme = cur + self.initialTheme = initialTheme + self.themeModeOverride = themeModeOverride + self.applyToMode = applyToMode + self._globalThemeUsed = globalThemeUsed + self.save = save + } + + var body: some View { + List { + WallpaperPresetSelector( + selectedWallpaper: currentTheme.wallpaper.type, + activeBackgroundColor: currentTheme.wallpaper.background, + activeTintColor: currentTheme.wallpaper.tint, + currentColors: currentColors, + onChooseType: onChooseType + ) + .padding(.bottom, 10) + .listRowInsets(.init()) + .listRowBackground(Color.clear) + .modifier(WallpaperImporter(showImageImporter: $showImageImporter, onChooseImage: { image in + if let filename = saveWallpaperFile(image: image) { + _ = onTypeCopyFromSameTheme(WallpaperType.image(filename, 1, WallpaperScaleType.fill)) + } + })) + + WallpaperSetupView( + wallpaperType: themeModeOverride.type, + base: currentTheme.base, + initialWallpaper: currentTheme.wallpaper, + editColor: editColor, + onTypeChange: onTypeChange + ) + + Section { + if !globalThemeUsed { + ResetToGlobalThemeButton(ChatModel.shared.currentUser?.uiThemes?.preferredMode(isInDarkTheme()) == nil, theme.colors.primary) { + themeModeOverride = ThemeManager.defaultActiveTheme(ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + globalThemeUsed = true + Task { + await save(applyToMode, nil) + } + } + } + + SetDefaultThemeButton(theme.colors.primary) { + globalThemeUsed = false + let lightBase = DefaultTheme.LIGHT + let darkBase = if currentTheme.base != DefaultTheme.LIGHT { currentTheme.base } else if systemDarkThemeDefault.get() == DefaultTheme.DARK.themeName { DefaultTheme.DARK } else if systemDarkThemeDefault.get() == DefaultTheme.BLACK.themeName { DefaultTheme.BLACK } else { DefaultTheme.SIMPLEX } + let mode = themeModeOverride.mode + Task { + // Saving for both modes in one place by changing mode once per save + if applyToMode == nil { + let oppositeMode = mode == DefaultThemeMode.light ? DefaultThemeMode.dark : DefaultThemeMode.light + await save(oppositeMode, ThemeModeOverride.withFilledAppDefaults(oppositeMode, oppositeMode == DefaultThemeMode.light ? lightBase : darkBase)) + } + await MainActor.run { + themeModeOverride = ThemeModeOverride.withFilledAppDefaults(mode, mode == DefaultThemeMode.light ? lightBase : darkBase) + } + await save(themeModeOverride.mode, themeModeOverride) + } + } + .onChange(of: initialTheme) { initial in + if initial.mode != themeModeOverride.mode { + themeModeOverride = initial + currentTheme = ThemeManager.currentColors(nil, globalThemeUsed ? nil : themeModeOverride, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + if applyToMode != nil { + applyToMode = initial.mode + } + } + } + .onChange(of: currentTheme) { _ in + // Applies updated global theme if current one tracks global theme + if globalThemeUsed { + themeModeOverride = ThemeManager.defaultActiveTheme(ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + globalThemeUsed = true + } + } + .onChange(of: themeModeOverride) { override in + currentTheme = ThemeManager.currentColors(nil, globalThemeUsed ? nil : override, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + } + } + + if showMore { + let values = [ + (nil, "All modes"), + (DefaultThemeMode.light, "Light mode"), + (DefaultThemeMode.dark, "Dark mode") + ] + Picker("Apply to", selection: $applyToMode) { + ForEach(values, id: \.0) { (_, text) in + Text(text) + } + } + .frame(height: 36) + .onChange(of: applyToMode) { mode in + if let mode, mode != currentTheme.base.mode { + let lightBase = DefaultTheme.LIGHT + let darkBase = if currentTheme.base != DefaultTheme.LIGHT { currentTheme.base } else if systemDarkThemeDefault.get() == DefaultTheme.DARK.themeName { DefaultTheme.DARK } else if systemDarkThemeDefault.get() == DefaultTheme.BLACK.themeName { DefaultTheme.BLACK } else { DefaultTheme.SIMPLEX } + ThemeManager.applyTheme(mode == DefaultThemeMode.light ? lightBase.themeName : darkBase.themeName) + } + } + + CustomizeThemeColorsSection(editColor: editColor) + + ImportExportThemeSection(showFileImporter: $showFileImporter, perChat: themeModeOverride, perUser: ChatModel.shared.currentUser?.uiThemes) + } else { + AdvancedSettingsButton(theme.colors.primary) { showMore = true } + } + } + .modifier( + ThemeImporter(isPresented: $showFileImporter) { imported in + let importedFromString = imported.wallpaper?.importFromString() + let importedType = importedFromString?.toAppWallpaper().type + let currentTheme = ThemeManager.currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + let type: WallpaperType? = if importedType?.sameType(currentTheme.wallpaper.type) == true { nil } else { importedType } + let colors = ThemeManager.currentThemeOverridesForExport(type, nil, ChatModel.shared.currentUser?.uiThemes).colors + let res = ThemeModeOverride(mode: imported.base.mode, colors: imported.colors, wallpaper: importedFromString).removeSameColors(imported.base, colorsToCompare: colors) + Task { + await MainActor.run { + themeModeOverride = res + } + await save(applyToMode, res) + } + } + ) + } + + private func onTypeCopyFromSameTheme(_ type: WallpaperType?) -> Bool { + let success = ThemeManager.copyFromSameThemeOverrides(type, ChatModel.shared.currentUser?.uiThemes?.preferredMode(!currentTheme.colors.isLight), $themeModeOverride) + if success { + Task { + await save(applyToMode, themeModeOverride) + } + globalThemeUsed = false + } + return success + } + + private func preApplyGlobalIfNeeded(_ type: WallpaperType?) { + if globalThemeUsed { + _ = onTypeCopyFromSameTheme(type) + } + } + + private func onTypeChange(_ type: WallpaperType?) { + if globalThemeUsed { + preApplyGlobalIfNeeded(type) + // Saves copied static image instead of original from global theme + ThemeManager.applyWallpaper(themeModeOverride.type, $themeModeOverride) + } else { + ThemeManager.applyWallpaper(type, $themeModeOverride) + } + Task { + await save(applyToMode, themeModeOverride) + } + } + + private func currentColors(_ type: WallpaperType?) -> ThemeManager.ActiveTheme { + // If applying for : + // - all themes: no overrides needed + // - specific user: only user overrides for currently selected theme are needed, because they will NOT be copied when other wallpaper is selected + let perChatOverride: ThemeModeOverride? = type?.sameType(themeModeOverride.type) == true ? themeModeOverride : nil + return ThemeManager.currentColors(type, perChatOverride, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + } + + private func onChooseType(_ type: WallpaperType?) { + if let type, case WallpaperType.image = type { + if (themeModeOverride.type?.isImage == true && !globalThemeUsed) || currentColors(type).wallpaper.type.image == nil { + showImageImporter = true + } else if !onTypeCopyFromSameTheme(currentColors(type).wallpaper.type) { + showImageImporter = true + } + } else if globalThemeUsed || themeModeOverride.type != type || themeModeOverride.type != type { + _ = onTypeCopyFromSameTheme(type) + } else { + onTypeChange(type) + } + } + + private func editColor(_ name: ThemeColor) -> Binding { + editColorBinding( + name: name, + wallpaperType: themeModeOverride.type, + wallpaperImage: themeModeOverride.type?.image, + theme: currentTheme.toAppTheme(), + onColorChange: { color in + preApplyGlobalIfNeeded(themeModeOverride.type) + ThemeManager.applyThemeColor(name: name, color: color, pref: $themeModeOverride) + currentTheme = ThemeManager.currentColors(nil, globalThemeUsed ? nil : themeModeOverride, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + Task { await save(applyToMode, themeModeOverride) } + }) + } +} + +private func ResetToGlobalThemeButton(_ app: Bool, _ primaryColor: Color, _ onClick: @escaping () -> Void) -> some View { + Button { + onClick() + } label: { + Text(app ? "Reset to app theme" : "Reset to user theme") + .foregroundColor(primaryColor) + } +} + +private func SetDefaultThemeButton(_ primaryColor: Color, _ onClick: @escaping () -> Void) -> some View { + Button { + onClick() + } label: { + Text("Set default theme") + .foregroundColor(primaryColor) + } +} + +private func AdvancedSettingsButton(_ primaryColor: Color, _ onClick: @escaping () -> Void) -> some View { + Button { + onClick() + } label: { + HStack { + Image(systemName: "chevron.down") + Text("Advanced settings") + }.foregroundColor(primaryColor) + } +} diff --git a/apps/ios/Shared/Views/Helpers/UserDefault.swift b/apps/ios/Shared/Views/Helpers/UserDefault.swift new file mode 100644 index 0000000000..5f18465d20 --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/UserDefault.swift @@ -0,0 +1,62 @@ +// +// UserDefault.swift +// SimpleX (iOS) +// +// Created by user on 14/10/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import Combine + +@propertyWrapper +public struct UserDefault: DynamicProperty { + @StateObject private var observer = UserDefaultObserver() + let initialValue: Value + let key: String + let store: UserDefaults + + public init( + wrappedValue: Value, + _ key: String, + store: UserDefaults = .standard + ) { + self.initialValue = wrappedValue + self.key = key + self.store = store + } + + public var wrappedValue: Value { + get { + // Observer can only be accessed after the property wrapper is installed in view (runtime exception) + observer.subscribe(to: key) + return store.object(forKey: key) as? Value ?? initialValue + } + nonmutating set { + store.set(newValue, forKey: key) + } + } +} + +private class UserDefaultObserver: ObservableObject { + private var subscribed = false + + func subscribe(to key: String) { + if !subscribed { + NotificationCenter.default.addObserver( + self, + selector: #selector(userDefaultsDidChange), + name: UserDefaults.didChangeNotification, + object: nil + ) + subscribed = true + } + } + + @objc + private func userDefaultsDidChange(_ notification: Notification) { + Task { @MainActor in objectWillChange.send() } + } + + deinit { NotificationCenter.default.removeObserver(self) } +} diff --git a/apps/ios/Shared/Views/Helpers/VideoUtils.swift b/apps/ios/Shared/Views/Helpers/VideoUtils.swift deleted file mode 100644 index e13893de6e..0000000000 --- a/apps/ios/Shared/Views/Helpers/VideoUtils.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// VideoUtils.swift -// SimpleX (iOS) -// -// Created by Avently on 25.12.2023. -// Copyright © 2023 SimpleX Chat. All rights reserved. -// - -import AVFoundation -import Foundation -import SimpleXChat - -func makeVideoQualityLower(_ input: URL, outputUrl: URL) async -> Bool { - let asset: AVURLAsset = AVURLAsset(url: input, options: nil) - if let s = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset640x480) { - s.outputURL = outputUrl - s.outputFileType = .mp4 - s.metadataItemFilter = AVMetadataItemFilter.forSharing() - await s.export() - if let err = s.error { - logger.error("Failed to export video with error: \(err)") - } - return s.status == .completed - } - return false -} diff --git a/apps/ios/Shared/Views/Helpers/ViewModifiers.swift b/apps/ios/Shared/Views/Helpers/ViewModifiers.swift new file mode 100644 index 0000000000..85ef85c611 --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/ViewModifiers.swift @@ -0,0 +1,54 @@ +// +// ViewModifiers.swift +// SimpleX (iOS) +// +// Created by Avently on 12.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +extension View { + @inline(__always) + @ViewBuilder func `if`(_ condition: Bool, transform: (Self) -> Content) -> some View { + if condition { + transform(self) + } else { + self + } + } +} + +extension Notification.Name { + static let chatViewWillBeginScrolling = Notification.Name("chatWillBeginScrolling") +} + +struct PrivacyBlur: ViewModifier { + var enabled: Bool = true + @Binding var blurred: Bool + @AppStorage(DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) private var blurRadius: Int = 0 + + func body(content: Content) -> some View { + if blurRadius > 0 { + // parallel ifs are necessary here because otherwise some views flicker, + // e.g. when playing video + content + .blur(radius: blurred && enabled ? CGFloat(blurRadius) * 0.5 : 0) + .overlay { + if (blurred && enabled) { + Color.clear.contentShape(Rectangle()) + .simultaneousGesture(TapGesture().onEnded { + blurred = false + }) + } + } + .onReceive(NotificationCenter.default.publisher(for: .chatViewWillBeginScrolling)) { _ in + if !blurred { + blurred = true + } + } + } else { + content + } + } +} diff --git a/apps/ios/Shared/Views/LocalAuth/LocalAuthView.swift b/apps/ios/Shared/Views/LocalAuth/LocalAuthView.swift index 9691a9efd3..16ab26eff7 100644 --- a/apps/ios/Shared/Views/LocalAuth/LocalAuthView.swift +++ b/apps/ios/Shared/Views/LocalAuth/LocalAuthView.swift @@ -64,8 +64,9 @@ struct LocalAuthView: View { deleteAppDatabaseAndFiles() // Clear sensitive data on screen just in case app fails to hide its views while new database is created m.chatId = nil - m.reversedChatItems = [] - m.chats = [] + ItemsModel.shared.reversedChatItems = [] + ItemsModel.shared.chatState.clear() + m.updateChats([]) m.users = [] _ = kcAppPassword.set(password) _ = kcSelfDestructPassword.remove() diff --git a/apps/ios/Shared/Views/LocalAuth/PasscodeEntry.swift b/apps/ios/Shared/Views/LocalAuth/PasscodeEntry.swift index 46ce66678a..4a6f8e7549 100644 --- a/apps/ios/Shared/Views/LocalAuth/PasscodeEntry.swift +++ b/apps/ios/Shared/Views/LocalAuth/PasscodeEntry.swift @@ -10,6 +10,7 @@ import SwiftUI struct PasscodeEntry: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme var width: CGFloat var height: CGFloat @Binding var password: String @@ -27,7 +28,7 @@ struct PasscodeEntry: View { } } - @ViewBuilder private func passwordView() -> some View { + private func passwordView() -> some View { Text( password == "" ? " " @@ -140,11 +141,11 @@ struct PasscodeEntry: View { ZStack { Circle() .frame(width: h, height: h) - .foregroundColor(Color(uiColor: .systemBackground)) + .foregroundColor(AppTheme.shared.colors.background) label() } } - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .frame(width: size, height: h) } } diff --git a/apps/ios/Shared/Views/LocalAuth/PasscodeView.swift b/apps/ios/Shared/Views/LocalAuth/PasscodeView.swift index 9e0d7f38b5..ca30fa5ce8 100644 --- a/apps/ios/Shared/Views/LocalAuth/PasscodeView.swift +++ b/apps/ios/Shared/Views/LocalAuth/PasscodeView.swift @@ -29,7 +29,7 @@ struct PasscodeView: View { } .padding(.horizontal, 40) .frame(maxWidth: .infinity, maxHeight: .infinity) - .background(Color(uiColor: .systemBackground)) + .background(AppTheme.shared.colors.background) } private func verticalPasscodeView(_ g: GeometryProxy) -> some View { diff --git a/apps/ios/Shared/Views/Migration/MigrateFromDevice.swift b/apps/ios/Shared/Views/Migration/MigrateFromDevice.swift index b3b7269d22..0af8fa7ad8 100644 --- a/apps/ios/Shared/Views/Migration/MigrateFromDevice.swift +++ b/apps/ios/Shared/Views/Migration/MigrateFromDevice.swift @@ -24,6 +24,7 @@ private enum MigrationFromState: Equatable { } private enum MigrateFromDeviceViewAlert: Identifiable { + case finishMigration(_ fileId: Int64, _ ctrl: chat_ctrl) case deleteChat(_ title: LocalizedStringKey = "Delete chat profile?", _ text: LocalizedStringKey = "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.") case startChat(_ title: LocalizedStringKey = "Start chat?", _ text: LocalizedStringKey = "Warning: starting chat on multiple devices is not supported and will cause message delivery failures") @@ -32,11 +33,13 @@ private enum MigrateFromDeviceViewAlert: Identifiable { case keychainError(_ title: LocalizedStringKey = "Keychain error") case databaseError(_ title: LocalizedStringKey = "Database error", message: String) case unknownError(_ title: LocalizedStringKey = "Unknown error", message: String) + case archiveExportedWithErrors(archivePath: URL, archiveErrors: [ArchiveError]) case error(title: LocalizedStringKey, error: String = "") var id: String { switch self { + case .finishMigration: return "finishMigration" case let .deleteChat(title, text): return "\(title) \(text)" case let .startChat(title, text): return "\(title) \(text)" @@ -45,6 +48,7 @@ private enum MigrateFromDeviceViewAlert: Identifiable { case .keychainError: return "keychainError" case let .databaseError(title, message): return "\(title) \(message)" case let .unknownError(title, message): return "\(title) \(message)" + case let .archiveExportedWithErrors(path, _): return "archiveExportedWithErrors \(path)" case let .error(title, _): return "error \(title)" } @@ -53,8 +57,7 @@ private enum MigrateFromDeviceViewAlert: Identifiable { struct MigrateFromDevice: View { @EnvironmentObject var m: ChatModel - @Environment(\.dismiss) var dismiss: DismissAction - @Binding var showSettings: Bool + @EnvironmentObject var theme: AppTheme @Binding var showProgressOnSettings: Bool @State private var migrationState: MigrationFromState = .chatStopInProgress @State private var useKeychain = storeDBPassphraseGroupDefault.get() @@ -103,9 +106,6 @@ struct MigrateFromDevice: View { finishedView(chatDeletion) } } - .modifier(BackButton(label: "Back", disabled: $backDisabled) { - dismiss() - }) .onChange(of: migrationState) { state in backDisabled = switch migrationState { case .chatStopInProgress, .archiving, .linkShown, .finished: true @@ -135,6 +135,15 @@ struct MigrateFromDevice: View { } .alert(item: $alert) { alert in switch alert { + case let .finishMigration(fileId, ctrl): + return Alert( + title: Text("Remove archive?"), + message: Text("The uploaded database archive will be permanently removed from the servers."), + primaryButton: .destructive(Text("Continue")) { + finishMigration(fileId, ctrl) + }, + secondaryButton: .cancel() + ) case let .startChat(title, text): return Alert( title: Text(title), @@ -150,7 +159,7 @@ struct MigrateFromDevice: View { return Alert( title: Text(title), message: Text(text), - primaryButton: .destructive(Text("Delete")) { + primaryButton: .default(Text("Delete")) { deleteChatAndDismiss() }, secondaryButton: .cancel() @@ -165,6 +174,14 @@ struct MigrateFromDevice: View { return Alert(title: Text(title), message: Text(message)) case let .unknownError(title, message): return Alert(title: Text(title), message: Text(message)) + case let .archiveExportedWithErrors(archivePath, errs): + return Alert( + title: Text("Chat database exported"), + message: Text("You may migrate the exported database.") + textNewLine + Text("Some file(s) were not exported:") + Text(archiveErrorsText(errs)), + dismissButton: .default(Text("Continue")) { + Task { await uploadArchive(path: archivePath) } + } + ) case let .error(title, error): return Alert(title: Text(title), message: Text(error)) } @@ -177,6 +194,7 @@ struct MigrateFromDevice: View { List { Section {} header: { Text("Stopping chat") + .foregroundColor(theme.colors.secondary) } } progressView() @@ -188,21 +206,24 @@ struct MigrateFromDevice: View { Section { Text(reason) Button(action: stopChat) { - settingsRow("stop.fill") { + settingsRow("stop.fill", color: theme.colors.secondary) { Text("Stop chat").foregroundColor(.red) } } } header: { Text("Error stopping chat") + .foregroundColor(theme.colors.secondary) } footer: { Text("In order to continue, chat should be stopped.") + .foregroundColor(theme.colors.secondary) .font(.callout) } } } private func passphraseNotSetView() -> some View { - DatabaseEncryptionView(useKeychain: $useKeychain, migration: true) + DatabaseEncryptionView(useKeychain: $useKeychain, migration: true, stopChatRunBlockStartChat: { _, _ in + }) .onChange(of: initialRandomDBPassphrase) { initial in if !initial { migrationState = .uploadConfirmation @@ -214,14 +235,16 @@ struct MigrateFromDevice: View { List { Section { Button(action: { migrationState = .archiving }) { - settingsRow("tray.and.arrow.up") { - Text("Archive and upload").foregroundColor(.accentColor) + settingsRow("tray.and.arrow.up", color: theme.colors.secondary) { + Text("Archive and upload").foregroundColor(theme.colors.primary) } } } header: { Text("Confirm upload") + .foregroundColor(theme.colors.secondary) } footer: { Text("All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays.") + .foregroundColor(theme.colors.secondary) .font(.callout) } } @@ -232,6 +255,7 @@ struct MigrateFromDevice: View { List { Section {} header: { Text("Archiving database") + .foregroundColor(theme.colors.secondary) } } progressView() @@ -246,10 +270,11 @@ struct MigrateFromDevice: View { List { Section {} header: { Text("Uploading archive") + .foregroundColor(theme.colors.secondary) } } let ratio = Float(uploadedBytes) / Float(totalBytes) - MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: uploadedBytes, countStyle: .binary)) uploaded") + MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: uploadedBytes, countStyle: .binary)) uploaded", theme.colors.primary) } .onAppear { startUploading(totalBytes, archivePath) @@ -262,14 +287,16 @@ struct MigrateFromDevice: View { Button(action: { migrationState = .uploadProgress(uploadedBytes: 0, totalBytes: totalBytes, fileId: 0, archivePath: archivePath, ctrl: nil) }) { - settingsRow("tray.and.arrow.up") { - Text("Repeat upload").foregroundColor(.accentColor) + settingsRow("tray.and.arrow.up", color: theme.colors.secondary) { + Text("Repeat upload").foregroundColor(theme.colors.primary) } } } header: { Text("Upload failed") + .foregroundColor(theme.colors.secondary) } footer: { Text("You can give another try.") + .foregroundColor(theme.colors.secondary) .font(.callout) } } @@ -283,6 +310,7 @@ struct MigrateFromDevice: View { List { Section {} header: { Text("Creating archive link") + .foregroundColor(theme.colors.secondary) } } progressView() @@ -293,13 +321,13 @@ struct MigrateFromDevice: View { List { Section { Button(action: { cancelMigration(fileId, ctrl) }) { - settingsRow("multiply") { + settingsRow("multiply", color: theme.colors.secondary) { Text("Cancel migration").foregroundColor(.red) } } - Button(action: { finishMigration(fileId, ctrl) }) { - settingsRow("checkmark") { - Text("Finalize migration").foregroundColor(.accentColor) + Button(action: { alert = .finishMigration(fileId, ctrl) }) { + settingsRow("checkmark", color: theme.colors.secondary) { + Text("Finalize migration").foregroundColor(theme.colors.primary) } } } footer: { @@ -307,9 +335,10 @@ struct MigrateFromDevice: View { Text("**Warning**: the archive will be removed.") Text("Choose _Migrate from another device_ on the new device and scan QR code.") } + .foregroundColor(theme.colors.secondary) .font(.callout) } - Section("Show QR code") { + Section(header: Text("Show QR code").foregroundColor(theme.colors.secondary)) { SimpleXLinkQRCode(uri: link) .padding() .background( @@ -322,7 +351,7 @@ struct MigrateFromDevice: View { .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) } - Section("Or securely share this file link") { + Section(header: Text("Or securely share this file link").foregroundColor(theme.colors.secondary)) { shareLinkView(link) } .listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 10)) @@ -333,23 +362,25 @@ struct MigrateFromDevice: View { ZStack { List { Section { - Button(action: { alert = .deleteChat() }) { - settingsRow("trash.fill") { - Text("Delete database from this device").foregroundColor(.accentColor) + Button(action: { alert = .startChat() }) { + settingsRow("play.fill", color: theme.colors.secondary) { + Text("Start chat").foregroundColor(.red) } } - Button(action: { alert = .startChat() }) { - settingsRow("play.fill") { - Text("Start chat").foregroundColor(.red) + Button(action: { alert = .deleteChat() }) { + settingsRow("trash.fill", color: theme.colors.secondary) { + Text("Delete database from this device").foregroundColor(theme.colors.primary) } } } header: { Text("Migration complete") + .foregroundColor(theme.colors.secondary) } footer: { VStack(alignment: .leading, spacing: 16) { Text("You **must not** use the same database on two devices.") Text("**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection.") } + .foregroundColor(theme.colors.secondary) .font(.callout) } } @@ -379,7 +410,7 @@ struct MigrateFromDevice: View { .truncationMode(.middle) } - static func largeProgressView(_ value: Float, _ title: String, _ description: LocalizedStringKey) -> some View { + static func largeProgressView(_ value: Float, _ title: String, _ description: LocalizedStringKey, _ primaryColor: Color) -> some View { ZStack { VStack { Text(description) @@ -389,7 +420,7 @@ struct MigrateFromDevice: View { Text(title) .font(.system(size: 54)) .bold() - .foregroundColor(.accentColor) + .foregroundColor(primaryColor) Text(description) .font(.title3) @@ -398,7 +429,7 @@ struct MigrateFromDevice: View { Circle() .trim(from: 0, to: CGFloat(value)) .stroke( - Color.accentColor, + primaryColor, style: StrokeStyle(lineWidth: 27) ) .rotationEffect(.degrees(180)) @@ -435,15 +466,12 @@ struct MigrateFromDevice: View { Task { do { try? FileManager.default.createDirectory(at: getMigrationTempFilesDirectory(), withIntermediateDirectories: true) - let archivePath = try await exportChatArchive(getMigrationTempFilesDirectory()) - if let attrs = try? FileManager.default.attributesOfItem(atPath: archivePath.path), - let totalBytes = attrs[.size] as? Int64 { - await MainActor.run { - migrationState = .uploadProgress(uploadedBytes: 0, totalBytes: totalBytes, fileId: 0, archivePath: archivePath, ctrl: nil) - } + let (archivePath, errs) = try await exportChatArchive(getMigrationTempFilesDirectory()) + if errs.isEmpty { + await uploadArchive(path: archivePath) } else { await MainActor.run { - alert = .error(title: "Exported file doesn't exist") + alert = .archiveExportedWithErrors(archivePath: archivePath, archiveErrors: errs) migrationState = .uploadConfirmation } } @@ -455,6 +483,20 @@ struct MigrateFromDevice: View { } } } + + private func uploadArchive(path archivePath: URL) async { + if let attrs = try? FileManager.default.attributesOfItem(atPath: archivePath.path), + let totalBytes = attrs[.size] as? Int64 { + await MainActor.run { + migrationState = .uploadProgress(uploadedBytes: 0, totalBytes: totalBytes, fileId: 0, archivePath: archivePath, ctrl: nil) + } + } else { + await MainActor.run { + alert = .error(title: "Exported file doesn't exist") + migrationState = .uploadConfirmation + } + } + } private func initTemporaryDatabase() -> (chat_ctrl, User)? { let (status, ctrl) = chatInitTemporaryDatabase(url: tempDatabaseUrl) @@ -478,19 +520,25 @@ struct MigrateFromDevice: View { chatReceiver = MigrationChatReceiver(ctrl: ctrl, databaseUrl: tempDatabaseUrl) { msg in await MainActor.run { switch msg { - case let .sndFileProgressXFTP(_, _, fileTransferMeta, sentSize, totalSize): + case let .result(.sndFileProgressXFTP(_, _, fileTransferMeta, sentSize, totalSize)): if case let .uploadProgress(uploaded, total, _, _, _) = migrationState, uploaded != total { migrationState = .uploadProgress(uploadedBytes: sentSize, totalBytes: totalSize, fileId: fileTransferMeta.fileId, archivePath: archivePath, ctrl: ctrl) } - case .sndFileRedirectStartXFTP: + case .result(.sndFileRedirectStartXFTP): DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { migrationState = .linkCreation } - case let .sndStandaloneFileComplete(_, fileTransferMeta, rcvURIs): + case let .result(.sndStandaloneFileComplete(_, fileTransferMeta, rcvURIs)): let cfg = getNetCfg() + let proxy: NetworkProxy? = if cfg.socksProxy == nil { + nil + } else { + networkProxyDefault.get() + } let data = MigrationFileLinkData.init( networkConfig: MigrationFileLinkData.NetworkConfig( socksProxy: cfg.socksProxy, + networkProxy: proxy, hostMode: cfg.hostMode, requiredHostMode: cfg.requiredHostMode ) @@ -498,7 +546,7 @@ struct MigrateFromDevice: View { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { migrationState = .linkShown(fileId: fileTransferMeta.fileId, link: data.addToLink(link: rcvURIs[0]), archivePath: archivePath, ctrl: ctrl) } - case .sndFileError: + case .result(.sndFileError): alert = .error(title: "Upload failed", error: "Check your internet connection and try again") migrationState = .uploadFailed(totalBytes: totalBytes, archivePath: archivePath) default: @@ -555,7 +603,7 @@ struct MigrateFromDevice: View { } catch let error { fatalError("Error starting chat \(responseError(error))") } - showSettings = false + dismissAllSheets(animated: true) } } catch let error { alert = .error(title: "Error deleting database", error: responseError(error)) @@ -578,9 +626,7 @@ struct MigrateFromDevice: View { } // Hide settings anyway if chatDbStatus is not ok, probably passphrase needs to be entered if dismiss || m.chatDbStatus != .ok { - await MainActor.run { - showSettings = false - } + dismissAllSheets(animated: true) } } @@ -590,6 +636,7 @@ struct MigrateFromDevice: View { } private struct PassphraseConfirmationView: View { + @EnvironmentObject var theme: AppTheme @Binding var migrationState: MigrationFromState @State private var useKeychain = storeDBPassphraseGroupDefault.get() @State private var currentKey: String = "" @@ -612,15 +659,17 @@ private struct PassphraseConfirmationView: View { verifyingPassphrase = false } }) { - settingsRow(useKeychain ? "key" : "lock", color: .secondary) { + settingsRow(useKeychain ? "key" : "lock", color: theme.colors.secondary) { Text("Verify passphrase") } } .disabled(verifyingPassphrase || currentKey.isEmpty) } header: { Text("Verify database passphrase") + .foregroundColor(theme.colors.secondary) } footer: { Text("Confirm that you remember database passphrase to migrate it.") + .foregroundColor(theme.colors.secondary) .font(.callout) } .onAppear { @@ -642,10 +691,10 @@ private struct PassphraseConfirmationView: View { migrationState = .uploadConfirmation } } catch let error { - if case .chatCmdError(_, .errorDatabase(.errorOpen(.errorNotADatabase))) = error as? ChatResponse { + if case .errorDatabase(.errorOpen(.errorNotADatabase)) = error as? ChatError { showErrorOnMigrationIfNeeded(.errorNotADatabase(dbFile: ""), $alert) } else { - alert = .error(title: "Error", error: NSLocalizedString("Error verifying passphrase:", comment: "") + " " + String(String(describing: error))) + alert = .error(title: "Error", error: NSLocalizedString("Error verifying passphrase:", comment: "") + " " + String(responseError(error))) } } } @@ -684,11 +733,11 @@ func chatStoppedView() -> some View { private class MigrationChatReceiver { let ctrl: chat_ctrl let databaseUrl: URL - let processReceivedMsg: (ChatResponse) async -> Void + let processReceivedMsg: (APIResult) async -> Void private var receiveLoop: Task? private var receiveMessages = true - init(ctrl: chat_ctrl, databaseUrl: URL, _ processReceivedMsg: @escaping (ChatResponse) async -> Void) { + init(ctrl: chat_ctrl, databaseUrl: URL, _ processReceivedMsg: @escaping (APIResult) async -> Void) { self.ctrl = ctrl self.databaseUrl = databaseUrl self.processReceivedMsg = processReceivedMsg @@ -703,9 +752,9 @@ private class MigrationChatReceiver { func receiveMsgLoop() async { // TODO use function that has timeout - if let msg = await chatRecvMsg(ctrl) { + if let msg: APIResult = await chatRecvMsg(ctrl) { Task { - await TerminalItems.shared.add(.resp(.now, msg)) + await TerminalItems.shared.addResult(msg) } logger.debug("processReceivedMsg: \(msg.responseType)") await processReceivedMsg(msg) @@ -729,6 +778,6 @@ private class MigrationChatReceiver { struct MigrateFromDevice_Previews: PreviewProvider { static var previews: some View { - MigrateFromDevice(showSettings: Binding.constant(true), showProgressOnSettings: Binding.constant(false)) + MigrateFromDevice(showProgressOnSettings: Binding.constant(false)) } } diff --git a/apps/ios/Shared/Views/Migration/MigrateToDevice.swift b/apps/ios/Shared/Views/Migration/MigrateToDevice.swift index 9afd0dd406..93fe19cf33 100644 --- a/apps/ios/Shared/Views/Migration/MigrateToDevice.swift +++ b/apps/ios/Shared/Views/Migration/MigrateToDevice.swift @@ -91,16 +91,21 @@ private enum MigrateToDeviceViewAlert: Identifiable { struct MigrateToDevice: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @Environment(\.dismiss) var dismiss: DismissAction - @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false @Binding var migrationState: MigrationToState? @State private var useKeychain = storeDBPassphraseGroupDefault.get() @State private var alert: MigrateToDeviceViewAlert? + @State private var databaseAlert: DatabaseAlert? = nil private let tempDatabaseUrl = urlForTemporaryDatabase() @State private var chatReceiver: MigrationChatReceiver? = nil // Prevent from hiding the view until migration is finished or app deleted @State private var backDisabled: Bool = false @State private var showQRCodeScanner: Bool = true + @State private var pasteboardHasStrings = UIPasteboard.general.hasStrings + + @State private var importingArchiveFromFileProgressIndicator = false + @State private var showFileImporter = false var body: some View { VStack { @@ -174,13 +179,27 @@ struct MigrateToDevice: View { return Alert(title: Text(title), message: Text(error)) } } + .alert(item: $databaseAlert) { item in + switch item { + case .archiveImported: + let (title, message) = archiveImportedAlertText() + return Alert(title: Text(title), message: Text(message)) + case let .archiveImportedWithErrors(errs): + let (title, message) = archiveImportedWithErrorsAlertText(errs: errs) + return Alert(title: Text(title), message: Text(message)) + case let .error(title, error): + return Alert(title: Text(title), message: Text(error)) + default: // not expected this branch to be called because this alert is used only for importArchive purpose + return Alert(title: Text("Error")) + } + } .interactiveDismissDisabled(backDisabled) } private func pasteOrScanLinkView() -> some View { ZStack { List { - Section("Scan QR code") { + Section(header: Text("Scan QR code").foregroundColor(theme.colors.secondary)) { ScannerInView(showQRCodeScanner: $showQRCodeScanner) { resp in switch resp { case let .success(r): @@ -196,11 +215,15 @@ struct MigrateToDevice: View { } } } - if developerTools { - Section("Or paste archive link") { - pasteLinkView() - } + Section(header: Text("Or paste archive link").foregroundColor(theme.colors.secondary)) { + pasteLinkView() } + Section(header: Text("Or import archive file").foregroundColor(theme.colors.secondary)) { + archiveImportFromFileView() + } + } + if importingArchiveFromFileProgressIndicator { + progressView() } } } @@ -217,15 +240,44 @@ struct MigrateToDevice: View { } label: { Text("Tap to paste link") } - .disabled(!ChatModel.shared.pasteboardHasStrings) + .disabled(!pasteboardHasStrings) .frame(maxWidth: .infinity, alignment: .center) } + private func archiveImportFromFileView() -> some View { + Button { + showFileImporter = true + } label: { + Label("Import database", systemImage: "square.and.arrow.down") + } + .disabled(importingArchiveFromFileProgressIndicator) + .fileImporter( + isPresented: $showFileImporter, + allowedContentTypes: [.zip], + allowsMultipleSelection: false + ) { result in + if case let .success(files) = result, let fileURL = files.first { + Task { + let success = await DatabaseView.importArchive(fileURL, $importingArchiveFromFileProgressIndicator, $databaseAlert, true) + if success { + DatabaseView.startChat( + Binding.constant(false), + $importingArchiveFromFileProgressIndicator + ) + hideView() + } + } + } + } + } + + private func linkDownloadingView(_ link: String) -> some View { ZStack { List { Section {} header: { Text("Downloading link details") + .foregroundColor(theme.colors.secondary) } } progressView() @@ -240,10 +292,11 @@ struct MigrateToDevice: View { List { Section {} header: { Text("Downloading archive") + .foregroundColor(theme.colors.secondary) } } let ratio = Float(downloadedBytes) / Float(max(totalBytes, 1)) - MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: downloadedBytes, countStyle: .binary)) downloaded") + MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: downloadedBytes, countStyle: .binary)) downloaded", theme.colors.primary) } } @@ -254,14 +307,16 @@ struct MigrateToDevice: View { try? FileManager.default.removeItem(atPath: archivePath) migrationState = .linkDownloading(link: link) }) { - settingsRow("tray.and.arrow.down") { - Text("Repeat download").foregroundColor(.accentColor) + settingsRow("tray.and.arrow.down", color: theme.colors.secondary) { + Text("Repeat download").foregroundColor(theme.colors.primary) } } } header: { Text("Download failed") + .foregroundColor(theme.colors.secondary) } footer: { Text("You can give another try.") + .foregroundColor(theme.colors.secondary) .font(.callout) } } @@ -277,6 +332,7 @@ struct MigrateToDevice: View { List { Section {} header: { Text("Importing archive") + .foregroundColor(theme.colors.secondary) } } progressView() @@ -292,14 +348,16 @@ struct MigrateToDevice: View { Button(action: { migrationState = .archiveImport(archivePath: archivePath) }) { - settingsRow("square.and.arrow.down") { - Text("Repeat import").foregroundColor(.accentColor) + settingsRow("square.and.arrow.down", color: theme.colors.secondary) { + Text("Repeat import").foregroundColor(theme.colors.primary) } } } header: { Text("Import failed") + .foregroundColor(theme.colors.secondary) } footer: { Text("You can give another try.") + .foregroundColor(theme.colors.secondary) .font(.callout) } } @@ -323,7 +381,7 @@ struct MigrateToDevice: View { case let .migrationError(mtrError): ("Incompatible database version", nil, - "\(NSLocalizedString("Error: ", comment: "")) \(DatabaseErrorView.mtrErrorDescription(mtrError))", + "\(NSLocalizedString("Error: ", comment: "")) \(mtrErrorDescription(mtrError))", nil) } default: ("Error", nil, "Unknown error", nil) @@ -333,8 +391,8 @@ struct MigrateToDevice: View { Button(action: { migrationState = .migration(passphrase: passphrase, confirmation: confirmation, useKeychain: useKeychain) }) { - settingsRow("square.and.arrow.down") { - Text(button).foregroundColor(.accentColor) + settingsRow("square.and.arrow.down", color: theme.colors.secondary) { + Text(button).foregroundColor(theme.colors.primary) } } } else { @@ -342,8 +400,10 @@ struct MigrateToDevice: View { } } header: { Text(header) + .foregroundColor(theme.colors.secondary) } footer: { Text(footer) + .foregroundColor(theme.colors.secondary) .font(.callout) } } @@ -354,6 +414,7 @@ struct MigrateToDevice: View { List { Section {} header: { Text("Migrating") + .foregroundColor(theme.colors.secondary) } } progressView() @@ -364,6 +425,7 @@ struct MigrateToDevice: View { } struct OnionView: View { + @EnvironmentObject var theme: AppTheme @State var appSettings: AppSettings @State private var onionHosts: OnionHosts = .no var finishMigration: (AppSettings) -> Void @@ -380,18 +442,20 @@ struct MigrateToDevice: View { appSettings.networkConfig = updated finishMigration(appSettings) }) { - settingsRow("checkmark") { - Text("Apply").foregroundColor(.accentColor) + settingsRow("checkmark", color: theme.colors.secondary) { + Text("Apply").foregroundColor(theme.colors.primary) } } } header: { Text("Confirm network settings") + .foregroundColor(theme.colors.secondary) } footer: { Text("Please confirm that network settings are correct for this device.") + .foregroundColor(theme.colors.secondary) .font(.callout) } - Section("Network settings") { + Section(header: Text("Network settings").foregroundColor(theme.colors.secondary)) { Picker("Use .onion hosts", selection: $onionHosts) { ForEach(OnionHosts.values, id: \.self) { Text($0.text) } } @@ -432,10 +496,10 @@ struct MigrateToDevice: View { chatReceiver = MigrationChatReceiver(ctrl: ctrl, databaseUrl: tempDatabaseUrl) { msg in await MainActor.run { switch msg { - case let .rcvFileProgressXFTP(_, _, receivedSize, totalSize, rcvFileTransfer): + case let .result(.rcvFileProgressXFTP(_, _, receivedSize, totalSize, rcvFileTransfer)): migrationState = .downloadProgress(downloadedBytes: receivedSize, totalBytes: totalSize, fileId: rcvFileTransfer.fileId, link: link, archivePath: archivePath, ctrl: ctrl) MigrationToDeviceState.save(.downloadProgress(link: link, archiveName: URL(fileURLWithPath: archivePath).lastPathComponent)) - case .rcvStandaloneFileComplete: + case .result(.rcvStandaloneFileComplete): DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // User closed the whole screen before new state was saved if migrationState == nil { @@ -445,7 +509,10 @@ struct MigrateToDevice: View { MigrationToDeviceState.save(.archiveImport(archiveName: URL(fileURLWithPath: archivePath).lastPathComponent)) } } - case .rcvFileError: + case .result(.rcvFileError): + alert = .error(title: "Download failed", error: "File was deleted or link is invalid") + migrationState = .downloadFailed(totalBytes: totalBytes, link: link, archivePath: archivePath) + case .error(.error(.noRcvFileUser)): alert = .error(title: "Download failed", error: "File was deleted or link is invalid") migrationState = .downloadFailed(totalBytes: totalBytes, link: link, archivePath: archivePath) default: @@ -470,8 +537,12 @@ struct MigrateToDevice: View { do { if !hasChatCtrl() { chatInitControllerRemovingDatabases() + } else if ChatModel.shared.chatRunning == true { + // cannot delete storage if chat is running + try await stopChatAsync() } try await apiDeleteStorage() + try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true) do { let config = ArchiveConfig(archivePath: archivePath) let archiveErrors = try await apiImportArchive(config: config) @@ -538,11 +609,22 @@ struct MigrateToDevice: View { do { try? FileManager.default.removeItem(at: getMigrationTempFilesDirectory()) MigrationToDeviceState.save(nil) - appSettings.importIntoApp() - try SimpleX.startChat(refreshInvitations: true) - AlertManager.shared.showAlertMsg(title: "Chat migrated!", message: "Finalize migration on another device.") + try ObjC.catchException { + appSettings.importIntoApp() + } + do { + try SimpleX.startChat(refreshInvitations: true) + AlertManager.shared.showAlertMsg(title: "Chat migrated!", message: "Finalize migration on another device.") + } catch let error { + AlertManager.shared.showAlert(Alert(title: Text("Error starting chat"), message: Text(responseError(error)))) + } } catch let error { - AlertManager.shared.showAlert(Alert(title: Text("Error starting chat"), message: Text(responseError(error)))) + logger.error("Error importing settings: \(error.localizedDescription)") + AlertManager.shared.showAlert( + Alert( + title: Text("Error migrating settings"), + message: Text ("Some app settings were not migrated.") + textNewLine + Text(responseError(error))) + ) } hideView() } @@ -550,6 +632,8 @@ struct MigrateToDevice: View { private func hideView() { onboardingStageDefault.set(.onboardingComplete) m.onboardingStage = .onboardingComplete + m.migrationState = nil + MigrationToDeviceState.save(nil) dismiss() } @@ -563,6 +647,7 @@ struct MigrateToDevice: View { } private struct PassphraseEnteringView: View { + @EnvironmentObject var theme: AppTheme @Binding var migrationState: MigrationToState? @State private var useKeychain = true @State var currentKey: String @@ -574,7 +659,7 @@ private struct PassphraseEnteringView: View { ZStack { List { Section { - settingsRow("key", color: .secondary) { + settingsRow("key", color: theme.colors.secondary) { Toggle("Save passphrase in Keychain", isOn: $useKeychain) } @@ -603,13 +688,14 @@ private struct PassphraseEnteringView: View { verifyingPassphrase = false } }) { - settingsRow("key", color: .secondary) { + settingsRow("key", color: theme.colors.secondary) { Text("Open chat") } } .disabled(verifyingPassphrase || currentKey.isEmpty) } header: { Text("Enter passphrase") + .foregroundColor(theme.colors.secondary) } footer: { VStack(alignment: .leading, spacing: 16) { if useKeychain { @@ -620,6 +706,7 @@ private struct PassphraseEnteringView: View { Text("**Warning**: Instant push notifications require passphrase saved in Keychain.") } } + .foregroundColor(theme.colors.secondary) .font(.callout) .padding(.top, 1) .onTapGesture { keyboardVisible = false } @@ -664,11 +751,11 @@ private func progressView() -> some View { private class MigrationChatReceiver { let ctrl: chat_ctrl let databaseUrl: URL - let processReceivedMsg: (ChatResponse) async -> Void + let processReceivedMsg: (APIResult) async -> Void private var receiveLoop: Task? private var receiveMessages = true - init(ctrl: chat_ctrl, databaseUrl: URL, _ processReceivedMsg: @escaping (ChatResponse) async -> Void) { + init(ctrl: chat_ctrl, databaseUrl: URL, _ processReceivedMsg: @escaping (APIResult) async -> Void) { self.ctrl = ctrl self.databaseUrl = databaseUrl self.processReceivedMsg = processReceivedMsg @@ -685,7 +772,7 @@ private class MigrationChatReceiver { // TODO use function that has timeout if let msg = await chatRecvMsg(ctrl) { Task { - await TerminalItems.shared.add(.resp(.now, msg)) + await TerminalItems.shared.addResult(msg) } logger.debug("processReceivedMsg: \(msg.responseType)") await processReceivedMsg(msg) diff --git a/apps/ios/Shared/Views/NewChat/AddContactLearnMore.swift b/apps/ios/Shared/Views/NewChat/AddContactLearnMore.swift index 45eb783326..3a64a955c5 100644 --- a/apps/ios/Shared/Views/NewChat/AddContactLearnMore.swift +++ b/apps/ios/Shared/Views/NewChat/AddContactLearnMore.swift @@ -28,8 +28,11 @@ struct AddContactLearnMore: View { Text("If you can't meet in person, show QR code in a video call, or share the link.") Text("Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).") } + .frame(maxWidth: .infinity, alignment: .leading) .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) } + .modifier(ThemedBackground(grouped: true)) } } diff --git a/apps/ios/Shared/Views/NewChat/AddGroupView.swift b/apps/ios/Shared/Views/NewChat/AddGroupView.swift index 3f3623033e..87c0b80372 100644 --- a/apps/ios/Shared/Views/NewChat/AddGroupView.swift +++ b/apps/ios/Shared/Views/NewChat/AddGroupView.swift @@ -11,6 +11,7 @@ import SimpleXChat struct AddGroupView: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @Environment(\.dismiss) var dismiss: DismissAction @AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false @State private var chat: Chat? @@ -22,7 +23,7 @@ struct AddGroupView: View { @State private var showTakePhoto = false @State private var chosenImage: UIImage? = nil @State private var showInvalidNameAlert = false - @State private var groupLink: String? + @State private var groupLink: CreatedConnLink? @State private var groupLinkMemberRole: GroupMemberRole = .member var body: some View { @@ -34,45 +35,40 @@ struct AddGroupView: View { creatingGroup: true, showFooterCounter: false ) { _ in - dismiss() DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - m.chatId = groupInfo.id + dismissAllSheets(animated: true) { + ItemsModel.shared.loadOpenChat(groupInfo.id) + } } } + .navigationBarTitleDisplayMode(.inline) } else { GroupLinkView( groupId: groupInfo.groupId, groupLink: $groupLink, groupLinkMemberRole: $groupLinkMemberRole, - showTitle: true, + showTitle: false, creatingGroup: true ) { - dismiss() DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - m.chatId = groupInfo.id + dismissAllSheets(animated: true) { + ItemsModel.shared.loadOpenChat(groupInfo.id) + } } } + .navigationBarTitle("Group link") } } else { - createGroupView().keyboardPadding() + createGroupView() } } func createGroupView() -> some View { List { Group { - Text("Create secret group") - .font(.largeTitle) - .bold() - .fixedSize(horizontal: false, vertical: true) - .padding(.bottom, 24) - .onTapGesture(perform: hideKeyboard) - ZStack(alignment: .center) { ZStack(alignment: .topTrailing) { - ProfileImage(imageStr: profile.image, color: Color(uiColor: .secondarySystemGroupedBackground)) - .aspectRatio(1, contentMode: .fit) - .frame(maxWidth: 128, maxHeight: 128) + ProfileImage(imageStr: profile.image, size: 128) if profile.image != nil { Button { profile.image = nil @@ -97,7 +93,7 @@ struct AddGroupView: View { Section { groupNameTextField() Button(action: createGroup) { - settingsRow("checkmark", color: .accentColor) { Text("Create group") } + settingsRow("checkmark", color: theme.colors.primary) { Text("Create group") } } .disabled(!canCreateProfile()) IncognitoToggle(incognitoEnabled: $incognitoDefault) @@ -106,6 +102,7 @@ struct AddGroupView: View { sharedGroupProfileInfo(incognitoDefault) Text("Fully decentralized – visible only to members.") } + .foregroundColor(theme.colors.secondary) .frame(maxWidth: .infinity, alignment: .leading) .onTapGesture(perform: hideKeyboard) } @@ -140,12 +137,16 @@ struct AddGroupView: View { createInvalidNameAlert(mkValidName(profile.displayName), $profile.displayName) } .onChange(of: chosenImage) { image in - if let image = image { - profile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) - } else { - profile.image = nil + Task { + let resized: String? = if let image { + await resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) + } else { + nil + } + await MainActor.run { profile.image = resized } } } + .modifier(ThemedBackground(grouped: true)) } func groupNameTextField() -> some View { @@ -158,7 +159,7 @@ struct AddGroupView: View { Image(systemName: "exclamationmark.circle").foregroundColor(.red) } } else { - Image(systemName: "pencil").foregroundColor(.secondary) + Image(systemName: "pencil").foregroundColor(theme.colors.secondary) } textField("Enter group name…", text: $profile.displayName) .focused($focusDisplayName) @@ -190,10 +191,7 @@ struct AddGroupView: View { profile.groupPreferences = GroupPreferences(history: GroupPreference(enable: .on)) let gInfo = try apiNewGroup(incognito: incognitoDefault, groupProfile: profile) Task { - let groupMembers = await apiListMembers(gInfo.groupId) - await MainActor.run { - m.groupMembers = groupMembers.map { GMember.init($0) } - } + await m.loadGroupMembers(gInfo) } let c = Chat(chatInfo: .group(groupInfo: gInfo), chatItems: []) m.addChat(c) @@ -202,13 +200,14 @@ struct AddGroupView: View { chat = c } } catch { - dismiss() - AlertManager.shared.showAlert( - Alert( - title: Text("Error creating group"), - message: Text(responseError(error)) + dismissAllSheets(animated: true) { + AlertManager.shared.showAlert( + Alert( + title: Text("Error creating group"), + message: Text(responseError(error)) + ) ) - ) + } } } diff --git a/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift b/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift index c3452ce18d..e5263813fa 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift @@ -7,46 +7,491 @@ // import SwiftUI +import SimpleXChat -enum NewChatMenuOption: Identifiable { - case newContact - case newGroup - - var id: Self { self } +enum ContactType: Int { + case card, request, recent, chatDeleted, unlisted } struct NewChatMenuButton: View { - @Binding var newChatMenuOption: NewChatMenuOption? + // do not use chatModel here because it prevents showing AddGroupMembersView after group creation and QR code after link creation on iOS 16 +// @EnvironmentObject var chatModel: ChatModel + @State private var showNewChatSheet = false + @State private var alert: SomeAlert? = nil var body: some View { - Menu { Button { - newChatMenuOption = .newContact - } label: { - Text("Add contact") - } - Button { - newChatMenuOption = .newGroup - } label: { - Text("Create group") - } + showNewChatSheet = true } label: { Image(systemName: "square.and.pencil") .resizable() .scaledToFit() .frame(width: 24, height: 24) } - .sheet(item: $newChatMenuOption) { opt in - switch opt { - case .newContact: NewChatView(selection: .invite) - case .newGroup: AddGroupView() - } + .appSheet(isPresented: $showNewChatSheet) { + NewChatSheet() + .environment(\EnvironmentValues.refresh as! WritableKeyPath, nil) + } + .alert(item: $alert) { a in + return a.alert } } } -#Preview { - NewChatMenuButton( - newChatMenuOption: Binding.constant(nil) - ) +private var indent: CGFloat = 36 + +struct NewChatSheet: View { + @EnvironmentObject var theme: AppTheme + @State private var baseContactTypes: [ContactType] = [.card, .request, .recent] + @EnvironmentObject var chatModel: ChatModel + @State private var searchMode = false + @FocusState var searchFocussed: Bool + @State private var searchText = "" + @State private var searchShowingSimplexLink = false + @State private var searchChatFilteredBySimplexLink: String? = nil + @State private var alert: SomeAlert? + + // Sheet height management + @State private var isAddContactActive = false + @State private var isScanPasteLinkActive = false + @State private var isLargeSheet = false + @State private var allowSmallSheet = true + + @AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true + + var body: some View { + let showArchive = !filterContactTypes(chats: chatModel.chats, contactTypes: [.chatDeleted]).isEmpty + let v = NavigationView { + viewBody(showArchive) + .navigationTitle("New message") + .navigationBarTitleDisplayMode(.large) + .navigationBarHidden(searchMode) + .modifier(ThemedBackground(grouped: true)) + .alert(item: $alert) { a in + return a.alert + } + } + if #available(iOS 16.0, *), oneHandUI { + let sheetHeight: CGFloat = showArchive ? 575 : 500 + v.presentationDetents( + allowSmallSheet ? [.height(sheetHeight), .large] : [.large], + selection: Binding( + get: { isLargeSheet || !allowSmallSheet ? .large : .height(sheetHeight) }, + set: { isLargeSheet = $0 == .large } + ) + ) + } else { + v + } + } + + private func viewBody(_ showArchive: Bool) -> some View { + List { + HStack { + ContactsListSearchBar( + searchMode: $searchMode, + searchFocussed: $searchFocussed, + searchText: $searchText, + searchShowingSimplexLink: $searchShowingSimplexLink, + searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink + ) + .frame(maxWidth: .infinity) + } + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + + if (searchText.isEmpty) { + Section { + NavigationLink(isActive: $isAddContactActive) { + NewChatView(selection: .invite) + .navigationTitle("New chat") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.large) + } label: { + navigateOnTap(Label("Create 1-time link", systemImage: "link.badge.plus")) { + isAddContactActive = true + } + } + NavigationLink(isActive: $isScanPasteLinkActive) { + NewChatView(selection: .connect, showQRCodeScanner: true) + .navigationTitle("New chat") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.large) + } label: { + navigateOnTap(Label("Scan / Paste link", systemImage: "qrcode")) { + isScanPasteLinkActive = true + } + } + NavigationLink { + AddGroupView() + .navigationTitle("Create secret group") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.large) + } label: { + Label("Create group", systemImage: "person.2.circle.fill") + } + } + + if (showArchive) { + Section { + NavigationLink { + DeletedChats() + } label: { + newChatActionButton("archivebox", color: theme.colors.secondary) { Text("Archived contacts") } + } + } + } + } + + ContactsList( + baseContactTypes: $baseContactTypes, + searchMode: $searchMode, + searchText: $searchText, + header: "Your Contacts", + searchFocussed: $searchFocussed, + searchShowingSimplexLink: $searchShowingSimplexLink, + searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink, + showDeletedChatIcon: true + ) + } + } + + /// Extends label's tap area to match `.insetGrouped` list row insets + private func navigateOnTap(_ label: L, setActive: @escaping () -> Void) -> some View { + label + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.leading, 16).padding(.vertical, 8).padding(.trailing, 32) + .contentShape(Rectangle()) + .onTapGesture { + isLargeSheet = true + DispatchQueue.main.async { + allowSmallSheet = false + setActive() + } + } + .padding(.leading, -16).padding(.vertical, -8).padding(.trailing, -32) + } + + func newChatActionButton(_ icon: String, color: Color/* = .secondary*/, content: @escaping () -> Content) -> some View { + ZStack(alignment: .leading) { + Image(systemName: icon) + .resizable() + .scaledToFit() + .frame(maxWidth: 24, maxHeight: 24, alignment: .center) + .symbolRenderingMode(.monochrome) + .foregroundColor(color) + content().foregroundColor(theme.colors.onBackground).padding(.leading, indent) + } + } +} + +func chatContactType(_ chat: Chat) -> ContactType { + switch chat.chatInfo { + case .contactRequest: + return .request + case let .direct(contact): + if contact.activeConn == nil && contact.profile.contactLink != nil && contact.active { + return .card + } else if contact.chatDeleted { + return .chatDeleted + } else if contact.contactStatus == .active { + return .recent + } else { + return .unlisted + } + default: + return .unlisted + } +} + +private func filterContactTypes(chats: [Chat], contactTypes: [ContactType]) -> [Chat] { + return chats.filter { chat in + contactTypes.contains(chatContactType(chat)) + } +} + +struct ContactsList: View { + @EnvironmentObject var theme: AppTheme + @EnvironmentObject var chatModel: ChatModel + @Binding var baseContactTypes: [ContactType] + @Binding var searchMode: Bool + @Binding var searchText: String + var header: String? = nil + @FocusState.Binding var searchFocussed: Bool + @Binding var searchShowingSimplexLink: Bool + @Binding var searchChatFilteredBySimplexLink: String? + var showDeletedChatIcon: Bool + @AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false + + var body: some View { + let contactTypes = contactTypesSearchTargets(baseContactTypes: baseContactTypes, searchEmpty: searchText.isEmpty) + let contactChats = filterContactTypes(chats: chatModel.chats, contactTypes: contactTypes) + let filteredContactChats = filteredContactChats( + showUnreadAndFavorites: showUnreadAndFavorites, + searchShowingSimplexLink: searchShowingSimplexLink, + searchChatFilteredBySimplexLink: searchChatFilteredBySimplexLink, + searchText: searchText, + contactChats: contactChats + ) + + if !filteredContactChats.isEmpty { + Section(header: Group { + if let header = header { + Text(header) + .textCase(.uppercase) + .foregroundColor(theme.colors.secondary) + } + } + ) { + ForEach(filteredContactChats, id: \.viewId) { chat in + ContactListNavLink(chat: chat, showDeletedChatIcon: showDeletedChatIcon) + .disabled(chatModel.chatRunning != true) + } + } + } + + if filteredContactChats.isEmpty && !contactChats.isEmpty { + noResultSection(text: "No filtered contacts") + } else if contactChats.isEmpty { + noResultSection(text: "No contacts") + } + } + + private func noResultSection(text: String) -> some View { + Section { + Text(text) + .foregroundColor(theme.colors.secondary) + .frame(maxWidth: .infinity, alignment: .center) + + } + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(top: 7, leading: 0, bottom: 7, trailing: 0)) + } + + private func contactTypesSearchTargets(baseContactTypes: [ContactType], searchEmpty: Bool) -> [ContactType] { + if baseContactTypes.contains(.chatDeleted) || searchEmpty { + return baseContactTypes + } else { + return baseContactTypes + [.chatDeleted] + } + } + + private func chatsByTypeComparator(chat1: Chat, chat2: Chat) -> Bool { + let chat1Type = chatContactType(chat1) + let chat2Type = chatContactType(chat2) + + if chat1Type.rawValue < chat2Type.rawValue { + return true + } else if chat1Type.rawValue > chat2Type.rawValue { + return false + } else { + return chat2.chatInfo.chatTs < chat1.chatInfo.chatTs + } + } + + private func filterChat(chat: Chat, searchText: String, showUnreadAndFavorites: Bool) -> Bool { + var meetsPredicate = true + let s = searchText.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() + let cInfo = chat.chatInfo + + if !searchText.isEmpty { + if (!cInfo.chatViewName.lowercased().contains(searchText.lowercased())) { + if case let .direct(contact) = cInfo { + meetsPredicate = contact.profile.displayName.lowercased().contains(s) || contact.fullName.lowercased().contains(s) + } else { + meetsPredicate = false + } + } + } + + if showUnreadAndFavorites { + meetsPredicate = meetsPredicate && (cInfo.chatSettings?.favorite ?? false) + } + + return meetsPredicate + } + + func filteredContactChats( + showUnreadAndFavorites: Bool, + searchShowingSimplexLink: Bool, + searchChatFilteredBySimplexLink: String?, + searchText: String, + contactChats: [Chat] + ) -> [Chat] { + let linkChatId = searchChatFilteredBySimplexLink + let s = searchShowingSimplexLink ? "" : searchText.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() + + let filteredChats: [Chat] + + if let linkChatId = linkChatId { + filteredChats = contactChats.filter { $0.id == linkChatId } + } else { + filteredChats = contactChats.filter { chat in + filterChat(chat: chat, searchText: s, showUnreadAndFavorites: showUnreadAndFavorites) + } + } + + return filteredChats.sorted(by: chatsByTypeComparator) + } +} + +struct ContactsListSearchBar: View { + @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme + @Binding var searchMode: Bool + @FocusState.Binding var searchFocussed: Bool + @Binding var searchText: String + @Binding var searchShowingSimplexLink: Bool + @Binding var searchChatFilteredBySimplexLink: String? + @State private var ignoreSearchTextChange = false + @State private var alert: PlanAndConnectAlert? + @State private var sheet: PlanAndConnectActionSheet? + @AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false + + var body: some View { + HStack(spacing: 12) { + HStack(spacing: 4) { + Spacer() + .frame(width: 8) + Image(systemName: "magnifyingglass") + .resizable() + .scaledToFit() + .frame(width: 16, height: 16) + TextField("Search or paste SimpleX link", text: $searchText) + .foregroundColor(searchShowingSimplexLink ? theme.colors.secondary : theme.colors.onBackground) + .disabled(searchShowingSimplexLink) + .focused($searchFocussed) + .frame(maxWidth: .infinity) + if !searchText.isEmpty { + Image(systemName: "xmark.circle.fill") + .resizable() + .scaledToFit() + .frame(width: 16, height: 16) + .onTapGesture { + searchText = "" + } + } + } + .padding(EdgeInsets(top: 7, leading: 7, bottom: 7, trailing: 7)) + .foregroundColor(theme.colors.secondary) + .background(Color(uiColor: .secondarySystemGroupedBackground)) + .cornerRadius(10.0) + + if searchFocussed { + Text("Cancel") + .foregroundColor(theme.colors.primary) + .onTapGesture { + searchText = "" + searchFocussed = false + } + } else if m.chats.count > 0 { + toggleFilterButton() + } + } + .padding(.top, 24) + .onChange(of: searchFocussed) { sf in + withAnimation { searchMode = sf } + } + .onChange(of: searchText) { t in + if ignoreSearchTextChange { + ignoreSearchTextChange = false + } else { + if let link = strHasSingleSimplexLink(t.trimmingCharacters(in: .whitespaces)) { // if SimpleX link is pasted, show connection dialogue + searchFocussed = false + if case let .simplexLink(linkType, _, smpHosts) = link.format { + ignoreSearchTextChange = true + searchText = simplexLinkText(linkType, smpHosts) + } + searchShowingSimplexLink = true + searchChatFilteredBySimplexLink = nil + connect(link.text) + } else { + if t != "" { // if some other text is pasted, enter search mode + searchFocussed = true + } + searchShowingSimplexLink = false + searchChatFilteredBySimplexLink = nil + } + } + } + .alert(item: $alert) { a in + planAndConnectAlert(a, dismiss: true, cleanup: { searchText = "" }) + } + .actionSheet(item: $sheet) { s in + planAndConnectActionSheet(s, dismiss: true, cleanup: { searchText = "" }) + } + } + + private func toggleFilterButton() -> some View { + ZStack { + Color.clear + .frame(width: 22, height: 22) + Image(systemName: showUnreadAndFavorites ? "line.3.horizontal.decrease.circle.fill" : "line.3.horizontal.decrease") + .resizable() + .scaledToFit() + .foregroundColor(showUnreadAndFavorites ? theme.colors.primary : theme.colors.secondary) + .frame(width: showUnreadAndFavorites ? 22 : 16, height: showUnreadAndFavorites ? 22 : 16) + .onTapGesture { + showUnreadAndFavorites = !showUnreadAndFavorites + } + } + } + + private func connect(_ link: String) { + planAndConnect( + link, + showAlert: { alert = $0 }, + showActionSheet: { sheet = $0 }, + dismiss: true, + incognito: nil, + filterKnownContact: { searchChatFilteredBySimplexLink = $0.id } + ) + } +} + + +struct DeletedChats: View { + @State private var baseContactTypes: [ContactType] = [.chatDeleted] + @State private var searchMode = false + @FocusState var searchFocussed: Bool + @State private var searchText = "" + @State private var searchShowingSimplexLink = false + @State private var searchChatFilteredBySimplexLink: String? = nil + + var body: some View { + List { + ContactsListSearchBar( + searchMode: $searchMode, + searchFocussed: $searchFocussed, + searchText: $searchText, + searchShowingSimplexLink: $searchShowingSimplexLink, + searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink + ) + .listRowSeparator(.hidden) + .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + .frame(maxWidth: .infinity) + + ContactsList( + baseContactTypes: $baseContactTypes, + searchMode: $searchMode, + searchText: $searchText, + searchFocussed: $searchFocussed, + searchShowingSimplexLink: $searchShowingSimplexLink, + searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink, + showDeletedChatIcon: false + ) + } + .navigationTitle("Archived contacts") + .navigationBarTitleDisplayMode(.large) + .navigationBarHidden(searchMode) + .modifier(ThemedBackground(grouped: true)) + + } +} + +#Preview { + NewChatMenuButton() } diff --git a/apps/ios/Shared/Views/NewChat/NewChatView.swift b/apps/ios/Shared/Views/NewChat/NewChatView.swift index 7ece4fdee6..110eda7882 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatView.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatView.swift @@ -10,21 +10,27 @@ import SwiftUI import SimpleXChat import CodeScanner import AVFoundation +import SimpleXChat -enum SomeAlert: Identifiable { - case someAlert(alert: Alert, id: String) +struct SomeAlert: Identifiable { + var alert: Alert + var id: String +} - var id: String { - switch self { - case let .someAlert(_, id): return id - } - } +struct SomeActionSheet: Identifiable { + var actionSheet: ActionSheet + var id: String +} + +struct SomeSheet: Identifiable { + @ViewBuilder var content: Content + var id: String + var fraction = 0.4 } private enum NewChatViewAlert: Identifiable { case planAndConnectAlert(alert: PlanAndConnectAlert) case newChatSomeAlert(alert: SomeAlert) - var id: String { switch self { case let .planAndConnectAlert(alert): return "planAndConnectAlert \(alert.id)" @@ -40,40 +46,64 @@ enum NewChatOption: Identifiable { var id: Self { self } } +func showKeepInvitationAlert() { + if let showingInvitation = ChatModel.shared.showingInvitation, + !showingInvitation.connChatUsed { + showAlert( + NSLocalizedString("Keep unused invitation?", comment: "alert title"), + message: NSLocalizedString("You can view invitation link again in connection details.", comment: "alert message"), + actions: {[ + UIAlertAction( + title: NSLocalizedString("Keep", comment: "alert action"), + style: .default + ), + UIAlertAction( + title: NSLocalizedString("Delete", comment: "alert action"), + style: .destructive, + handler: { _ in + Task { + await deleteChat(Chat( + chatInfo: .contactConnection(contactConnection: showingInvitation.pcc), + chatItems: [] + )) + } + } + ) + ]} + ) + } + ChatModel.shared.showingInvitation = nil +} + struct NewChatView: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @State var selection: NewChatOption @State var showQRCodeScanner = false @State private var invitationUsed: Bool = false - @State private var contactConnection: PendingContactConnection? = nil - @State private var connReqInvitation: String = "" + @State private var connLinkInvitation: CreatedConnLink = CreatedConnLink(connFullLink: "", connShortLink: nil) + @State private var showShortLink = true @State private var creatingConnReq = false + @State var choosingProfile = false @State private var pastedLink: String = "" @State private var alert: NewChatViewAlert? + @State private var contactConnection: PendingContactConnection? = nil var body: some View { VStack(alignment: .leading) { - HStack { - Text("New chat") - .font(.largeTitle) - .bold() - .fixedSize(horizontal: false, vertical: true) - Spacer() - InfoSheetButton { - AddContactLearnMore(showTitle: true) - } - } - .padding() - .padding(.top) - Picker("New chat", selection: $selection) { - Label("Add contact", systemImage: "link") + Label("1-time link", systemImage: "link") .tag(NewChatOption.invite) Label("Connect via link", systemImage: "qrcode") .tag(NewChatOption.connect) } .pickerStyle(.segmented) .padding() + .onChange(of: $selection.wrappedValue) { opt in + if opt == NewChatOption.connect { + showQRCodeScanner = true + } + } VStack { // it seems there's a bug in iOS 15 if several views in switch (or if-else) statement have different transitions @@ -91,10 +121,11 @@ struct NewChatView: View { } } .frame(maxWidth: .infinity, maxHeight: .infinity) + .modifier(ThemedBackground(grouped: true)) .background( // Rectangle is needed for swipe gesture to work on mostly empty views (creatingLinkProgressView and retryButton) Rectangle() - .fill(Color(uiColor: .systemGroupedBackground)) + .fill(theme.base == DefaultTheme.LIGHT ? theme.colors.background.asGroupedBackground(theme.base.mode) : theme.colors.background) ) .animation(.easeInOut(duration: 0.3333), value: selection) .gesture(DragGesture(minimumDistance: 20.0, coordinateSpace: .local) @@ -113,48 +144,44 @@ struct NewChatView: View { } ) } - .background(Color(.systemGroupedBackground)) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + InfoSheetButton { + AddContactLearnMore(showTitle: true) + } + } + } + .modifier(ThemedBackground(grouped: true)) .onChange(of: invitationUsed) { used in if used && !(m.showingInvitation?.connChatUsed ?? true) { m.markShowingInvitationUsed() } } .onDisappear { - if !(m.showingInvitation?.connChatUsed ?? true), - let conn = contactConnection { - AlertManager.shared.showAlert(Alert( - title: Text("Keep unused invitation?"), - message: Text("You can view invitation link again in connection details."), - primaryButton: .default(Text("Keep")) {}, - secondaryButton: .destructive(Text("Delete")) { - Task { - await deleteChat(Chat( - chatInfo: .contactConnection(contactConnection: conn), - chatItems: [] - )) - } - } - )) + if !choosingProfile { + showKeepInvitationAlert() + contactConnection = nil } - m.showingInvitation = nil } .alert(item: $alert) { a in switch(a) { case let .planAndConnectAlert(alert): return planAndConnectAlert(alert, dismiss: true, cleanup: { pastedLink = "" }) - case let .newChatSomeAlert(.someAlert(alert, _)): - return alert + case let .newChatSomeAlert(a): + return a.alert } } } private func prepareAndInviteView() -> some View { ZStack { // ZStack is needed for views to not make transitions between each other - if connReqInvitation != "" { + if connLinkInvitation.connFullLink != "" { InviteView( invitationUsed: $invitationUsed, contactConnection: $contactConnection, - connReqInvitation: connReqInvitation + connLinkInvitation: $connLinkInvitation, + showShortLink: $showShortLink, + choosingProfile: $choosingProfile ) } else if creatingConnReq { creatingLinkProgressView() @@ -165,23 +192,23 @@ struct NewChatView: View { } private func createInvitation() { - if connReqInvitation == "" && contactConnection == nil && !creatingConnReq { + if connLinkInvitation.connFullLink == "" && contactConnection == nil && !creatingConnReq { creatingConnReq = true Task { _ = try? await Task.sleep(nanoseconds: 250_000000) let (r, apiAlert) = await apiAddContact(incognito: incognitoGroupDefault.get()) - if let (connReq, pcc) = r { + if let (connLink, pcc) = r { await MainActor.run { m.updateContactConnection(pcc) - m.showingInvitation = ShowingInvitation(connId: pcc.id, connChatUsed: false) - connReqInvitation = connReq + m.showingInvitation = ShowingInvitation(pcc: pcc, connChatUsed: false) + connLinkInvitation = connLink contactConnection = pcc } } else { await MainActor.run { creatingConnReq = false if let apiAlert = apiAlert { - alert = .newChatSomeAlert(alert: .someAlert(alert: apiAlert, id: "createInvitation error")) + alert = .newChatSomeAlert(alert: SomeAlert(alert: apiAlert, id: "createInvitation error")) } } } @@ -205,49 +232,74 @@ struct NewChatView: View { } } +private func incognitoProfileImage() -> some View { + Image(systemName: "theatermasks.fill") + .resizable() + .scaledToFit() + .frame(width: 30) + .foregroundColor(.indigo) +} + private struct InviteView: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme @Binding var invitationUsed: Bool @Binding var contactConnection: PendingContactConnection? - var connReqInvitation: String + @Binding var connLinkInvitation: CreatedConnLink + @Binding var showShortLink: Bool + @Binding var choosingProfile: Bool + @AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false var body: some View { List { - Section("Share this 1-time invite link") { + Section(header: Text("Share this 1-time invite link").foregroundColor(theme.colors.secondary)) { shareLinkView() } .listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 10)) qrCodeView() - - Section { - IncognitoToggle(incognitoEnabled: $incognitoDefault) - } footer: { - sharedProfileInfo(incognitoDefault) + if let selectedProfile = chatModel.currentUser { + Section { + NavigationLink { + ActiveProfilePicker( + contactConnection: $contactConnection, + connLinkInvitation: $connLinkInvitation, + incognitoEnabled: $incognitoDefault, + choosingProfile: $choosingProfile, + selectedProfile: selectedProfile + ) + } label: { + HStack { + if incognitoDefault { + incognitoProfileImage() + Text("Incognito") + } else { + ProfileImage(imageStr: chatModel.currentUser?.image, size: 30) + Text(chatModel.currentUser?.chatViewName ?? "") + } + } + } + } header: { + Text("Share profile").foregroundColor(theme.colors.secondary) + } footer: { + if incognitoDefault { + Text("A new random profile will be shared.") + } + } } } .onChange(of: incognitoDefault) { incognito in - Task { - do { - if let contactConn = contactConnection, - let conn = try await apiSetConnectionIncognito(connId: contactConn.pccConnId, incognito: incognito) { - await MainActor.run { - contactConnection = conn - chatModel.updateContactConnection(conn) - } - } - } catch { - logger.error("apiSetConnectionIncognito error: \(responseError(error))") - } - } + setInvitationUsed() + } + .onChange(of: chatModel.currentUser) { u in setInvitationUsed() } } private func shareLinkView() -> some View { HStack { - let link = simplexChatLink(connReqInvitation) + let link = connLinkInvitation.simplexChatUri(short: showShortLink) linkTextView(link) Button { showShareSheet(items: [link]) @@ -261,8 +313,9 @@ private struct InviteView: View { } private func qrCodeView() -> some View { - Section("Or show this code") { - SimpleXLinkQRCode(uri: connReqInvitation, onShare: setInvitationUsed) + Section { + SimpleXCreatedLinkQRCode(link: connLinkInvitation, short: $showShortLink, onShare: setInvitationUsed) + .id("simplex-qrcode-view-for-\(connLinkInvitation.simplexChatUri(short: showShortLink))") .padding() .background( RoundedRectangle(cornerRadius: 12, style: .continuous) @@ -272,6 +325,8 @@ private struct InviteView: View { .listRowBackground(Color.clear) .listRowSeparator(.hidden) .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + } header: { + ToggleShortLinkHeader(text: Text("Or show this code"), link: connLinkInvitation, short: $showShortLink) } } @@ -282,19 +337,271 @@ private struct InviteView: View { } } +private enum ProfileSwitchStatus { + case switchingUser + case switchingIncognito + case idle +} + +private struct ActiveProfilePicker: View { + @Environment(\.dismiss) var dismiss + @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme + @Binding var contactConnection: PendingContactConnection? + @Binding var connLinkInvitation: CreatedConnLink + @Binding var incognitoEnabled: Bool + @Binding var choosingProfile: Bool + @State private var alert: SomeAlert? + @State private var profileSwitchStatus: ProfileSwitchStatus = .idle + @State private var switchingProfileByTimeout = false + @State private var lastSwitchingProfileByTimeoutCall: Double? + @State private var profiles: [User] = [] + @State private var searchTextOrPassword = "" + @State private var showIncognitoSheet = false + @State private var incognitoFirst: Bool = false + @State var selectedProfile: User + var trimmedSearchTextOrPassword: String { searchTextOrPassword.trimmingCharacters(in: .whitespaces)} + + var body: some View { + viewBody() + .navigationTitle("Select chat profile") + .searchable(text: $searchTextOrPassword, placement: .navigationBarDrawer(displayMode: .always)) + .autocorrectionDisabled(true) + .navigationBarTitleDisplayMode(.large) + .onAppear { + profiles = chatModel.users + .map { $0.user } + .sorted { u, _ in u.activeUser } + } + .onChange(of: incognitoEnabled) { incognito in + if profileSwitchStatus != .switchingIncognito { + return + } + + Task { + do { + if let contactConn = contactConnection, + let conn = try await apiSetConnectionIncognito(connId: contactConn.pccConnId, incognito: incognito) { + await MainActor.run { + contactConnection = conn + chatModel.updateContactConnection(conn) + profileSwitchStatus = .idle + dismiss() + } + } + } catch { + profileSwitchStatus = .idle + incognitoEnabled = !incognito + logger.error("apiSetConnectionIncognito error: \(responseError(error))") + let err = getErrorAlert(error, "Error changing to incognito!") + + alert = SomeAlert( + alert: Alert( + title: Text(err.title), + message: Text(err.message ?? "Error: \(responseError(error))") + ), + id: "setConnectionIncognitoError" + ) + } + } + } + .onChange(of: profileSwitchStatus) { sp in + if sp != .idle { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + switchingProfileByTimeout = profileSwitchStatus != .idle + } + } else { + switchingProfileByTimeout = false + } + } + .onChange(of: selectedProfile) { profile in + if (profileSwitchStatus != .switchingUser) { + return + } + Task { + do { + if let contactConn = contactConnection { + let conn = try await apiChangeConnectionUser(connId: contactConn.pccConnId, userId: profile.userId) + await MainActor.run { + contactConnection = conn + connLinkInvitation = conn.connLinkInv ?? CreatedConnLink(connFullLink: "", connShortLink: nil) + incognitoEnabled = false + chatModel.updateContactConnection(conn) + } + do { + try await changeActiveUserAsync_(profile.userId, viewPwd: profile.hidden ? trimmedSearchTextOrPassword : nil ) + await MainActor.run { + profileSwitchStatus = .idle + dismiss() + } + } catch { + await MainActor.run { + profileSwitchStatus = .idle + alert = SomeAlert( + alert: Alert( + title: Text("Error switching profile"), + message: Text("Your connection was moved to \(profile.chatViewName) but an unexpected error occurred while redirecting you to the profile.") + ), + id: "switchingProfileError" + ) + } + } + } + } catch { + await MainActor.run { + profileSwitchStatus = .idle + if let currentUser = chatModel.currentUser { + selectedProfile = currentUser + } + let err = getErrorAlert(error, "Error changing connection profile") + alert = SomeAlert( + alert: Alert( + title: Text(err.title), + message: Text(err.message ?? "Error: \(responseError(error))") + ), + id: "changeConnectionUserError" + ) + } + } + } + } + .alert(item: $alert) { a in + a.alert + } + .onAppear { + incognitoFirst = incognitoEnabled + choosingProfile = true + } + .onDisappear { + choosingProfile = false + } + .sheet(isPresented: $showIncognitoSheet) { + IncognitoHelp() + } + } + + + @ViewBuilder private func viewBody() -> some View { + profilePicker() + .allowsHitTesting(!switchingProfileByTimeout) + .modifier(ThemedBackground(grouped: true)) + .overlay { + if switchingProfileByTimeout { + ProgressView() + .scaleEffect(2) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + } + + private func filteredProfiles() -> [User] { + let s = trimmedSearchTextOrPassword + let lower = s.localizedLowercase + + return profiles.filter { u in + if (u.activeUser || !u.hidden) && (s == "" || u.chatViewName.localizedLowercase.contains(lower)) { + return true + } + return correctPassword(u, s) + } + } + + private func profilerPickerUserOption(_ user: User) -> some View { + Button { + if selectedProfile == user && incognitoEnabled { + incognitoEnabled = false + profileSwitchStatus = .switchingIncognito + } else if selectedProfile != user { + selectedProfile = user + profileSwitchStatus = .switchingUser + } + } label: { + HStack { + ProfileImage(imageStr: user.image, size: 30) + .padding(.trailing, 2) + Text(user.chatViewName) + .foregroundColor(theme.colors.onBackground) + .lineLimit(1) + Spacer() + if selectedProfile == user, !incognitoEnabled { + Image(systemName: "checkmark") + .resizable().scaledToFit().frame(width: 16) + .foregroundColor(theme.colors.primary) + } + } + } + } + + @ViewBuilder private func profilePicker() -> some View { + let incognitoOption = Button { + if !incognitoEnabled { + incognitoEnabled = true + profileSwitchStatus = .switchingIncognito + } + } label : { + HStack { + incognitoProfileImage() + Text("Incognito") + .foregroundColor(theme.colors.onBackground) + Image(systemName: "info.circle") + .foregroundColor(theme.colors.primary) + .font(.system(size: 14)) + .onTapGesture { + showIncognitoSheet = true + } + Spacer() + if incognitoEnabled { + Image(systemName: "checkmark") + .resizable().scaledToFit().frame(width: 16) + .foregroundColor(theme.colors.primary) + } + } + } + + List { + let filteredProfiles = filteredProfiles() + let activeProfile = filteredProfiles.first { u in u.activeUser } + + if let selectedProfile = activeProfile { + let otherProfiles = filteredProfiles.filter { u in u.userId != activeProfile?.userId } + + if incognitoFirst { + incognitoOption + profilerPickerUserOption(selectedProfile) + } else { + profilerPickerUserOption(selectedProfile) + incognitoOption + } + + ForEach(otherProfiles) { p in + profilerPickerUserOption(p) + } + } else { + incognitoOption + ForEach(filteredProfiles) { p in + profilerPickerUserOption(p) + } + } + } + .opacity(switchingProfileByTimeout ? 0.4 : 1) + } +} + private struct ConnectView: View { @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var theme: AppTheme @Binding var showQRCodeScanner: Bool @Binding var pastedLink: String @Binding var alert: NewChatViewAlert? @State private var sheet: PlanAndConnectActionSheet? + @State private var pasteboardHasStrings = UIPasteboard.general.hasStrings var body: some View { List { - Section("Paste the link you received") { + Section(header: Text("Paste the link you received").foregroundColor(theme.colors.secondary)) { pasteLinkView() } - Section("Or scan QR code") { + Section(header: Text("Or scan QR code").foregroundColor(theme.colors.secondary)) { ScannerInView(showQRCodeScanner: $showQRCodeScanner, processQRCode: processQRCode) } } @@ -315,7 +622,7 @@ private struct ConnectView: View { // showQRCodeScanner = false connect(pastedLink) } else { - alert = .newChatSomeAlert(alert: .someAlert( + alert = .newChatSomeAlert(alert: SomeAlert( alert: mkAlert(title: "Invalid link", message: "The text you pasted is not a SimpleX link."), id: "pasteLinkView: code is not a SimpleX link" )) @@ -324,7 +631,7 @@ private struct ConnectView: View { } label: { Text("Tap to paste link") } - .disabled(!ChatModel.shared.pasteboardHasStrings) + .disabled(!pasteboardHasStrings) .frame(maxWidth: .infinity, alignment: .center) } else { linkTextView(pastedLink) @@ -338,14 +645,14 @@ private struct ConnectView: View { if strIsSimplexLink(r.string) { connect(link) } else { - alert = .newChatSomeAlert(alert: .someAlert( + alert = .newChatSomeAlert(alert: SomeAlert( alert: mkAlert(title: "Invalid QR code", message: "The code you scanned is not a SimpleX link QR code."), id: "processQRCode: code is not a SimpleX link" )) } case let .failure(e): logger.error("processQRCode QR code error: \(e.localizedDescription)") - alert = .newChatSomeAlert(alert: .someAlert( + alert = .newChatSomeAlert(alert: SomeAlert( alert: mkAlert(title: "Invalid QR code", message: "Error scanning code: \(e.localizedDescription)"), id: "processQRCode: failure" )) @@ -367,11 +674,12 @@ struct ScannerInView: View { @Binding var showQRCodeScanner: Bool let processQRCode: (_ resp: Result) -> Void @State private var cameraAuthorizationStatus: AVAuthorizationStatus? + var scanMode: ScanMode = .continuous var body: some View { Group { if showQRCodeScanner, case .authorized = cameraAuthorizationStatus { - CodeScannerView(codeTypes: [.qr], scanMode: .continuous, completion: processQRCode) + CodeScannerView(codeTypes: [.qr], scanMode: scanMode, completion: processQRCode) .aspectRatio(1, contentMode: .fit) .cornerRadius(12) .listRowBackground(Color.clear) @@ -394,6 +702,7 @@ struct ScannerInView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) .foregroundColor(Color.clear) switch cameraAuthorizationStatus { + case .authorized, nil: EmptyView() case .restricted: Text("Camera not available") case .denied: Label("Enable camera access", systemImage: "camera") default: Label("Tap to scan", systemImage: "qrcode") @@ -413,21 +722,26 @@ struct ScannerInView: View { .disabled(cameraAuthorizationStatus == .restricted) } } - .onAppear { + .task { let status = AVCaptureDevice.authorizationStatus(for: .video) cameraAuthorizationStatus = status if showQRCodeScanner { switch status { - case .notDetermined: askCameraAuthorization() + case .notDetermined: await askCameraAuthorizationAsync() case .restricted: showQRCodeScanner = false case .denied: showQRCodeScanner = false case .authorized: () - @unknown default: askCameraAuthorization() + @unknown default: await askCameraAuthorizationAsync() } } } } + func askCameraAuthorizationAsync() async { + await AVCaptureDevice.requestAccess(for: .video) + cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video) + } + func askCameraAuthorization(_ cb: (() -> Void)? = nil) { AVCaptureDevice.requestAccess(for: .video) { allowed in cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video) @@ -436,6 +750,7 @@ struct ScannerInView: View { } } + private func linkTextView(_ link: String) -> some View { Text(link) .lineLimit(1) @@ -486,6 +801,7 @@ func strHasSingleSimplexLink(_ str: String) -> FormattedText? { } struct IncognitoToggle: View { + @EnvironmentObject var theme: AppTheme @Binding var incognitoEnabled: Bool @State private var showIncognitoSheet = false @@ -493,13 +809,13 @@ struct IncognitoToggle: View { ZStack(alignment: .leading) { Image(systemName: incognitoEnabled ? "theatermasks.fill" : "theatermasks") .frame(maxWidth: 24, maxHeight: 24, alignment: .center) - .foregroundColor(incognitoEnabled ? Color.indigo : .secondary) + .foregroundColor(incognitoEnabled ? Color.indigo : theme.colors.secondary) .font(.system(size: 14)) Toggle(isOn: $incognitoEnabled) { HStack(spacing: 6) { Text("Incognito") Image(systemName: "info.circle") - .foregroundColor(.accentColor) + .foregroundColor(theme.colors.primary) .font(.system(size: 14)) } .onTapGesture { @@ -524,23 +840,25 @@ func sharedProfileInfo(_ incognito: Bool) -> Text { } enum PlanAndConnectAlert: Identifiable { - case ownInvitationLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool) - case invitationLinkConnecting(connectionLink: String) - case ownContactAddressConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool) - case contactAddressConnectingConfirmReconnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool) - case groupLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool) - case groupLinkConnectingConfirmReconnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool) - case groupLinkConnecting(connectionLink: String, groupInfo: GroupInfo?) + case ownInvitationLinkConfirmConnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool) + case invitationLinkConnecting(connectionLink: CreatedConnLink) + case ownContactAddressConfirmConnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool) + case contactAddressConnectingConfirmReconnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool) + case groupLinkConfirmConnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool) + case groupLinkConnectingConfirmReconnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool) + case groupLinkConnecting(connectionLink: CreatedConnLink, groupInfo: GroupInfo?) + case error(shortOrFullLink: String, alert: Alert) var id: String { switch self { - case let .ownInvitationLinkConfirmConnect(connectionLink, _, _): return "ownInvitationLinkConfirmConnect \(connectionLink)" - case let .invitationLinkConnecting(connectionLink): return "invitationLinkConnecting \(connectionLink)" - case let .ownContactAddressConfirmConnect(connectionLink, _, _): return "ownContactAddressConfirmConnect \(connectionLink)" - case let .contactAddressConnectingConfirmReconnect(connectionLink, _, _): return "contactAddressConnectingConfirmReconnect \(connectionLink)" - case let .groupLinkConfirmConnect(connectionLink, _, _): return "groupLinkConfirmConnect \(connectionLink)" - case let .groupLinkConnectingConfirmReconnect(connectionLink, _, _): return "groupLinkConnectingConfirmReconnect \(connectionLink)" - case let .groupLinkConnecting(connectionLink, _): return "groupLinkConnecting \(connectionLink)" + case let .ownInvitationLinkConfirmConnect(connectionLink, _, _): return "ownInvitationLinkConfirmConnect \(connectionLink.connFullLink)" + case let .invitationLinkConnecting(connectionLink): return "invitationLinkConnecting \(connectionLink.connFullLink)" + case let .ownContactAddressConfirmConnect(connectionLink, _, _): return "ownContactAddressConfirmConnect \(connectionLink.connFullLink)" + case let .contactAddressConnectingConfirmReconnect(connectionLink, _, _): return "contactAddressConnectingConfirmReconnect \(connectionLink.connFullLink)" + case let .groupLinkConfirmConnect(connectionLink, _, _): return "groupLinkConfirmConnect \(connectionLink.connFullLink)" + case let .groupLinkConnectingConfirmReconnect(connectionLink, _, _): return "groupLinkConnectingConfirmReconnect \(connectionLink.connFullLink)" + case let .groupLinkConnecting(connectionLink, _): return "groupLinkConnecting \(connectionLink.connFullLink)" + case let .error(shortOrFullLink, alert): return "error \(shortOrFullLink)" } } } @@ -605,11 +923,17 @@ func planAndConnectAlert(_ alert: PlanAndConnectAlert, dismiss: Bool, cleanup: ( ) case let .groupLinkConnecting(_, groupInfo): if let groupInfo = groupInfo { - return Alert( + return groupInfo.businessChat == nil + ? Alert( title: Text("Group already exists!"), message: Text("You are already joining the group \(groupInfo.displayName)."), dismissButton: .default(Text("OK")) { cleanup?() } ) + : Alert( + title: Text("Chat already exists!"), + message: Text("You are already connecting to \(groupInfo.displayName)."), + dismissButton: .default(Text("OK")) { cleanup?() } + ) } else { return Alert( title: Text("Already joining the group!"), @@ -617,21 +941,22 @@ func planAndConnectAlert(_ alert: PlanAndConnectAlert, dismiss: Bool, cleanup: ( dismissButton: .default(Text("OK")) { cleanup?() } ) } + case let .error(_, alert): return alert } } enum PlanAndConnectActionSheet: Identifiable { - case askCurrentOrIncognitoProfile(connectionLink: String, connectionPlan: ConnectionPlan?, title: LocalizedStringKey) - case askCurrentOrIncognitoProfileDestructive(connectionLink: String, connectionPlan: ConnectionPlan, title: LocalizedStringKey) + case askCurrentOrIncognitoProfile(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan?, title: LocalizedStringKey) + case askCurrentOrIncognitoProfileDestructive(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, title: LocalizedStringKey) case askCurrentOrIncognitoProfileConnectContactViaAddress(contact: Contact) - case ownGroupLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool?, groupInfo: GroupInfo) + case ownGroupLinkConfirmConnect(connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan, incognito: Bool?, groupInfo: GroupInfo) var id: String { switch self { - case let .askCurrentOrIncognitoProfile(connectionLink, _, _): return "askCurrentOrIncognitoProfile \(connectionLink)" - case let .askCurrentOrIncognitoProfileDestructive(connectionLink, _, _): return "askCurrentOrIncognitoProfileDestructive \(connectionLink)" + case let .askCurrentOrIncognitoProfile(connectionLink, _, _): return "askCurrentOrIncognitoProfile \(connectionLink.connFullLink)" + case let .askCurrentOrIncognitoProfileDestructive(connectionLink, _, _): return "askCurrentOrIncognitoProfileDestructive \(connectionLink.connFullLink)" case let .askCurrentOrIncognitoProfileConnectContactViaAddress(contact): return "askCurrentOrIncognitoProfileConnectContactViaAddress \(contact.contactId)" - case let .ownGroupLinkConfirmConnect(connectionLink, _, _, _): return "ownGroupLinkConfirmConnect \(connectionLink)" + case let .ownGroupLinkConfirmConnect(connectionLink, _, _, _): return "ownGroupLinkConfirmConnect \(connectionLink.connFullLink)" } } } @@ -690,7 +1015,7 @@ func planAndConnectActionSheet(_ sheet: PlanAndConnectActionSheet, dismiss: Bool } func planAndConnect( - _ connectionLink: String, + _ shortOrFullLink: String, showAlert: @escaping (PlanAndConnectAlert) -> Void, showActionSheet: @escaping (PlanAndConnectActionSheet) -> Void, dismiss: Bool, @@ -700,8 +1025,8 @@ func planAndConnect( filterKnownGroup: ((GroupInfo) -> Void)? = nil ) { Task { - do { - let connectionPlan = try await apiConnectPlan(connReq: connectionLink) + let (result, alert) = await apiConnectPlan(connLink: shortOrFullLink) + if let (connectionLink, connectionPlan) = result { switch connectionPlan { case let .invitationLink(ilp): switch ilp { @@ -710,32 +1035,40 @@ func planAndConnect( if let incognito = incognito { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) } else { - showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect via one-time link")) + await MainActor.run { + showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect via one-time link")) + } } case .ownLink: logger.debug("planAndConnect, .invitationLink, .ownLink, incognito=\(incognito?.description ?? "nil")") - if let incognito = incognito { - showAlert(.ownInvitationLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) - } else { - showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own one-time link!")) + await MainActor.run { + if let incognito = incognito { + showAlert(.ownInvitationLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) + } else { + showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own one-time link!")) + } } case let .connecting(contact_): logger.debug("planAndConnect, .invitationLink, .connecting, incognito=\(incognito?.description ?? "nil")") - if let contact = contact_ { - if let f = filterKnownContact { - f(contact) + await MainActor.run { + if let contact = contact_ { + if let f = filterKnownContact { + f(contact) + } else { + openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyConnectingAlert(contact)) } + } } else { - openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyConnectingAlert(contact)) } + showAlert(.invitationLinkConnecting(connectionLink: connectionLink)) } - } else { - showAlert(.invitationLinkConnecting(connectionLink: connectionLink)) } case let .known(contact): logger.debug("planAndConnect, .invitationLink, .known, incognito=\(incognito?.description ?? "nil")") - if let f = filterKnownContact { - f(contact) - } else { - openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyExistsAlert(contact)) } + await MainActor.run { + if let f = filterKnownContact { + f(contact) + } else { + openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyExistsAlert(contact)) } + } } } case let .contactAddress(cap): @@ -745,83 +1078,109 @@ func planAndConnect( if let incognito = incognito { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) } else { - showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect via contact address")) + await MainActor.run { + showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect via contact address")) + } } case .ownLink: logger.debug("planAndConnect, .contactAddress, .ownLink, incognito=\(incognito?.description ?? "nil")") - if let incognito = incognito { - showAlert(.ownContactAddressConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) - } else { - showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own SimpleX address!")) + await MainActor.run { + if let incognito = incognito { + showAlert(.ownContactAddressConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) + } else { + showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own SimpleX address!")) + } } case .connectingConfirmReconnect: logger.debug("planAndConnect, .contactAddress, .connectingConfirmReconnect, incognito=\(incognito?.description ?? "nil")") - if let incognito = incognito { - showAlert(.contactAddressConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) - } else { - showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You have already requested connection!\nRepeat connection request?")) + await MainActor.run { + if let incognito = incognito { + showAlert(.contactAddressConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) + } else { + showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You have already requested connection!\nRepeat connection request?")) + } } case let .connectingProhibit(contact): logger.debug("planAndConnect, .contactAddress, .connectingProhibit, incognito=\(incognito?.description ?? "nil")") - if let f = filterKnownContact { - f(contact) - } else { - openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyConnectingAlert(contact)) } + await MainActor.run { + if let f = filterKnownContact { + f(contact) + } else { + openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyConnectingAlert(contact)) } + } } case let .known(contact): logger.debug("planAndConnect, .contactAddress, .known, incognito=\(incognito?.description ?? "nil")") - if let f = filterKnownContact { - f(contact) - } else { - openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyExistsAlert(contact)) } + await MainActor.run { + if let f = filterKnownContact { + f(contact) + } else { + openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyExistsAlert(contact)) } + } } case let .contactViaAddress(contact): logger.debug("planAndConnect, .contactAddress, .contactViaAddress, incognito=\(incognito?.description ?? "nil")") if let incognito = incognito { connectContactViaAddress_(contact, dismiss: dismiss, incognito: incognito, cleanup: cleanup) } else { - showActionSheet(.askCurrentOrIncognitoProfileConnectContactViaAddress(contact: contact)) + await MainActor.run { + showActionSheet(.askCurrentOrIncognitoProfileConnectContactViaAddress(contact: contact)) + } } } case let .groupLink(glp): switch glp { case .ok: - if let incognito = incognito { - showAlert(.groupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) - } else { - showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Join group")) + await MainActor.run { + if let incognito = incognito { + showAlert(.groupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) + } else { + showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Join group")) + } } case let .ownLink(groupInfo): logger.debug("planAndConnect, .groupLink, .ownLink, incognito=\(incognito?.description ?? "nil")") - if let f = filterKnownGroup { - f(groupInfo) + await MainActor.run { + if let f = filterKnownGroup { + f(groupInfo) + } + showActionSheet(.ownGroupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito, groupInfo: groupInfo)) } - showActionSheet(.ownGroupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito, groupInfo: groupInfo)) case .connectingConfirmReconnect: logger.debug("planAndConnect, .groupLink, .connectingConfirmReconnect, incognito=\(incognito?.description ?? "nil")") - if let incognito = incognito { - showAlert(.groupLinkConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) - } else { - showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You are already joining the group!\nRepeat join request?")) + await MainActor.run { + if let incognito = incognito { + showAlert(.groupLinkConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito)) + } else { + showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You are already joining the group!\nRepeat join request?")) + } } case let .connectingProhibit(groupInfo_): logger.debug("planAndConnect, .groupLink, .connectingProhibit, incognito=\(incognito?.description ?? "nil")") - showAlert(.groupLinkConnecting(connectionLink: connectionLink, groupInfo: groupInfo_)) + await MainActor.run { + showAlert(.groupLinkConnecting(connectionLink: connectionLink, groupInfo: groupInfo_)) + } case let .known(groupInfo): logger.debug("planAndConnect, .groupLink, .known, incognito=\(incognito?.description ?? "nil")") - if let f = filterKnownGroup { - f(groupInfo) - } else { - openKnownGroup(groupInfo, dismiss: dismiss) { AlertManager.shared.showAlert(groupAlreadyExistsAlert(groupInfo)) } + await MainActor.run { + if let f = filterKnownGroup { + f(groupInfo) + } else { + openKnownGroup(groupInfo, dismiss: dismiss) { AlertManager.shared.showAlert(groupAlreadyExistsAlert(groupInfo)) } + } } } + case let .error(chatError): + logger.debug("planAndConnect, .error \(chatErrorString(chatError))") + if let incognito = incognito { + connectViaLink(connectionLink, connectionPlan: nil, dismiss: dismiss, incognito: incognito, cleanup: cleanup) + } else { + showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: nil, title: "Connect via link")) + } } - } catch { - logger.debug("planAndConnect, plan error") - if let incognito = incognito { - connectViaLink(connectionLink, connectionPlan: nil, dismiss: dismiss, incognito: incognito, cleanup: cleanup) - } else { - showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: nil, title: "Connect via link")) + } else if let alert { + await MainActor.run { + showAlert(.error(shortOrFullLink: shortOrFullLink, alert: alert)) } } } @@ -834,28 +1193,31 @@ private func connectContactViaAddress_(_ contact: Contact, dismiss: Bool, incogn dismissAllSheets(animated: true) } } - _ = await connectContactViaAddress(contact.contactId, incognito) + let ok = await connectContactViaAddress(contact.contactId, incognito, showAlert: { AlertManager.shared.showAlert($0) }) + if ok { + AlertManager.shared.showAlert(connReqSentAlert(.contact)) + } cleanup?() } } private func connectViaLink( - _ connectionLink: String, + _ connectionLink: CreatedConnLink, connectionPlan: ConnectionPlan?, dismiss: Bool, incognito: Bool, cleanup: (() -> Void)? ) { Task { - if let (connReqType, pcc) = await apiConnect(incognito: incognito, connReq: connectionLink) { + if let (connReqType, pcc) = await apiConnect(incognito: incognito, connLink: connectionLink) { await MainActor.run { ChatModel.shared.updateContactConnection(pcc) } let crt: ConnReqType - if let plan = connectionPlan { - crt = planToConnReqType(plan) + crt = if let plan = connectionPlan { + planToConnReqType(plan) ?? connReqType } else { - crt = connReqType + connReqType } DispatchQueue.main.async { if dismiss { @@ -878,39 +1240,35 @@ private func connectViaLink( } func openKnownContact(_ contact: Contact, dismiss: Bool, showAlreadyExistsAlert: (() -> Void)?) { - Task { - let m = ChatModel.shared - if let c = m.getContactChat(contact.contactId) { - DispatchQueue.main.async { - if dismiss { - dismissAllSheets(animated: true) { - m.chatId = c.id - showAlreadyExistsAlert?() - } - } else { - m.chatId = c.id + let m = ChatModel.shared + if let c = m.getContactChat(contact.contactId) { + if dismiss { + dismissAllSheets(animated: true) { + ItemsModel.shared.loadOpenChat(c.id) { showAlreadyExistsAlert?() } } + } else { + ItemsModel.shared.loadOpenChat(c.id) { + showAlreadyExistsAlert?() + } } } } func openKnownGroup(_ groupInfo: GroupInfo, dismiss: Bool, showAlreadyExistsAlert: (() -> Void)?) { - Task { - let m = ChatModel.shared - if let g = m.getGroupChat(groupInfo.groupId) { - DispatchQueue.main.async { - if dismiss { - dismissAllSheets(animated: true) { - m.chatId = g.id - showAlreadyExistsAlert?() - } - } else { - m.chatId = g.id + let m = ChatModel.shared + if let g = m.getGroupChat(groupInfo.groupId) { + if dismiss { + dismissAllSheets(animated: true) { + ItemsModel.shared.loadOpenChat(g.id) { showAlreadyExistsAlert?() } } + } else { + ItemsModel.shared.loadOpenChat(g.id) { + showAlreadyExistsAlert?() + } } } } @@ -923,10 +1281,15 @@ func contactAlreadyConnectingAlert(_ contact: Contact) -> Alert { } func groupAlreadyExistsAlert(_ groupInfo: GroupInfo) -> Alert { - mkAlert( + groupInfo.businessChat == nil + ? mkAlert( title: "Group already exists", message: "You are already in group \(groupInfo.displayName)." ) + : mkAlert( + title: "Chat already exists", + message: "You are already connected with \(groupInfo.displayName)." + ) } enum ConnReqType: Equatable { @@ -943,11 +1306,12 @@ enum ConnReqType: Equatable { } } -private func planToConnReqType(_ connectionPlan: ConnectionPlan) -> ConnReqType { +private func planToConnReqType(_ connectionPlan: ConnectionPlan) -> ConnReqType? { switch connectionPlan { - case .invitationLink: return .invitation - case .contactAddress: return .contact - case .groupLink: return .groupLink + case .invitationLink: .invitation + case .contactAddress: .contact + case .groupLink: .groupLink + case .error: nil } } @@ -958,8 +1322,13 @@ func connReqSentAlert(_ type: ConnReqType) -> Alert { ) } -#Preview { - NewChatView( - selection: .invite - ) +struct NewChatView_Previews: PreviewProvider { + static var previews: some View { + @State var parentAlert: SomeAlert? + @State var contactConnection: PendingContactConnection? = nil + + NewChatView( + selection: .invite + ) + } } diff --git a/apps/ios/Shared/Views/NewChat/QRCode.swift b/apps/ios/Shared/Views/NewChat/QRCode.swift index e3bae9287a..453149198b 100644 --- a/apps/ios/Shared/Views/NewChat/QRCode.swift +++ b/apps/ios/Shared/Views/NewChat/QRCode.swift @@ -8,6 +8,7 @@ import SwiftUI import CoreImage.CIFilterBuiltins +import SimpleXChat struct MutableQRCode: View { @Binding var uri: String @@ -20,6 +21,16 @@ struct MutableQRCode: View { } } +struct SimpleXCreatedLinkQRCode: View { + let link: CreatedConnLink + @Binding var short: Bool + var onShare: (() -> Void)? = nil + + var body: some View { + QRCode(uri: link.simplexChatUri(short: short), onShare: onShare) + } +} + struct SimpleXLinkQRCode: View { let uri: String var withLogo: Bool = true @@ -31,12 +42,6 @@ struct SimpleXLinkQRCode: View { } } -func simplexChatLink(_ uri: String) -> String { - uri.starts(with: "simplex:/") - ? uri.replacingOccurrences(of: "simplex:/", with: "https://simplex.chat/") - : uri -} - struct QRCode: View { let uri: String var withLogo: Bool = true @@ -49,34 +54,34 @@ struct QRCode: View { ZStack { if let image = image { qrCodeImage(image) - } - GeometryReader { geo in - ZStack { - if withLogo { - let w = geo.size.width - Image("icon-light") - .resizable() - .scaledToFit() - .frame(width: w * 0.16, height: w * 0.16) - .frame(width: w * 0.165, height: w * 0.165) - .background(.white) - .clipShape(Circle()) + GeometryReader { geo in + ZStack { + if withLogo { + let w = geo.size.width + Image("icon-light") + .resizable() + .scaledToFit() + .frame(width: w * 0.16, height: w * 0.16) + .frame(width: w * 0.165, height: w * 0.165) + .background(.white) + .clipShape(Circle()) + } } - } - .onAppear { - makeScreenshotFunc = { - let size = CGSizeMake(1024 / UIScreen.main.scale, 1024 / UIScreen.main.scale) - showShareSheet(items: [makeScreenshot(geo.frame(in: .local).origin, size)]) - onShare?() + .onAppear { + makeScreenshotFunc = { + let size = CGSizeMake(1024 / UIScreen.main.scale, 1024 / UIScreen.main.scale) + showShareSheet(items: [makeScreenshot(geo.frame(in: .local).origin, size)]) + onShare?() + } } + .frame(width: geo.size.width, height: geo.size.height) } - .frame(width: geo.size.width, height: geo.size.height) + } else { + Color.clear.aspectRatio(1, contentMode: .fit) } } .onTapGesture(perform: makeScreenshotFunc) - .onAppear { - image = image ?? generateImage(uri, tintColor: tintColor) - } + .task { image = await generateImage(uri, tintColor: tintColor) } .frame(maxWidth: .infinity, maxHeight: .infinity) } } @@ -89,7 +94,7 @@ private func qrCodeImage(_ image: UIImage) -> some View { .textSelection(.enabled) } -private func generateImage(_ uri: String, tintColor: UIColor) -> UIImage? { +private func generateImage(_ uri: String, tintColor: UIColor) async -> UIImage? { let context = CIContext() let filter = CIFilter.qrCodeGenerator() filter.message = Data(uri.utf8) diff --git a/apps/ios/Shared/Views/Onboarding/AddressCreationCard.swift b/apps/ios/Shared/Views/Onboarding/AddressCreationCard.swift new file mode 100644 index 0000000000..c8d0faafa7 --- /dev/null +++ b/apps/ios/Shared/Views/Onboarding/AddressCreationCard.swift @@ -0,0 +1,109 @@ +// +// AddressCreationCard.swift +// SimpleX (iOS) +// +// Created by Diogo Cunha on 13/11/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct AddressCreationCard: View { + @EnvironmentObject var theme: AppTheme + @EnvironmentObject private var chatModel: ChatModel + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize + @AppStorage(DEFAULT_ADDRESS_CREATION_CARD_SHOWN) private var addressCreationCardShown = false + @State private var showAddressCreationAlert = false + @State private var showAddressSheet = false + @State private var showAddressInfoSheet = false + + var body: some View { + let addressExists = chatModel.userAddress != nil + let chats = chatModel.chats.filter { chat in + !chat.chatInfo.chatDeleted && !chat.chatInfo.contactCard + } + ZStack(alignment: .topTrailing) { + HStack(alignment: .top, spacing: 16) { + let envelopeSize = dynamicSize(userFont).profileImageSize + Image(systemName: "envelope.circle.fill") + .resizable() + .frame(width: envelopeSize, height: envelopeSize) + .foregroundColor(.accentColor) + VStack(alignment: .leading) { + Text("Your SimpleX address") + .font(.title3) + Spacer() + Text("How to use it") + textSpace + Text(Image(systemName: "info.circle")).foregroundColor(theme.colors.secondary) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + VStack(alignment: .trailing) { + Image(systemName: "multiply") + .foregroundColor(theme.colors.secondary) + .onTapGesture { + showAddressCreationAlert = true + } + Spacer() + Text("Create") + .foregroundColor(.accentColor) + .onTapGesture { + showAddressSheet = true + } + } + } + .onTapGesture { + showAddressInfoSheet = true + } + .padding() + .background(theme.appColors.sentMessage) + .cornerRadius(12) + .frame(height: dynamicSize(userFont).rowHeight) + .alert(isPresented: $showAddressCreationAlert) { + Alert( + title: Text("SimpleX address"), + message: Text("Tap Create SimpleX address in the menu to create it later."), + dismissButton: .default(Text("Ok")) { + withAnimation { + addressCreationCardShown = true + } + } + ) + } + .sheet(isPresented: $showAddressSheet) { + NavigationView { + UserAddressView(autoCreate: true) + .navigationTitle("SimpleX address") + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } + } + .sheet(isPresented: $showAddressInfoSheet) { + NavigationView { + UserAddressLearnMore(showCreateAddressButton: true) + .navigationTitle("Address or 1-time link?") + .navigationBarTitleDisplayMode(.inline) + .modifier(ThemedBackground(grouped: true)) + } + } + .onChange(of: addressExists) { exists in + if exists, !addressCreationCardShown { + addressCreationCardShown = true + } + } + .onChange(of: chats.count) { size in + if size >= 3, !addressCreationCardShown { + addressCreationCardShown = true + } + } + .onAppear { + if addressExists, !addressCreationCardShown { + addressCreationCardShown = true + } + } + } +} + +#Preview { + AddressCreationCard() +} diff --git a/apps/ios/Shared/Views/Onboarding/ChooseServerOperators.swift b/apps/ios/Shared/Views/Onboarding/ChooseServerOperators.swift new file mode 100644 index 0000000000..656cef4a04 --- /dev/null +++ b/apps/ios/Shared/Views/Onboarding/ChooseServerOperators.swift @@ -0,0 +1,412 @@ +// +// ChooseServerOperators.swift +// SimpleX (iOS) +// +// Created by spaced4ndy on 31.10.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +let conditionsURL = URL(string: "https://github.com/simplex-chat/simplex-chat/blob/stable/PRIVACY.md")! + +struct OnboardingButtonStyle: ButtonStyle { + @EnvironmentObject var theme: AppTheme + var isDisabled: Bool = false + + func makeBody(configuration: Configuration) -> some View { + configuration.label + .font(.system(size: 17, weight: .semibold)) + .padding() + .frame(maxWidth: .infinity) + .background( + isDisabled + ? ( + theme.colors.isLight + ? .gray.opacity(0.17) + : .gray.opacity(0.27) + ) + : theme.colors.primary + ) + .foregroundColor( + isDisabled + ? ( + theme.colors.isLight + ? .gray.opacity(0.4) + : .white.opacity(0.2) + ) + : .white + ) + .cornerRadius(16) + .scaleEffect(configuration.isPressed ? 0.95 : 1.0) + } +} + +private enum OnboardingConditionsViewSheet: Identifiable { + case showConditions + case configureOperators + + var id: String { + switch self { + case .showConditions: return "showConditions" + case .configureOperators: return "configureOperators" + } + } +} + +struct OnboardingConditionsView: View { + @EnvironmentObject var theme: AppTheme + @State private var serverOperators: [ServerOperator] = [] + @State private var selectedOperatorIds = Set() + @State private var sheetItem: OnboardingConditionsViewSheet? = nil + @State private var notificationsModeNavLinkActive = false + @State private var justOpened = true + + var selectedOperators: [ServerOperator] { serverOperators.filter { selectedOperatorIds.contains($0.operatorId) } } + + var body: some View { + GeometryReader { g in + let v = ScrollView { + VStack(alignment: .leading, spacing: 20) { + Text("Conditions of use") + .font(.largeTitle) + .bold() + .frame(maxWidth: .infinity, alignment: .center) + .padding(.top, 25) + + Spacer() + + VStack(alignment: .leading, spacing: 20) { + Text("Private chats, groups and your contacts are not accessible to server operators.") + .lineSpacing(2) + .frame(maxWidth: .infinity, alignment: .leading) + Text(""" + By using SimpleX Chat you agree to: + - send only legal content in public groups. + - respect other users – no spam. + """) + .lineSpacing(2) + .frame(maxWidth: .infinity, alignment: .leading) + + Button("Privacy policy and conditions of use.") { + sheetItem = .showConditions + } + .frame(maxWidth: .infinity, alignment: .leading) + } + .padding(.horizontal, 4) + + Spacer() + + VStack(spacing: 12) { + acceptConditionsButton() + + Button("Configure server operators") { + sheetItem = .configureOperators + } + .frame(minHeight: 40) + } + } + .padding(25) + .frame(minHeight: g.size.height) + } + .onAppear { + if justOpened { + serverOperators = ChatModel.shared.conditions.serverOperators + selectedOperatorIds = Set(serverOperators.filter { $0.enabled }.map { $0.operatorId }) + justOpened = false + } + } + .sheet(item: $sheetItem) { item in + switch item { + case .showConditions: + SimpleConditionsView() + .modifier(ThemedBackground(grouped: true)) + case .configureOperators: + ChooseServerOperators(serverOperators: serverOperators, selectedOperatorIds: $selectedOperatorIds) + .modifier(ThemedBackground()) + } + } + .frame(maxHeight: .infinity, alignment: .top) + if #available(iOS 16.4, *) { + v.scrollBounceBehavior(.basedOnSize) + } else { + v + } + } + .frame(maxHeight: .infinity, alignment: .top) + .navigationBarHidden(true) // necessary on iOS 15 + } + + private func continueToNextStep() { + onboardingStageDefault.set(.step4_SetNotificationsMode) + notificationsModeNavLinkActive = true + } + + func notificationsModeNavLinkButton(_ button: @escaping (() -> some View)) -> some View { + ZStack { + button() + + NavigationLink(isActive: $notificationsModeNavLinkActive) { + notificationsModeDestinationView() + } label: { + EmptyView() + } + .frame(width: 1, height: 1) + .hidden() + } + } + + private func notificationsModeDestinationView() -> some View { + SetNotificationsMode() + .navigationBarBackButtonHidden(true) + .modifier(ThemedBackground()) + } + + private func acceptConditionsButton() -> some View { + notificationsModeNavLinkButton { + Button { + Task { + do { + let conditionsId = ChatModel.shared.conditions.currentConditions.conditionsId + let acceptForOperators = selectedOperators.filter { !$0.conditionsAcceptance.conditionsAccepted } + let operatorIds = acceptForOperators.map { $0.operatorId } + let r = try await acceptConditions(conditionsId: conditionsId, operatorIds: operatorIds) + await MainActor.run { + ChatModel.shared.conditions = r + } + if let enabledOperators = enabledOperators(r.serverOperators) { + let r2 = try await setServerOperators(operators: enabledOperators) + await MainActor.run { + ChatModel.shared.conditions = r2 + continueToNextStep() + } + } else { + await MainActor.run { + continueToNextStep() + } + } + } catch let error { + await MainActor.run { + showAlert( + NSLocalizedString("Error accepting conditions", comment: "alert title"), + message: responseError(error) + ) + } + } + } + } label: { + Text("Accept") + } + .buttonStyle(OnboardingButtonStyle(isDisabled: selectedOperatorIds.isEmpty)) + .disabled(selectedOperatorIds.isEmpty) + } + } + + private func enabledOperators(_ operators: [ServerOperator]) -> [ServerOperator]? { + var ops = operators + if !ops.isEmpty { + for i in 0.. + @State private var sheetItem: ChooseServerOperatorsSheet? = nil + + var body: some View { + GeometryReader { g in + ScrollView { + VStack(alignment: .leading, spacing: 20) { + Text("Server operators") + .font(.largeTitle) + .bold() + .frame(maxWidth: .infinity, alignment: .center) + .padding(.top, 25) + + infoText() + .frame(maxWidth: .infinity, alignment: .center) + + Spacer() + + ForEach(serverOperators) { srvOperator in + operatorCheckView(srvOperator) + } + VStack { + Text("SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app.").padding(.bottom, 8) + Text("You can configure servers via settings.") + } + .font(.footnote) + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity, alignment: .center) + .padding(.horizontal, 16) + + Spacer() + + VStack(spacing: 8) { + setOperatorsButton() + onboardingButtonPlaceholder() + } + } + .frame(minHeight: g.size.height) + } + .sheet(item: $sheetItem) { item in + switch item { + case .showInfo: + ChooseServerOperatorsInfoView() + } + } + .frame(maxHeight: .infinity, alignment: .top) + } + .frame(maxHeight: .infinity, alignment: .top) + .padding(25) + .interactiveDismissDisabled(selectedOperatorIds.isEmpty) + } + + private func infoText() -> some View { + Button { + sheetItem = .showInfo + } label: { + Label("How it helps privacy", systemImage: "info.circle") + .font(.headline) + } + } + + private func operatorCheckView(_ serverOperator: ServerOperator) -> some View { + let checked = selectedOperatorIds.contains(serverOperator.operatorId) + let icon = checked ? "checkmark.circle.fill" : "circle" + let iconColor = checked ? theme.colors.primary : Color(uiColor: .tertiaryLabel).asAnotherColorFromSecondary(theme) + return HStack(spacing: 10) { + Image(serverOperator.largeLogo(colorScheme)) + .resizable() + .scaledToFit() + .frame(height: 48) + Spacer() + Image(systemName: icon) + .resizable() + .scaledToFit() + .frame(width: 26, height: 26) + .foregroundColor(iconColor) + } + .background(theme.colors.background) + .padding() + .clipShape(RoundedRectangle(cornerRadius: 18)) + .overlay( + RoundedRectangle(cornerRadius: 18) + .stroke(Color(uiColor: .secondarySystemFill), lineWidth: 2) + ) + .padding(.horizontal, 2) + .onTapGesture { + if checked { + selectedOperatorIds.remove(serverOperator.operatorId) + } else { + selectedOperatorIds.insert(serverOperator.operatorId) + } + } + } + + private func setOperatorsButton() -> some View { + Button { + dismiss() + } label: { + Text("OK") + } + .buttonStyle(OnboardingButtonStyle(isDisabled: selectedOperatorIds.isEmpty)) + .disabled(selectedOperatorIds.isEmpty) + } +} + +let operatorsPostLink = URL(string: "https://simplex.chat/blog/20241125-servers-operated-by-flux-true-privacy-and-decentralization-for-all-users.html")! + +struct ChooseServerOperatorsInfoView: View { + @Environment(\.colorScheme) var colorScheme: ColorScheme + @EnvironmentObject var theme: AppTheme + + var body: some View { + NavigationView { + List { + VStack(alignment: .leading, spacing: 12) { + Text("The app protects your privacy by using different operators in each conversation.") + Text("When more than one operator is enabled, none of them has metadata to learn who communicates with whom.") + Text("For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server.") + } + .fixedSize(horizontal: false, vertical: true) + .listRowBackground(Color.clear) + .listRowSeparator(.hidden) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + .padding(.top) + + Section { + ForEach(ChatModel.shared.conditions.serverOperators) { op in + operatorInfoNavLinkView(op) + } + } header: { + Text("About operators") + .foregroundColor(theme.colors.secondary) + } + } + .navigationTitle("Server operators") + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } + } + + private func operatorInfoNavLinkView(_ op: ServerOperator) -> some View { + NavigationLink() { + OperatorInfoView(serverOperator: op) + .navigationBarTitle("Network operator") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.large) + } label: { + HStack { + Image(op.logo(colorScheme)) + .resizable() + .scaledToFit() + .frame(width: 24, height: 24) + Text(op.tradeName) + } + } + } +} + +#Preview { + OnboardingConditionsView() +} diff --git a/apps/ios/Shared/Views/Onboarding/CreateProfile.swift b/apps/ios/Shared/Views/Onboarding/CreateProfile.swift index 3f835e25d4..ae72cb1be5 100644 --- a/apps/ios/Shared/Views/Onboarding/CreateProfile.swift +++ b/apps/ios/Shared/Views/Onboarding/CreateProfile.swift @@ -27,6 +27,7 @@ enum UserProfileAlert: Identifiable { struct CreateProfile: View { @Environment(\.dismiss) var dismiss + @EnvironmentObject var theme: AppTheme @State private var displayName: String = "" @FocusState private var focusDisplayName @State private var alert: UserProfileAlert? @@ -37,7 +38,7 @@ struct CreateProfile: View { TextField("Enter your name…", text: $displayName) .focused($focusDisplayName) Button { - createProfile(displayName, showAlert: { alert = $0 }, dismiss: dismiss) + createProfile() } label: { Label("Create profile", systemImage: "checkmark") } @@ -45,6 +46,8 @@ struct CreateProfile: View { } header: { HStack { Text("Your profile") + .foregroundColor(theme.colors.secondary) + let name = displayName.trimmingCharacters(in: .whitespaces) let validName = mkValidName(name) if name != validName { @@ -59,152 +62,210 @@ struct CreateProfile: View { .frame(height: 20) } footer: { VStack(alignment: .leading, spacing: 8) { - Text("Your profile, contacts and delivered messages are stored on your device.") - Text("The profile is only shared with your contacts.") + Text("Your profile is stored on your device and only shared with your contacts.") } + .foregroundColor(theme.colors.secondary) .frame(maxWidth: .infinity, alignment: .leading) } } .navigationTitle("Create your profile") + .modifier(ThemedBackground(grouped: true)) .alert(item: $alert) { a in userProfileAlert(a, $displayName) } .onAppear() { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { focusDisplayName = true } } - .keyboardPadding() + } + + private func createProfile() { + hideKeyboard() + let profile = Profile( + displayName: displayName.trimmingCharacters(in: .whitespaces), + fullName: "" + ) + let m = ChatModel.shared + do { + AppChatState.shared.set(.active) + m.currentUser = try apiCreateActiveUser(profile) + // .isEmpty check is redundant here, but it makes it clearer what is going on + if m.users.isEmpty || m.users.allSatisfy({ $0.user.hidden }) { + try startChat() + withAnimation { + onboardingStageDefault.set(.step3_ChooseServerOperators) + m.onboardingStage = .step3_ChooseServerOperators + } + } else { + onboardingStageDefault.set(.onboardingComplete) + m.onboardingStage = .onboardingComplete + dismiss() + m.users = try listUsers() + try getUserChatData() + } + } catch let error { + showCreateProfileAlert(showAlert: { alert = $0 }, error) + } } } struct CreateFirstProfile: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @Environment(\.dismiss) var dismiss @State private var displayName: String = "" @FocusState private var focusDisplayName + @State private var nextStepNavLinkActive = false var body: some View { - VStack(alignment: .leading) { - Group { - Text("Create your profile") + let v = VStack(alignment: .leading, spacing: 16) { + VStack(alignment: .center, spacing: 16) { + Text("Create profile") .font(.largeTitle) .bold() - Text("Your profile, contacts and delivered messages are stored on your device.") - .foregroundColor(.secondary) - Text("The profile is only shared with your contacts.") - .foregroundColor(.secondary) - .padding(.bottom) - } - .padding(.bottom) + .multilineTextAlignment(.center) - ZStack(alignment: .topLeading) { + Text("Your profile is stored on your device and only shared with your contacts.") + .font(.callout) + .foregroundColor(theme.colors.secondary) + .multilineTextAlignment(.center) + } + .fixedSize(horizontal: false, vertical: true) + .frame(maxWidth: .infinity) // Ensures it takes up the full width + .padding(.horizontal, 10) + .onTapGesture { focusDisplayName = false } + + HStack { let name = displayName.trimmingCharacters(in: .whitespaces) let validName = mkValidName(name) - if name != validName { - Button { - showAlert(.invalidNameError(validName: validName)) - } label: { - Image(systemName: "exclamationmark.circle").foregroundColor(.red) + ZStack(alignment: .trailing) { + TextField("Enter your name…", text: $displayName) + .focused($focusDisplayName) + .padding(.horizontal) + .padding(.trailing, 20) + .padding(.vertical, 10) + .background( + RoundedRectangle(cornerRadius: 10, style: .continuous) + .fill(Color(uiColor: .tertiarySystemFill)) + ) + if name != validName { + Button { + showAlert(.invalidNameError(validName: validName)) + } label: { + Image(systemName: "exclamationmark.circle") + .foregroundColor(.red) + .padding(.horizontal, 10) + } } - } else { - Image(systemName: "exclamationmark.circle").foregroundColor(.clear) } - TextField("Enter your name…", text: $displayName) - .focused($focusDisplayName) - .padding(.leading, 32) } - .padding(.bottom) + .padding(.top) + Spacer() - onboardingButtons() + + VStack(spacing: 10) { + createProfileButton() + if !focusDisplayName { + onboardingButtonPlaceholder() + } + } } .onAppear() { - focusDisplayName = true - setLastVersionDefault() + if #available(iOS 16, *) { + focusDisplayName = true + } else { + // it does not work before animation completes on iOS 15 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + focusDisplayName = true + } + } } - .padding() + .padding(.horizontal, 25) + .padding(.bottom, 25) .frame(maxWidth: .infinity, alignment: .leading) - .keyboardPadding() + if #available(iOS 16, *) { + return v.padding(.top, 10) + } else { + return v.padding(.top, 75).ignoresSafeArea(.all, edges: .top) + } } - func onboardingButtons() -> some View { - HStack { + func createProfileButton() -> some View { + ZStack { Button { - hideKeyboard() - withAnimation { - m.onboardingStage = .step1_SimpleXInfo - } + createProfile() } label: { - HStack { - Image(systemName: "lessthan") - Text("About SimpleX") - } - } - - Spacer() - - Button { - createProfile(displayName, showAlert: showAlert, dismiss: dismiss) - } label: { - HStack { - Text("Create") - Image(systemName: "greaterthan") - } + Text("Create profile") } + .buttonStyle(OnboardingButtonStyle(isDisabled: !canCreateProfile(displayName))) .disabled(!canCreateProfile(displayName)) + + NavigationLink(isActive: $nextStepNavLinkActive) { + nextStepDestinationView() + } label: { + EmptyView() + } + .frame(width: 1, height: 1) + .hidden() } } private func showAlert(_ alert: UserProfileAlert) { AlertManager.shared.showAlert(userProfileAlert(alert, $displayName)) } + + private func nextStepDestinationView() -> some View { + OnboardingConditionsView() + .navigationBarBackButtonHidden(true) + .modifier(ThemedBackground()) + } + + private func createProfile() { + hideKeyboard() + let profile = Profile( + displayName: displayName.trimmingCharacters(in: .whitespaces), + fullName: "" + ) + let m = ChatModel.shared + do { + AppChatState.shared.set(.active) + m.currentUser = try apiCreateActiveUser(profile) + try startChat(onboarding: true) + onboardingStageDefault.set(.step3_ChooseServerOperators) + nextStepNavLinkActive = true + } catch let error { + showCreateProfileAlert(showAlert: showAlert, error) + } + } } -private func createProfile(_ displayName: String, showAlert: (UserProfileAlert) -> Void, dismiss: DismissAction) { - hideKeyboard() - let profile = Profile( - displayName: displayName.trimmingCharacters(in: .whitespaces), - fullName: "" - ) +private func showCreateProfileAlert( + showAlert: (UserProfileAlert) -> Void, + _ error: Error +) { let m = ChatModel.shared - do { - m.currentUser = try apiCreateActiveUser(profile) - if m.users.isEmpty { - try startChat() - withAnimation { - onboardingStageDefault.set(.step3_CreateSimpleXAddress) - m.onboardingStage = .step3_CreateSimpleXAddress - } + switch error as? ChatError { + case .errorStore(.duplicateName), + .error(.userExists): + if m.currentUser == nil { + AlertManager.shared.showAlert(duplicateUserAlert) } else { - onboardingStageDefault.set(.onboardingComplete) - m.onboardingStage = .onboardingComplete - dismiss() - m.users = try listUsers() - try getUserChatData() + showAlert(.duplicateUserError) } - } catch let error { - switch error as? ChatResponse { - case .chatCmdError(_, .errorStore(.duplicateName)), - .chatCmdError(_, .error(.userExists)): - if m.currentUser == nil { - AlertManager.shared.showAlert(duplicateUserAlert) - } else { - showAlert(.duplicateUserError) - } - case .chatCmdError(_, .error(.invalidDisplayName)): - if m.currentUser == nil { - AlertManager.shared.showAlert(invalidDisplayNameAlert) - } else { - showAlert(.invalidDisplayNameError) - } - default: - let err: LocalizedStringKey = "Error: \(responseError(error))" - if m.currentUser == nil { - AlertManager.shared.showAlert(creatUserErrorAlert(err)) - } else { - showAlert(.createUserError(error: err)) - } + case .error(.invalidDisplayName): + if m.currentUser == nil { + AlertManager.shared.showAlert(invalidDisplayNameAlert) + } else { + showAlert(.invalidDisplayNameError) + } + default: + let err: LocalizedStringKey = "Error: \(responseError(error))" + if m.currentUser == nil { + AlertManager.shared.showAlert(creatUserErrorAlert(err)) + } else { + showAlert(.createUserError(error: err)) } - logger.error("Failed to create user or start chat: \(responseError(error))") } + logger.error("Failed to create user or start chat: \(responseError(error))") } private func canCreateProfile(_ displayName: String) -> Bool { diff --git a/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift b/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift index befb34b318..a2f5db7f03 100644 --- a/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift +++ b/apps/ios/Shared/Views/Onboarding/CreateSimpleXAddress.swift @@ -31,7 +31,7 @@ struct CreateSimpleXAddress: View { Spacer() if let userAddress = m.userAddress { - SimpleXLinkQRCode(uri: userAddress.connReqContact) + SimpleXCreatedLinkQRCode(link: userAddress.connLinkContact, short: Binding.constant(false)) .frame(maxHeight: g.size.width) shareQRCodeButton(userAddress) .frame(maxWidth: .infinity) @@ -77,9 +77,9 @@ struct CreateSimpleXAddress: View { progressIndicator = true Task { do { - let connReqContact = try await apiCreateUserAddress() + let connLinkContact = try await apiCreateUserAddress(short: false) DispatchQueue.main.async { - m.userAddress = UserContactLink(connReqContact: connReqContact) + m.userAddress = UserContactLink(connLinkContact: connLinkContact) } await MainActor.run { progressIndicator = false } } catch let error { @@ -121,7 +121,7 @@ struct CreateSimpleXAddress: View { private func shareQRCodeButton(_ userAddress: UserContactLink) -> some View { Button { - showShareSheet(items: [simplexChatLink(userAddress.connReqContact)]) + showShareSheet(items: [simplexChatLink(userAddress.connLinkContact.simplexChatUri(short: false))]) } label: { Label("Share", systemImage: "square.and.arrow.up") } @@ -189,7 +189,7 @@ struct SendAddressMailView: View { let messageBody = String(format: NSLocalizedString("""

Hi!

Connect to me via SimpleX Chat

- """, comment: "email text"), simplexChatLink(userAddress.connReqContact)) + """, comment: "email text"), simplexChatLink(userAddress.connLinkContact.simplexChatUri(short: false))) MailView( isShowing: self.$showMailView, result: $mailViewResult, diff --git a/apps/ios/Shared/Views/Onboarding/HowItWorks.swift b/apps/ios/Shared/Views/Onboarding/HowItWorks.swift index fdd73d2632..7452d74e91 100644 --- a/apps/ios/Shared/Views/Onboarding/HowItWorks.swift +++ b/apps/ios/Shared/Views/Onboarding/HowItWorks.swift @@ -9,24 +9,24 @@ import SwiftUI struct HowItWorks: View { + @Environment(\.dismiss) var dismiss: DismissAction @EnvironmentObject var m: ChatModel var onboarding: Bool + @Binding var createProfileNavLinkActive: Bool var body: some View { VStack(alignment: .leading) { Text("How SimpleX works") .font(.largeTitle) + .bold() .padding(.vertical) ScrollView { VStack(alignment: .leading) { Group { - Text("Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*") - Text("To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts.") - Text("You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them.") - Text("Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.") - if onboarding { - Text("Read more in our GitHub repository.") - } else { + Text("To protect your privacy, SimpleX uses separate IDs for each of your contacts.") + Text("Only client devices store user profiles, contacts, groups, and messages.") + Text("All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages.") + if !onboarding { Text("Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme).") } } @@ -37,18 +37,34 @@ struct HowItWorks: View { Spacer() if onboarding { - OnboardingActionButton() - .padding(.bottom, 8) + VStack(spacing: 10) { + createFirstProfileButton() + onboardingButtonPlaceholder() + } } } .lineLimit(10) - .padding() + .padding(onboarding ? 25 : 16) .frame(maxHeight: .infinity, alignment: .top) + .modifier(ThemedBackground()) + } + + private func createFirstProfileButton() -> some View { + Button { + dismiss() + createProfileNavLinkActive = true + } label: { + Text("Create your profile") + } + .buttonStyle(OnboardingButtonStyle(isDisabled: false)) } } struct HowItWorks_Previews: PreviewProvider { static var previews: some View { - HowItWorks(onboarding: true) + HowItWorks( + onboarding: true, + createProfileNavLinkActive: Binding.constant(false) + ) } } diff --git a/apps/ios/Shared/Views/Onboarding/OnboardingView.swift b/apps/ios/Shared/Views/Onboarding/OnboardingView.swift index 438491b5f1..8f448dc508 100644 --- a/apps/ios/Shared/Views/Onboarding/OnboardingView.swift +++ b/apps/ios/Shared/Views/Onboarding/OnboardingView.swift @@ -12,20 +12,39 @@ struct OnboardingView: View { var onboarding: OnboardingStage var body: some View { - switch onboarding { - case .step1_SimpleXInfo: SimpleXInfo(onboarding: true) - case .step2_CreateProfile: CreateFirstProfile() - case .step3_CreateSimpleXAddress: CreateSimpleXAddress() - case .step4_SetNotificationsMode: SetNotificationsMode() - case .onboardingComplete: EmptyView() + NavigationView { + switch onboarding { + case .step1_SimpleXInfo: + SimpleXInfo(onboarding: true) + .modifier(ThemedBackground()) + case .step2_CreateProfile: // deprecated + CreateFirstProfile() + .modifier(ThemedBackground()) + case .step3_CreateSimpleXAddress: // deprecated + CreateSimpleXAddress() + case .step3_ChooseServerOperators: + OnboardingConditionsView() + .navigationBarBackButtonHidden(true) + .modifier(ThemedBackground()) + case .step4_SetNotificationsMode: + SetNotificationsMode() + .navigationBarBackButtonHidden(true) + .modifier(ThemedBackground()) + case .onboardingComplete: EmptyView() + } } } } +func onboardingButtonPlaceholder() -> some View { + Spacer().frame(height: 40) +} + enum OnboardingStage: String, Identifiable { case step1_SimpleXInfo - case step2_CreateProfile - case step3_CreateSimpleXAddress + case step2_CreateProfile // deprecated + case step3_CreateSimpleXAddress // deprecated + case step3_ChooseServerOperators // changed to simplified conditions case step4_SetNotificationsMode case onboardingComplete diff --git a/apps/ios/Shared/Views/Onboarding/SetNotificationsMode.swift b/apps/ios/Shared/Views/Onboarding/SetNotificationsMode.swift index 3bbd7a5c94..31865e7af9 100644 --- a/apps/ios/Shared/Views/Onboarding/SetNotificationsMode.swift +++ b/apps/ios/Shared/Views/Onboarding/SetNotificationsMode.swift @@ -13,43 +13,61 @@ struct SetNotificationsMode: View { @EnvironmentObject var m: ChatModel @State private var notificationMode = NotificationsMode.instant @State private var showAlert: NotificationAlert? + @State private var showInfo: Bool = false var body: some View { - ScrollView { - VStack(alignment: .leading, spacing: 16) { - Text("Push notifications") - .font(.largeTitle) - .bold() - .frame(maxWidth: .infinity) + GeometryReader { g in + let v = ScrollView { + VStack(alignment: .center, spacing: 20) { + Text("Push notifications") + .font(.largeTitle) + .bold() + .padding(.top, 25) + + infoText() + + Spacer() - Text("Send notifications:") - ForEach(NotificationsMode.values) { mode in - NtfModeSelector(mode: mode, selection: $notificationMode) - } - - Spacer() - - Button { - if let token = m.deviceToken { - setNotificationsMode(token, notificationMode) - } else { - AlertManager.shared.showAlertMsg(title: "No device token!") + ForEach(NotificationsMode.values) { mode in + NtfModeSelector(mode: mode, selection: $notificationMode) } - onboardingStageDefault.set(.onboardingComplete) - m.onboardingStage = .onboardingComplete - } label: { - if case .off = notificationMode { - Text("Use chat") - } else { - Text("Enable notifications") + + Spacer() + + VStack(spacing: 10) { + Button { + if let token = m.deviceToken { + setNotificationsMode(token, notificationMode) + } else { + AlertManager.shared.showAlertMsg(title: "No device token!") + } + onboardingStageDefault.set(.onboardingComplete) + m.onboardingStage = .onboardingComplete + } label: { + if case .off = notificationMode { + Text("Use chat") + } else { + Text("Enable notifications") + } + } + .buttonStyle(OnboardingButtonStyle()) + onboardingButtonPlaceholder() } } - .font(.title) - .frame(maxWidth: .infinity) + .padding(25) + .frame(minHeight: g.size.height) + } + if #available(iOS 16.4, *) { + v.scrollBounceBehavior(.basedOnSize) + } else { + v } - .padding() - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) } + .frame(maxHeight: .infinity) + .sheet(isPresented: $showInfo) { + NotificationsInfoView() + } + .navigationBarHidden(true) // necessary on iOS 15 } private func setNotificationsMode(_ token: DeviceToken, _ mode: NotificationsMode) { @@ -75,31 +93,51 @@ struct SetNotificationsMode: View { } } } + + private func infoText() -> some View { + Button { + showInfo = true + } label: { + Label("How it affects privacy", systemImage: "info.circle") + .font(.headline) + } + } } struct NtfModeSelector: View { + @EnvironmentObject var theme: AppTheme var mode: NotificationsMode @Binding var selection: NotificationsMode @State private var tapped = false var body: some View { ZStack { - VStack(alignment: .leading, spacing: 4) { - Text(mode.label) - .font(.headline) - .foregroundColor(selection == mode ? .accentColor : .secondary) - Text(ntfModeDescription(mode)) - .lineLimit(10) - .font(.subheadline) + HStack(spacing: 16) { + Image(systemName: mode.icon) + .resizable() + .scaledToFill() + .frame(width: mode.icon == "bolt" ? 14 : 18, height: 18) + .foregroundColor(selection == mode ? theme.colors.primary : theme.colors.secondary) + VStack(alignment: .leading, spacing: 4) { + Text(mode.label) + .font(.headline) + .foregroundColor(selection == mode ? theme.colors.primary : theme.colors.secondary) + Text(ntfModeShortDescription(mode)) + .lineLimit(2) + .font(.callout) + .fixedSize(horizontal: false, vertical: true) + } } - .padding(12) + .padding(.vertical, 12) + .padding(.trailing, 12) + .padding(.leading, 16) } .frame(maxWidth: .infinity, alignment: .leading) - .background(Color(uiColor: tapped ? .secondarySystemFill : .systemBackground)) + .background(tapped ? Color(uiColor: .secondarySystemFill) : theme.colors.background) .clipShape(RoundedRectangle(cornerRadius: 18)) .overlay( RoundedRectangle(cornerRadius: 18) - .stroke(selection == mode ? Color.accentColor : Color(uiColor: .secondarySystemFill), lineWidth: 2) + .stroke(selection == mode ? theme.colors.primary : Color(uiColor: .secondarySystemFill), lineWidth: 2) ) ._onButtonGesture { down in tapped = down @@ -108,6 +146,37 @@ struct NtfModeSelector: View { } } +struct NotificationsInfoView: View { + var body: some View { + VStack(alignment: .leading) { + Text("Notifications privacy") + .font(.largeTitle) + .bold() + .padding(.vertical) + ScrollView { + VStack(alignment: .leading) { + Group { + ForEach(NotificationsMode.values) { mode in + VStack(alignment: .leading, spacing: 4) { + (Text(Image(systemName: mode.icon)) + textSpace + Text(mode.label)) + .font(.headline) + .foregroundColor(.secondary) + Text(ntfModeDescription(mode)) + .lineLimit(10) + .font(.callout) + } + } + } + .padding(.bottom) + } + } + } + .padding() + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) + .modifier(ThemedBackground()) + } +} + struct NotificationsModeView_Previews: PreviewProvider { static var previews: some View { SetNotificationsMode() diff --git a/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift b/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift index 94e281be7d..9f41a37b1d 100644 --- a/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift +++ b/apps/ios/Shared/Views/Onboarding/SimpleXInfo.swift @@ -13,59 +13,62 @@ struct SimpleXInfo: View { @EnvironmentObject var m: ChatModel @Environment(\.colorScheme) var colorScheme: ColorScheme @State private var showHowItWorks = false + @State private var createProfileNavLinkActive = false var onboarding: Bool var body: some View { GeometryReader { g in - ScrollView { + let v = ScrollView { VStack(alignment: .leading) { - Image(colorScheme == .light ? "logo" : "logo-light") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: g.size.width * 0.67) - .padding(.bottom, 8) - .frame(maxWidth: .infinity, minHeight: 48, alignment: .top) - - VStack(alignment: .leading) { - Text("The next generation of private messaging") - .font(.title2) - .padding(.bottom, 30) - .padding(.horizontal, 40) - .frame(maxWidth: .infinity) - .multilineTextAlignment(.center) - infoRow("privacy", "Privacy redefined", - "The 1st platform without any user identifiers – private by design.", width: 48) - infoRow("shield", "Immune to spam and abuse", - "People can connect to you only via the links you share.", width: 46) - infoRow(colorScheme == .light ? "decentralized" : "decentralized-light", "Decentralized", - "Open-source protocol and code – anybody can run the servers.", width: 44) + VStack(alignment: .center, spacing: 10) { + Image(colorScheme == .light ? "logo" : "logo-light") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: g.size.width * 0.67) + .padding(.bottom, 8) + .padding(.leading, 4) + .frame(maxWidth: .infinity, minHeight: 48, alignment: .top) + + Button { + showHowItWorks = true + } label: { + Label("The future of messaging", systemImage: "info.circle") + .font(.headline) + } } Spacer() + + VStack(alignment: .leading) { + onboardingInfoRow("privacy", "Privacy redefined", + "No user identifiers.", width: 48) + onboardingInfoRow("shield", "Immune to spam", + "You decide who can connect.", width: 46) + onboardingInfoRow(colorScheme == .light ? "decentralized" : "decentralized-light", "Decentralized", + "Anybody can host servers.", width: 46) + } + .padding(.leading, 16) + + Spacer() + if onboarding { - OnboardingActionButton() - Spacer() + VStack(spacing: 10) { + createFirstProfileButton() - Button { - m.migrationState = .pasteOrScanLink - } label: { - Label("Migrate from another device", systemImage: "tray.and.arrow.down") - .font(.subheadline) + Button { + m.migrationState = .pasteOrScanLink + } label: { + Label("Migrate from another device", systemImage: "tray.and.arrow.down") + .font(.system(size: 17, weight: .semibold)) + .frame(minHeight: 40) + } + .frame(maxWidth: .infinity) } - .padding(.bottom, 8) - .frame(maxWidth: .infinity) } - - Button { - showHowItWorks = true - } label: { - Label("How it works", systemImage: "info.circle") - .font(.subheadline) - } - .padding(.bottom, 8) - .frame(maxWidth: .infinity) - } + .padding(.horizontal, 25) + .padding(.top, 75) + .padding(.bottom, 25) .frame(minHeight: g.size.height) } .sheet(isPresented: Binding( @@ -79,80 +82,72 @@ struct SimpleXInfo: View { MigrateToDevice(migrationState: $m.migrationState) } .navigationTitle("Migrate here") - .background(colorScheme == .light ? Color(uiColor: .tertiarySystemGroupedBackground) : .clear) + .modifier(ThemedBackground(grouped: true)) } } .sheet(isPresented: $showHowItWorks) { - HowItWorks(onboarding: onboarding) + HowItWorks( + onboarding: onboarding, + createProfileNavLinkActive: $createProfileNavLinkActive + ) + } + if #available(iOS 16.4, *) { + v.scrollBounceBehavior(.basedOnSize) + } else { + v } } + .onAppear() { + setLastVersionDefault() + } .frame(maxHeight: .infinity) - .padding() + .navigationBarHidden(true) // necessary on iOS 15 } - private func infoRow(_ image: String, _ title: LocalizedStringKey, _ text: LocalizedStringKey, width: CGFloat) -> some View { + private func onboardingInfoRow(_ image: String, _ title: LocalizedStringKey, _ text: LocalizedStringKey, width: CGFloat) -> some View { HStack(alignment: .top) { Image(image) .resizable() .scaledToFit() .frame(width: width, height: 54) .frame(width: 54) - .padding(.top, 4) - .padding(.leading, 4) .padding(.trailing, 10) VStack(alignment: .leading, spacing: 4) { Text(title).font(.headline) Text(text).frame(minHeight: 40, alignment: .top) + .font(.callout) + .lineLimit(3) + .fixedSize(horizontal: false, vertical: true) } + .padding(.top, 4) + } + .padding(.bottom, 12) + } + + private func createFirstProfileButton() -> some View { + ZStack { + Button { + createProfileNavLinkActive = true + } label: { + Text("Create your profile") + } + .buttonStyle(OnboardingButtonStyle(isDisabled: false)) + + NavigationLink(isActive: $createProfileNavLinkActive) { + CreateFirstProfile() + .modifier(ThemedBackground()) + } label: { + EmptyView() + } + .frame(width: 1, height: 1) + .hidden() } - .padding(.bottom, 20) - .padding(.trailing, 6) } } -struct OnboardingActionButton: View { - @EnvironmentObject var m: ChatModel - @Environment(\.colorScheme) var colorScheme +let textSpace = Text(verbatim: " ") - var body: some View { - if m.currentUser == nil { - actionButton("Create your profile", onboarding: .step2_CreateProfile) - } else { - actionButton("Make a private connection", onboarding: .onboardingComplete) - } - } - - private func actionButton(_ label: LocalizedStringKey, onboarding: OnboardingStage) -> some View { - Button { - withAnimation { - onboardingStageDefault.set(onboarding) - m.onboardingStage = onboarding - } - } label: { - HStack { - Text(label).font(.title2) - Image(systemName: "greaterthan") - } - } - .frame(maxWidth: .infinity) - .padding(.bottom) - } - - private func actionButton(_ label: LocalizedStringKey, action: @escaping () -> Void) -> some View { - Button { - withAnimation { - action() - } - } label: { - HStack { - Text(label).font(.title2) - Image(systemName: "greaterthan") - } - } - .frame(maxWidth: .infinity) - .padding(.bottom) - } -} +let textNewLine = Text(verbatim: "\n") struct SimpleXInfo_Previews: PreviewProvider { static var previews: some View { diff --git a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift index 73ea5720c6..f65a21623a 100644 --- a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift +++ b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift @@ -7,17 +7,37 @@ // import SwiftUI +import SimpleXChat private struct VersionDescription { var version: String var post: URL? - var features: [FeatureDescription] + var features: [Feature] } -private struct FeatureDescription { - var icon: String - var title: LocalizedStringKey - var description: LocalizedStringKey +private enum Feature: Identifiable { + case feature(Description) + case view(FeatureView) + + var id: LocalizedStringKey { + switch self { + case let .feature(d): d.title + case let .view(v): v.title + } + } +} + +private struct Description { + let icon: String? + let title: LocalizedStringKey + let description: LocalizedStringKey? + var subfeatures: [(icon: String, description: LocalizedStringKey)] = [] +} + +private struct FeatureView { + let icon: String? + let title: LocalizedStringKey + let view: () -> any View } private let versionDescriptions: [VersionDescription] = [ @@ -25,171 +45,171 @@ private let versionDescriptions: [VersionDescription] = [ version: "v4.2", post: URL(string: "https://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html"), features: [ - FeatureDescription( + .feature(Description( icon: "checkmark.shield", title: "Security assessment", description: "SimpleX Chat security was audited by Trail of Bits." - ), - FeatureDescription( + )), + .feature(Description( icon: "person.2", title: "Group links", description: "Admins can create the links to join groups." - ), - FeatureDescription( + )), + .feature(Description( icon: "checkmark", title: "Auto-accept contact requests", description: "With optional welcome message." - ), + )), ] ), VersionDescription( version: "v4.3", post: URL(string: "https://simplex.chat/blog/20221206-simplex-chat-v4.3-voice-messages.html"), features: [ - FeatureDescription( + .feature(Description( icon: "mic", title: "Voice messages", description: "Max 30 seconds, received instantly." - ), - FeatureDescription( + )), + .feature(Description( icon: "trash.slash", title: "Irreversible message deletion", description: "Your contacts can allow full message deletion." - ), - FeatureDescription( + )), + .feature(Description( icon: "externaldrive.connected.to.line.below", title: "Improved server configuration", description: "Add servers by scanning QR codes." - ), - FeatureDescription( + )), + .feature(Description( icon: "eye.slash", title: "Improved privacy and security", description: "Hide app screen in the recent apps." - ), + )), ] ), VersionDescription( version: "v4.4", post: URL(string: "https://simplex.chat/blog/20230103-simplex-chat-v4.4-disappearing-messages.html"), features: [ - FeatureDescription( + .feature(Description( icon: "stopwatch", title: "Disappearing messages", description: "Sent messages will be deleted after set time." - ), - FeatureDescription( + )), + .feature(Description( icon: "ellipsis.circle", title: "Live messages", description: "Recipients see updates as you type them." - ), - FeatureDescription( + )), + .feature(Description( icon: "checkmark.shield", title: "Verify connection security", description: "Compare security codes with your contacts." - ), - FeatureDescription( + )), + .feature(Description( icon: "camera", title: "GIFs and stickers", description: "Send them from gallery or custom keyboards." - ), - FeatureDescription( + )), + .feature(Description( icon: "character", title: "French interface", description: "Thanks to the users – contribute via Weblate!" - ) + )), ] ), VersionDescription( version: "v4.5", post: URL(string: "https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html"), features: [ - FeatureDescription( + .feature(Description( icon: "person.crop.rectangle.stack", title: "Multiple chat profiles", description: "Different names, avatars and transport isolation." - ), - FeatureDescription( + )), + .feature(Description( icon: "rectangle.and.pencil.and.ellipsis", title: "Message draft", description: "Preserve the last message draft, with attachments." - ), - FeatureDescription( + )), + .feature(Description( icon: "network.badge.shield.half.filled", title: "Transport isolation", description: "By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." - ), - FeatureDescription( + )), + .feature(Description( icon: "lock.doc", title: "Private filenames", description: "To protect timezone, image/voice files use UTC." - ), - FeatureDescription( + )), + .feature(Description( icon: "battery.25", title: "Reduced battery usage", description: "More improvements are coming soon!" - ), - FeatureDescription( + )), + .feature(Description( icon: "character", title: "Italian interface", description: "Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" - ) + )), ] ), VersionDescription( version: "v4.6", post: URL(string: "https://simplex.chat/blog/20230328-simplex-chat-v4-6-hidden-profiles.html"), features: [ - FeatureDescription( + .feature(Description( icon: "lock", title: "Hidden chat profiles", description: "Protect your chat profiles with a password!" - ), - FeatureDescription( + )), + .feature(Description( icon: "phone.arrow.up.right", title: "Audio and video calls", description: "Fully re-implemented - work in background!" - ), - FeatureDescription( + )), + .feature(Description( icon: "flag", title: "Group moderation", description: "Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" - ), - FeatureDescription( + )), + .feature(Description( icon: "plus.message", title: "Group welcome message", description: "Set the message shown to new members!" - ), - FeatureDescription( + )), + .feature(Description( icon: "battery.50", title: "Further reduced battery usage", description: "More improvements are coming soon!" - ), - FeatureDescription( + )), + .feature(Description( icon: "character", title: "Chinese and Spanish interface", description: "Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" - ), + )), ] ), VersionDescription( version: "v5.0", post: URL(string: "https://simplex.chat/blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.html"), features: [ - FeatureDescription( + .feature(Description( icon: "arrow.up.doc", title: "Videos and files up to 1gb", description: "Fast and no wait until the sender is online!" - ), - FeatureDescription( + )), + .feature(Description( icon: "lock", title: "App passcode", description: "Set it instead of system authentication." - ), - FeatureDescription( + )), + .feature(Description( icon: "character", title: "Polish interface", description: "Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" - ), + )), ] ), // Also @@ -199,183 +219,366 @@ private let versionDescriptions: [VersionDescription] = [ version: "v5.1", post: URL(string: "https://simplex.chat/blog/20230523-simplex-chat-v5-1-message-reactions-self-destruct-passcode.html"), features: [ - FeatureDescription( + .feature(Description( icon: "face.smiling", title: "Message reactions", description: "Finally, we have them! 🚀" - ), - FeatureDescription( + )), + .feature(Description( icon: "arrow.up.message", title: "Better messages", description: "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." - ), - FeatureDescription( + )), + .feature(Description( icon: "lock", title: "Self-destruct passcode", description: "All data is erased when it is entered." - ), - FeatureDescription( + )), + .feature(Description( icon: "character", title: "Japanese interface", description: "Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" - ), + )), ] ), VersionDescription( version: "v5.2", post: URL(string: "https://simplex.chat/blog/20230722-simplex-chat-v5-2-message-delivery-receipts.html"), features: [ - FeatureDescription( + .feature(Description( icon: "checkmark", title: "Message delivery receipts!", description: "The second tick we missed! ✅" - ), - FeatureDescription( + )), + .feature(Description( icon: "star", title: "Find chats faster", description: "Filter unread and favorite chats." - ), - FeatureDescription( + )), + .feature(Description( icon: "exclamationmark.arrow.triangle.2.circlepath", title: "Keep your connections", description: "Fix encryption after restoring backups." - ), - FeatureDescription( + )), + .feature(Description( icon: "stopwatch", title: "Make one message disappear", description: "Even when disabled in the conversation." - ), - FeatureDescription( + )), + .feature(Description( icon: "gift", title: "A few more things", description: "- more stable message delivery.\n- a bit better groups.\n- and more!" - ), + )), ] ), VersionDescription( version: "v5.3", post: URL(string: "https://simplex.chat/blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.html"), features: [ - FeatureDescription( + .feature(Description( icon: "desktopcomputer", title: "New desktop app!", description: "Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" - ), - FeatureDescription( + )), + .feature(Description( icon: "lock", title: "Encrypt stored files & media", description: "App encrypts new local files (except videos)." - ), - FeatureDescription( + )), + .feature(Description( icon: "magnifyingglass", title: "Discover and join groups", description: "- connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)!\n- delivery receipts (up to 20 members).\n- faster and more stable." - ), - FeatureDescription( + )), + .feature(Description( icon: "theatermasks", title: "Simplified incognito mode", description: "Toggle incognito when connecting." - ), - FeatureDescription( + )), + .feature(Description( icon: "character", title: "\(4) new interface languages", description: "Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" - ), + )), ] ), VersionDescription( version: "v5.4", post: URL(string: "https://simplex.chat/blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.html"), features: [ - FeatureDescription( + .feature(Description( icon: "desktopcomputer", title: "Link mobile and desktop apps! 🔗", description: "Via secure quantum resistant protocol." - ), - FeatureDescription( + )), + .feature(Description( icon: "person.2", title: "Better groups", description: "Faster joining and more reliable messages." - ), - FeatureDescription( + )), + .feature(Description( icon: "theatermasks", title: "Incognito groups", description: "Create a group using a random profile." - ), - FeatureDescription( + )), + .feature(Description( icon: "hand.raised", title: "Block group members", description: "To hide unwanted messages." - ), - FeatureDescription( + )), + .feature(Description( icon: "gift", title: "A few more things", description: "- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" - ), + )), ] ), VersionDescription( version: "v5.5", post: URL(string: "https://simplex.chat/blog/20240124-simplex-chat-infrastructure-costs-v5-5-simplex-ux-private-notes-group-history.html"), features: [ - FeatureDescription( + .feature(Description( icon: "folder", title: "Private notes", description: "With encrypted files and media." - ), - FeatureDescription( + )), + .feature(Description( icon: "link", title: "Paste link to connect!", description: "Search bar accepts invitation links." - ), - FeatureDescription( + )), + .feature(Description( icon: "bubble.left.and.bubble.right", title: "Join group conversations", description: "Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)." - ), - FeatureDescription( + )), + .feature(Description( icon: "battery.50", title: "Improved message delivery", description: "With reduced battery usage." - ), - FeatureDescription( + )), + .feature(Description( icon: "character", title: "Turkish interface", description: "Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" - ), + )), ] ), VersionDescription( version: "v5.6", post: URL(string: "https://simplex.chat/blog/20240323-simplex-network-privacy-non-profit-v5-6-quantum-resistant-e2e-encryption-simple-migration.html"), features: [ - FeatureDescription( + .feature(Description( icon: "key", title: "Quantum resistant encryption", description: "Enable in direct chats (BETA)!" - ), - FeatureDescription( + )), + .feature(Description( icon: "tray.and.arrow.up", title: "App data migration", description: "Migrate to another device via QR code." - ), - FeatureDescription( + )), + .feature(Description( icon: "phone", title: "Picture-in-picture calls", description: "Use the app while in the call." - ), - FeatureDescription( + )), + .feature(Description( icon: "hand.raised", title: "Safer groups", description: "Admins can block a member for all." - ), - FeatureDescription( + )), + .feature(Description( icon: "character", title: "Hungarian interface", description: "Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" - ), + )), ] - ) + ), + VersionDescription( + version: "v5.7", + post: URL(string: "https://simplex.chat/blog/20240426-simplex-legally-binding-transparency-v5-7-better-user-experience.html"), + features: [ + .feature(Description( + icon: "key", + title: "Quantum resistant encryption", + description: "Will be enabled in direct chats!" + )), + .feature(Description( + icon: "arrowshape.turn.up.forward", + title: "Forward and save messages", + description: "Message source remains private." + )), + .feature(Description( + icon: "music.note", + title: "In-call sounds", + description: "When connecting audio and video calls." + )), + .feature(Description( + icon: "person.crop.square", + title: "Shape profile images", + description: "Square, circle, or anything in between." + )), + .feature(Description( + icon: "antenna.radiowaves.left.and.right", + title: "Network management", + description: "More reliable network connection." + )), + ] + ), + VersionDescription( + version: "v5.8", + post: URL(string: "https://simplex.chat/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.html"), + features: [ + .feature(Description( + icon: "arrow.forward", + title: "Private message routing 🚀", + description: "Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." + )), + .feature(Description( + icon: "network.badge.shield.half.filled", + title: "Safely receive files", + description: "Confirm files from unknown servers." + )), + .feature(Description( + icon: "battery.50", + title: "Improved message delivery", + description: "With reduced battery usage." + )), + ] + ), + VersionDescription( + version: "v6.0", + post: URL(string: "https://simplex.chat/blog/20240814-simplex-chat-vision-funding-v6-private-routing-new-user-experience.html"), + features: [ + .feature(Description( + icon: nil, + title: "New chat experience 🎉", + description: nil, + subfeatures: [ + ("link.badge.plus", "Connect to your friends faster."), + ("archivebox", "Archive contacts to chat later."), + ("trash", "Delete up to 20 messages at once."), + ("platter.filled.bottom.and.arrow.down.iphone", "Use the app with one hand."), + ("paintpalette", "Color chats with the new themes."), + ] + )), + .feature(Description( + icon: nil, + title: "New media options", + description: nil, + subfeatures: [ + ("square.and.arrow.up", "Share from other apps."), + ("play.circle", "Play from the chat list."), + ("circle.filled.pattern.diagonalline.rectangle", "Blur for better privacy.") + ] + )), + .feature(Description( + icon: "arrow.forward", + title: "Private message routing 🚀", + description: "It protects your IP address and connections." + )), + .feature(Description( + icon: "network", + title: "Better networking", + description: "Connection and servers status." + )), + ] + ), + VersionDescription( + version: "v6.1", + post: URL(string: "https://simplex.chat/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.html"), + features: [ + .feature(Description( + icon: "checkmark.shield", + title: "Better security ✅", + description: "SimpleX protocols reviewed by Trail of Bits." + )), + .feature(Description( + icon: "video", + title: "Better calls", + description: "Switch audio and video during the call." + )), + .feature(Description( + icon: "bolt", + title: "Better notifications", + description: "Improved delivery, reduced traffic usage.\nMore improvements are coming soon!" + )), + .feature(Description( + icon: nil, + title: "Better user experience", + description: nil, + subfeatures: [ + ("link", "Switch chat profile for 1-time invitations."), + ("message", "Customizable message shape."), + ("calendar", "Better message dates."), + ("arrowshape.turn.up.right", "Forward up to 20 messages at once."), + ("flag", "Delete or moderate up to 200 messages.") + ] + )), + ] + ), + VersionDescription( + version: "v6.2", + post: URL(string: "https://simplex.chat/blog/20241210-simplex-network-v6-2-servers-by-flux-business-chats.html"), + features: [ + .view(FeatureView( + icon: nil, + title: "Network decentralization", + view: { NewOperatorsView() } + )), + .feature(Description( + icon: "briefcase", + title: "Business chats", + description: "Privacy for your customers." + )), + .feature(Description( + icon: "bolt", + title: "More reliable notifications", + description: "Delivered even when Apple drops them." + )), + ] + ), + VersionDescription( + version: "v6.3", + post: URL(string: "https://simplex.chat/blog/20250308-simplex-chat-v6-3-new-user-experience-safety-in-public-groups.html"), + features: [ + .feature(Description( + icon: "at", + title: "Mention members 👋", + description: "Get notified when mentioned." + )), + .feature(Description( + icon: "flag", + title: "Send private reports", + description: "Help admins moderating their groups." + )), + .feature(Description( + icon: "list.bullet", + title: "Organize chats into lists", + description: "Don't miss important messages." + )), + .feature(Description( + icon: nil, + title: "Better privacy and security", + description: nil, + subfeatures: [ + ("eye.slash", "Private media file names."), + ("trash", "Set message expiration in chats.") + ] + )), + .feature(Description( + icon: nil, + title: "Better groups performance", + description: nil, + subfeatures: [ + ("bolt", "Faster sending messages."), + ("person.2.slash", "Faster deletion of groups.") + ] + )), + ] + ), ] private let lastVersion = versionDescriptions.last!.version @@ -390,45 +593,100 @@ func shouldShowWhatsNew() -> Bool { return v != lastVersion } +fileprivate struct NewOperatorsView: View { + var body: some View { + VStack(alignment: .leading) { + Image((operatorsInfo[.flux] ?? ServerOperator.dummyOperatorInfo).largeLogo) + .resizable() + .scaledToFit() + .frame(height: 48) + Text("The second preset operator in the app!") + .multilineTextAlignment(.leading) + .lineLimit(10) + HStack { + Text("Enable Flux in Network & servers settings for better metadata privacy.") + } + } + } +} + +private enum WhatsNewViewSheet: Identifiable { + case showConditions + + var id: String { + switch self { + case .showConditions: return "showConditions" + } + } +} + struct WhatsNewView: View { @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var theme: AppTheme @State var currentVersion = versionDescriptions.count - 1 @State var currentVersionNav = versionDescriptions.count - 1 var viaSettings = false + var updatedConditions: Bool + @State private var sheetItem: WhatsNewViewSheet? = nil var body: some View { + whatsNewView() + .sheet(item: $sheetItem) { item in + switch item { + case .showConditions: + UsageConditionsView( + currUserServers: Binding.constant([]), + userServers: Binding.constant([]) + ) + .modifier(ThemedBackground(grouped: true)) + } + } + } + + private func whatsNewView() -> some View { VStack { TabView(selection: $currentVersion) { ForEach(Array(versionDescriptions.enumerated()), id: \.0) { (i, v) in - VStack(alignment: .leading, spacing: 16) { - Text("New in \(v.version)") - .font(.title) - .foregroundColor(.secondary) - .frame(maxWidth: .infinity) - .padding(.vertical) - ForEach(v.features, id: \.icon) { f in - featureDescription(f.icon, f.title, f.description) - .padding(.bottom, 8) - } - if let post = v.post { - Link(destination: post) { - HStack { - Text("Read more") - Image(systemName: "arrow.up.right.circle") + ScrollView { + VStack(alignment: .leading, spacing: 16) { + Text("New in \(v.version)") + .font(.title) + .foregroundColor(theme.colors.secondary) + .frame(maxWidth: .infinity) + .padding(.vertical) + ForEach(v.features) { f in + switch f { + case let .feature(d): featureDescription(d).padding(.bottom, 8) + case let .view(v): AnyView(v.view()).padding(.bottom, 8) } } - } - if !viaSettings { - Spacer() - Button("Ok") { - dismiss() + if let post = v.post { + Link(destination: post) { + HStack { + Text("Read more") + Image(systemName: "arrow.up.right.circle") + } + } + } + if updatedConditions { + Button("View updated conditions") { + sheetItem = .showConditions + } + } + if !viaSettings { + Spacer() + + Button("Ok") { + dismiss() + } + .font(.title3) + .frame(maxWidth: .infinity, alignment: .center) + + Spacer() } - .font(.title3) - .frame(maxWidth: .infinity, alignment: .center) - Spacer() } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) } - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .tag(i) } } @@ -441,19 +699,42 @@ struct WhatsNewView: View { currentVersionNav = currentVersion } } - - private func featureDescription(_ icon: String, _ title: LocalizedStringKey, _ description: LocalizedStringKey) -> some View { - VStack(alignment: .leading, spacing: 4) { + + @ViewBuilder private func featureHeader(_ icon: String?, _ title: LocalizedStringKey) -> some View { + if let icon { HStack(alignment: .center, spacing: 4) { Image(systemName: icon) .symbolRenderingMode(.monochrome) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .frame(minWidth: 30, alignment: .center) Text(title).font(.title3).bold() } - Text(description) - .multilineTextAlignment(.leading) - .lineLimit(10) + } else { + Text(title).font(.title3).bold() + } + } + + private func featureDescription(_ f: Description) -> some View { + VStack(alignment: .leading, spacing: 4) { + featureHeader(f.icon, f.title) + if let d = f.description { + Text(d) + .multilineTextAlignment(.leading) + .lineLimit(10) + } + if f.subfeatures.count > 0 { + ForEach(f.subfeatures, id: \.icon) { s in + HStack(alignment: .center, spacing: 4) { + Image(systemName: s.icon) + .symbolRenderingMode(.monochrome) + .foregroundColor(theme.colors.secondary) + .frame(minWidth: 30, alignment: .center) + Text(s.description) + .multilineTextAlignment(.leading) + .lineLimit(3) + } + } + } } } @@ -490,6 +771,6 @@ struct WhatsNewView: View { struct NewFeaturesView_Previews: PreviewProvider { static var previews: some View { - WhatsNewView() + WhatsNewView(updatedConditions: false) } } diff --git a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift index 3059b049a3..01b25baed8 100644 --- a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift +++ b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift @@ -12,8 +12,8 @@ import CodeScanner struct ConnectDesktopView: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @Environment(\.dismiss) var dismiss: DismissAction - var viaSettings = false @AppStorage(DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS) private var deviceName = UIDevice.current.name @AppStorage(DEFAULT_CONFIRM_REMOTE_SESSIONS) private var confirmRemoteSessions = false @AppStorage(DEFAULT_CONNECT_REMOTE_VIA_MULTICAST) private var connectRemoteViaMulticast = true @@ -36,7 +36,7 @@ struct ConnectDesktopView: View { case badInvitationError case badVersionError(version: String?) case desktopDisconnectedError - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -56,23 +56,6 @@ struct ConnectDesktopView: View { } var body: some View { - if viaSettings { - viewBody - .modifier(BackButton(label: "Back", disabled: Binding.constant(false)) { - if m.activeRemoteCtrl { - alert = .disconnectDesktop(action: .back) - } else { - dismiss() - } - }) - } else { - NavigationView { - viewBody - } - } - } - - var viewBody: some View { Group { let discovery = m.remoteCtrlSession?.discovery if discovery == true || (discovery == nil && !showConnectScreen) { @@ -159,7 +142,7 @@ struct ConnectDesktopView: View { case .desktopDisconnectedError: Alert(title: Text("Connection terminated")) case let .error(title, error): - Alert(title: Text(title), message: Text(error)) + mkAlert(title: title, message: error) } } .interactiveDismissDisabled(m.activeRemoteCtrl) @@ -167,7 +150,7 @@ struct ConnectDesktopView: View { private func connectDesktopView(showScanner: Bool = true) -> some View { List { - Section("This device name") { + Section(header: Text("This device name").foregroundColor(theme.colors.secondary)) { devicesView() } if showScanner { @@ -178,34 +161,40 @@ struct ConnectDesktopView: View { } } .navigationTitle("Connect to desktop") + .modifier(ThemedBackground(grouped: true)) } private func connectingDesktopView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> some View { - List { - Section("Connecting to desktop") { - ctrlDeviceNameText(session, rc) - ctrlDeviceVersionText(session) - } + ZStack { + List { + Section(header: Text("Connecting to desktop").foregroundColor(theme.colors.secondary)) { + ctrlDeviceNameText(session, rc) + ctrlDeviceVersionText(session) + } - if let sessCode = session.sessionCode { - Section("Session code") { - sessionCodeText(sessCode) + if let sessCode = session.sessionCode { + Section(header: Text("Session code").foregroundColor(theme.colors.secondary)) { + sessionCodeText(sessCode) + } + } + + Section { + disconnectButton() } } + .navigationTitle("Connecting to desktop") - Section { - disconnectButton() - } + ProgressView().scaleEffect(2) } - .navigationTitle("Connecting to desktop") + .modifier(ThemedBackground(grouped: true)) } private func searchingDesktopView() -> some View { List { - Section("This device name") { + Section(header: Text("This device name").foregroundColor(theme.colors.secondary)) { devicesView() } - Section("Found desktop") { + Section(header: Text("Found desktop").foregroundColor(theme.colors.secondary)) { Text("Waiting for desktop...").italic() Button { disconnectDesktop() @@ -215,14 +204,15 @@ struct ConnectDesktopView: View { } } .navigationTitle("Connecting to desktop") + .modifier(ThemedBackground(grouped: true)) } @ViewBuilder private func foundDesktopView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo, _ compatible: Bool) -> some View { let v = List { - Section("This device name") { + Section(header: Text("This device name").foregroundColor(theme.colors.secondary)) { devicesView() } - Section("Found desktop") { + Section(header: Text("Found desktop").foregroundColor(theme.colors.secondary)) { ctrlDeviceNameText(session, rc) ctrlDeviceVersionText(session) if !compatible { @@ -242,6 +232,7 @@ struct ConnectDesktopView: View { } } .navigationTitle("Found desktop") + .modifier(ThemedBackground(grouped: true)) if compatible && connectRemoteViaMulticastAuto { v.onAppear { confirmKnownDesktop(rc) } @@ -252,12 +243,12 @@ struct ConnectDesktopView: View { private func verifySessionView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?, _ sessCode: String) -> some View { List { - Section("Connected to desktop") { + Section(header: Text("Connected to desktop").foregroundColor(theme.colors.secondary)) { ctrlDeviceNameText(session, rc) ctrlDeviceVersionText(session) } - Section("Verify code with desktop") { + Section(header: Text("Verify code with desktop").foregroundColor(theme.colors.secondary)) { sessionCodeText(sessCode) Button { verifyDesktopSessionCode(sessCode) @@ -271,12 +262,13 @@ struct ConnectDesktopView: View { } } .navigationTitle("Verify connection") + .modifier(ThemedBackground(grouped: true)) } private func ctrlDeviceNameText(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> Text { var t = Text(rc?.deviceViewName ?? session.ctrlAppInfo?.deviceName ?? "") if (rc == nil) { - t = t + Text(" ") + Text("(new)").italic() + t = t + textSpace + Text("(new)").italic() } return t } @@ -285,20 +277,20 @@ struct ConnectDesktopView: View { let v = session.ctrlAppInfo?.appVersionRange.maxVersion var t = Text("v\(v ?? "")") if v != session.appVersion { - t = t + Text(" ") + Text("(this device v\(session.appVersion))").italic() + t = t + textSpace + Text("(this device v\(session.appVersion))").italic() } return t } private func activeSessionView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo) -> some View { List { - Section("Connected desktop") { + Section(header: Text("Connected desktop").foregroundColor(theme.colors.secondary)) { Text(rc.deviceViewName) ctrlDeviceVersionText(session) } if let sessCode = session.sessionCode { - Section("Session code") { + Section(header: Text("Session code").foregroundColor(theme.colors.secondary)) { sessionCodeText(sessCode) } } @@ -308,9 +300,11 @@ struct ConnectDesktopView: View { } footer: { // This is specific to iOS Text("Keep the app open to use it from desktop") + .foregroundColor(theme.colors.secondary) } } .navigationTitle("Connected to desktop") + .modifier(ThemedBackground(grouped: true)) } private func sessionCodeText(_ code: String) -> some View { @@ -331,19 +325,13 @@ struct ConnectDesktopView: View { } private func scanDesctopAddressView() -> some View { - Section("Scan QR code from desktop") { - CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processDesktopQRCode) - .aspectRatio(1, contentMode: .fit) - .cornerRadius(12) - .listRowBackground(Color.clear) - .listRowSeparator(.hidden) - .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) - .padding(.horizontal) + Section(header: Text("Scan QR code from desktop").foregroundColor(theme.colors.secondary)) { + ScannerInView(showQRCodeScanner: $showQRCodeScanner, processQRCode: processDesktopQRCode, scanMode: .oncePerCode) } } private func desktopAddressView() -> some View { - Section("Desktop address") { + Section(header: Text("Desktop address").foregroundColor(theme.colors.secondary)) { if sessionAddress.isEmpty { Button { sessionAddress = UIPasteboard.general.string ?? "" @@ -356,7 +344,7 @@ struct ConnectDesktopView: View { Text(sessionAddress).lineLimit(1) Spacer() Image(systemName: "multiply.circle.fill") - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .onTapGesture { sessionAddress = "" } } } @@ -371,7 +359,7 @@ struct ConnectDesktopView: View { private func linkedDesktopsView() -> some View { List { - Section("Desktop devices") { + Section(header: Text("Desktop devices").foregroundColor(theme.colors.secondary)) { ForEach(remoteCtrls, id: \.remoteCtrlId) { rc in remoteCtrlView(rc) } @@ -382,7 +370,7 @@ struct ConnectDesktopView: View { } } - Section("Linked desktop options") { + Section(header: Text("Linked desktop options").foregroundColor(theme.colors.secondary)) { Toggle("Verify connections", isOn: $confirmRemoteSessions) Toggle("Discover via local network", isOn: $connectRemoteViaMulticast) if connectRemoteViaMulticast { @@ -391,6 +379,7 @@ struct ConnectDesktopView: View { } } .navigationTitle("Linked desktops") + .modifier(ThemedBackground(grouped: true)) } private func remoteCtrlView(_ rc: RemoteCtrlInfo) -> some View { @@ -467,12 +456,12 @@ struct ConnectDesktopView: View { } } catch let e { await MainActor.run { - switch e as? ChatResponse { - case .chatCmdError(_, .errorRemoteCtrl(.badInvitation)): alert = .badInvitationError - case .chatCmdError(_, .error(.commandError)): alert = .badInvitationError - case let .chatCmdError(_, .errorRemoteCtrl(.badVersion(v))): alert = .badVersionError(version: v) - case .chatCmdError(_, .errorAgent(.RCP(.version))): alert = .badVersionError(version: nil) - case .chatCmdError(_, .errorAgent(.RCP(.ctrlAuth))): alert = .desktopDisconnectedError + switch e as? ChatError { + case .errorRemoteCtrl(.badInvitation): alert = .badInvitationError + case .error(.commandError): alert = .badInvitationError + case let .errorRemoteCtrl(.badVersion(v)): alert = .badVersionError(version: v) + case .errorAgent(.RCP(.version)): alert = .badVersionError(version: nil) + case .errorAgent(.RCP(.ctrlAuth)): alert = .desktopDisconnectedError default: errorAlert(e) } } diff --git a/apps/ios/Shared/Views/TerminalView.swift b/apps/ios/Shared/Views/TerminalView.swift index 94a8937db6..554219eb69 100644 --- a/apps/ios/Shared/Views/TerminalView.swift +++ b/apps/ios/Shared/Views/TerminalView.swift @@ -18,7 +18,9 @@ struct TerminalView: View { @AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false @State var composeState: ComposeState = ComposeState() + @State var selectedRange = NSRange() @State private var keyboardVisible = false + @State private var keyboardHiddenDate = Date.now @State var authorized = !UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA) @State private var terminalItem: TerminalItem? @State private var scrolled = false @@ -96,16 +98,24 @@ struct TerminalView: View { SendMessageView( composeState: $composeState, + selectedRange: $selectedRange, sendMessage: { _ in consoleSendMessage() }, showVoiceMessageButton: false, onMediaAdded: { _ in }, - keyboardVisible: $keyboardVisible + keyboardVisible: $keyboardVisible, + keyboardHiddenDate: $keyboardHiddenDate ) .padding(.horizontal, 12) } } .navigationViewStyle(.stack) - .navigationTitle("Chat console") + .toolbar { + // Redaction broken for `.navigationTitle` - using a toolbar item instead. + ToolbarItem(placement: .principal) { + Text("Chat console").font(.headline) + } + } + .modifier(ThemedBackground()) } func scrollToBottom(_ proxy: ScrollViewProxy, animation: Animation = .default) { @@ -121,6 +131,7 @@ struct TerminalView: View { return ScrollView { Text(s.prefix(maxItemSize)) .padding() + .frame(maxWidth: .infinity) } .toolbar { ToolbarItem(placement: .navigationBarTrailing) { @@ -130,21 +141,22 @@ struct TerminalView: View { } } .onDisappear { terminalItem = nil } + .modifier(ThemedBackground()) } func consoleSendMessage() { - let cmd = ChatCommand.string(composeState.message) if composeState.message.starts(with: "/sql") && (!prefPerformLA || !developerTools) { - let resp = ChatResponse.chatCmdError(user_: nil, chatError: ChatError.error(errorType: ChatErrorType.commandError(message: "Failed reading: empty"))) + let resp: APIResult = APIResult.error(ChatError.error(errorType: ChatErrorType.commandError(message: "Failed reading: empty"))) Task { - await TerminalItems.shared.addCommand(.now, cmd, resp) + await TerminalItems.shared.addCommand(.now, .string(composeState.message), resp) } } else { + let cmd = composeState.message DispatchQueue.global().async { Task { - composeState.inProgress = true - _ = await chatSendCmd(cmd) - composeState.inProgress = false + await MainActor.run { composeState.inProgress = true } + await sendTerminalCmd(cmd) + await MainActor.run { composeState.inProgress = false } } } } @@ -152,12 +164,38 @@ struct TerminalView: View { } } +func sendTerminalCmd(_ cmd: String) async { + let start: Date = .now + await withCheckedContinuation { (cont: CheckedContinuation) in + let d = sendSimpleXCmdStr(cmd) + Task { + guard let d else { + await TerminalItems.shared.addCommand(start, ChatCommand.string(cmd), APIResult.error(.invalidJSON(json: nil))) + return + } + let r0: APIResult = decodeAPIResult(d) + guard case .invalid = r0 else { + await TerminalItems.shared.addCommand(start, .string(cmd), r0) + return + } + let r1: APIResult = decodeAPIResult(d) + guard case .invalid = r1 else { + await TerminalItems.shared.addCommand(start, .string(cmd), r1) + return + } + let r2: APIResult = decodeAPIResult(d) + await TerminalItems.shared.addCommand(start, .string(cmd), r2) + } + cont.resume(returning: ()) + } +} + struct TerminalView_Previews: PreviewProvider { static var previews: some View { let chatModel = ChatModel() chatModel.terminalItems = [ - .resp(.now, ChatResponse.response(type: "contactSubscribed", json: "{}")), - .resp(.now, ChatResponse.response(type: "newChatItem", json: "{}")) + .err(.now, APIResult.invalid(type: "contactSubscribed", json: "{}".data(using: .utf8)!).unexpected), + .err(.now, APIResult.invalid(type: "newChatItems", json: "{}".data(using: .utf8)!).unexpected) ] return NavigationView { TerminalView() diff --git a/apps/ios/Shared/Views/UserSettings/AdvancedNetworkSettings.swift b/apps/ios/Shared/Views/UserSettings/AdvancedNetworkSettings.swift deleted file mode 100644 index 9da3bac00b..0000000000 --- a/apps/ios/Shared/Views/UserSettings/AdvancedNetworkSettings.swift +++ /dev/null @@ -1,170 +0,0 @@ -// -// AdvancedNetworkSettings.swift -// SimpleX (iOS) -// -// Created by Evgeny on 02/08/2022. -// Copyright © 2022 SimpleX Chat. All rights reserved. -// - -import SwiftUI -import SimpleXChat - -private let secondsLabel = NSLocalizedString("sec", comment: "network option") - -enum NetworkSettingsAlert: Identifiable { - case update - case error(err: String) - - var id: String { - switch self { - case .update: return "update" - case let .error(err): return "error \(err)" - } - } -} - -struct AdvancedNetworkSettings: View { - @State private var netCfg = NetCfg.defaults - @State private var currentNetCfg = NetCfg.defaults - @State private var cfgLoaded = false - @State private var enableKeepAlive = true - @State private var keepAliveOpts = KeepAliveOpts.defaults - @State private var showSettingsAlert: NetworkSettingsAlert? - - var body: some View { - VStack { - List { - Section { - Button { - updateNetCfgView(NetCfg.defaults) - showSettingsAlert = .update - } label: { - Text("Reset to defaults") - } - .disabled(currentNetCfg == NetCfg.defaults) - - Button { - updateNetCfgView(NetCfg.proxyDefaults) - showSettingsAlert = .update - } label: { - Text("Set timeouts for proxy/VPN") - } - .disabled(currentNetCfg == NetCfg.proxyDefaults) - - timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 45_000000], label: secondsLabel) - timeoutSettingPicker("Protocol timeout", selection: $netCfg.tcpTimeout, values: [5_000000, 7_000000, 10_000000, 15_000000, 20_000000, 30_000000], label: secondsLabel) - timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [15_000, 30_000, 45_000, 60_000, 90_000, 120_000], label: secondsLabel) - timeoutSettingPicker("PING interval", selection: $netCfg.smpPingInterval, values: [120_000000, 300_000000, 600_000000, 1200_000000, 2400_000000, 3600_000000], label: secondsLabel) - intSettingPicker("PING count", selection: $netCfg.smpPingCount, values: [1, 2, 3, 5, 8], label: "") - Toggle("Enable TCP keep-alive", isOn: $enableKeepAlive) - - if enableKeepAlive { - intSettingPicker("TCP_KEEPIDLE", selection: $keepAliveOpts.keepIdle, values: [15, 30, 60, 120, 180], label: secondsLabel) - intSettingPicker("TCP_KEEPINTVL", selection: $keepAliveOpts.keepIntvl, values: [5, 10, 15, 30, 60], label: secondsLabel) - intSettingPicker("TCP_KEEPCNT", selection: $keepAliveOpts.keepCnt, values: [1, 2, 4, 6, 8], label: "") - } else { - Group { - Text("TCP_KEEPIDLE") - Text("TCP_KEEPINTVL") - Text("TCP_KEEPCNT") - } - .foregroundColor(.secondary) - } - } header: { - Text("") - } footer: { - HStack { - Button { - updateNetCfgView(currentNetCfg) - } label: { - Label("Revert", systemImage: "arrow.counterclockwise").font(.callout) - } - - Spacer() - - Button { - showSettingsAlert = .update - } label: { - Label("Save", systemImage: "checkmark").font(.callout) - } - } - .disabled(netCfg == currentNetCfg) - } - } - } - .onChange(of: keepAliveOpts) { opts in - netCfg.tcpKeepAlive = keepAliveOpts - } - .onChange(of: enableKeepAlive) { on in - netCfg.tcpKeepAlive = on ? (currentNetCfg.tcpKeepAlive ?? KeepAliveOpts.defaults) : nil - } - .onAppear { - if cfgLoaded { return } - cfgLoaded = true - currentNetCfg = getNetCfg() - updateNetCfgView(currentNetCfg) - } - .alert(item: $showSettingsAlert) { a in - switch a { - case .update: - return Alert( - title: Text("Update network settings?"), - message: Text("Updating settings will re-connect the client to all servers."), - primaryButton: .default(Text("Ok")) { - saveNetCfg() - }, - secondaryButton: .cancel() - ) - case let .error(err): - return Alert( - title: Text("Error updating settings"), - message: Text(err) - ) - } - } - } - - private func updateNetCfgView(_ cfg: NetCfg) { - netCfg = cfg - enableKeepAlive = netCfg.enableKeepAlive - keepAliveOpts = netCfg.tcpKeepAlive ?? KeepAliveOpts.defaults - } - - private func saveNetCfg() { - do { - try setNetworkConfig(netCfg) - currentNetCfg = netCfg - setNetCfg(netCfg) - } catch let error { - let err = responseError(error) - showSettingsAlert = .error(err: err) - logger.error("\(err)") - } - } - - private func intSettingPicker(_ title: LocalizedStringKey, selection: Binding, values: [Int], label: String) -> some View { - Picker(title, selection: selection) { - ForEach(values, id: \.self) { value in - Text("\(value) \(label)") - } - } - .frame(height: 36) - } - - private func timeoutSettingPicker(_ title: LocalizedStringKey, selection: Binding, values: [Int], label: String) -> some View { - Picker(title, selection: selection) { - let v = selection.wrappedValue - let vs = values.contains(v) ? values : values + [v] - ForEach(vs, id: \.self) { value in - Text("\(String(format: "%g", (Double(value) / 1000000))) \(secondsLabel)") - } - } - .frame(height: 36) - } -} - -struct AdvancedNetworkSettings_Previews: PreviewProvider { - static var previews: some View { - AdvancedNetworkSettings() - } -} diff --git a/apps/ios/Shared/Views/UserSettings/AppSettings.swift b/apps/ios/Shared/Views/UserSettings/AppSettings.swift index ba192b333c..44e0b20958 100644 --- a/apps/ios/Shared/Views/UserSettings/AppSettings.swift +++ b/apps/ios/Shared/Views/UserSettings/AppSettings.swift @@ -19,18 +19,29 @@ extension AppSettings { val.hostMode = .publicHost val.requiredHostMode = true } - val.socksProxy = nil - setNetCfg(val) + if val.socksProxy != nil { + val.socksProxy = networkProxy?.toProxyString() + setNetCfg(val, networkProxy: networkProxy) + } else { + val.socksProxy = nil + setNetCfg(val, networkProxy: nil) + } } + if let val = networkProxy { networkProxyDefault.set(val) } if let val = privacyEncryptLocalFiles { privacyEncryptLocalFilesGroupDefault.set(val) } + if let val = privacyAskToApproveRelays { privacyAskToApproveRelaysGroupDefault.set(val) } if let val = privacyAcceptImages { privacyAcceptImagesGroupDefault.set(val) def.setValue(val, forKey: DEFAULT_PRIVACY_ACCEPT_IMAGES) } - if let val = privacyLinkPreviews { def.setValue(val, forKey: DEFAULT_PRIVACY_LINK_PREVIEWS) } + if let val = privacyLinkPreviews { + privacyLinkPreviewsGroupDefault.set(val) + def.setValue(val, forKey: DEFAULT_PRIVACY_LINK_PREVIEWS) + } if let val = privacyShowChatPreviews { def.setValue(val, forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) } if let val = privacySaveLastDraft { def.setValue(val, forKey: DEFAULT_PRIVACY_SAVE_LAST_DRAFT) } if let val = privacyProtectScreen { def.setValue(val, forKey: DEFAULT_PRIVACY_PROTECT_SCREEN) } + if let val = privacyMediaBlurRadius { def.setValue(val, forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) } if let val = notificationMode { ChatModel.shared.notificationMode = val.toNotificationsMode() } if let val = notificationPreviewMode { ntfPreviewModeGroupDefault.set(val) } if let val = webrtcPolicyRelay { def.setValue(val, forKey: DEFAULT_WEBRTC_POLICY_RELAY) } @@ -43,18 +54,33 @@ extension AppSettings { if let val = androidCallOnLockScreen { def.setValue(val.rawValue, forKey: ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN) } if let val = iosCallKitEnabled { callKitEnabledGroupDefault.set(val) } if let val = iosCallKitCallsInRecents { def.setValue(val, forKey: DEFAULT_CALL_KIT_CALLS_IN_RECENTS) } + if let val = uiProfileImageCornerRadius { + profileImageCornerRadiusGroupDefault.set(val) + def.setValue(val, forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) + } + if let val = uiChatItemRoundness { def.setValue(val, forKey: DEFAULT_CHAT_ITEM_ROUNDNESS)} + if let val = uiChatItemTail { def.setValue(val, forKey: DEFAULT_CHAT_ITEM_TAIL)} + if let val = uiColorScheme { currentThemeDefault.set(val) } + if let val = uiDarkColorScheme { systemDarkThemeDefault.set(val) } + if let val = uiCurrentThemeIds { currentThemeIdsDefault.set(val) } + if let val = uiThemes { themeOverridesDefault.set(val.skipDuplicates()) } + if let val = oneHandUI { groupDefaults.setValue(val, forKey: GROUP_DEFAULT_ONE_HAND_UI) } + if let val = chatBottomBar { groupDefaults.setValue(val, forKey: GROUP_DEFAULT_CHAT_BOTTOM_BAR) } } public static var current: AppSettings { let def = UserDefaults.standard var c = AppSettings.defaults c.networkConfig = getNetCfg() + c.networkProxy = networkProxyDefault.get() c.privacyEncryptLocalFiles = privacyEncryptLocalFilesGroupDefault.get() + c.privacyAskToApproveRelays = privacyAskToApproveRelaysGroupDefault.get() c.privacyAcceptImages = privacyAcceptImagesGroupDefault.get() c.privacyLinkPreviews = def.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS) c.privacyShowChatPreviews = def.bool(forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) c.privacySaveLastDraft = def.bool(forKey: DEFAULT_PRIVACY_SAVE_LAST_DRAFT) c.privacyProtectScreen = def.bool(forKey: DEFAULT_PRIVACY_PROTECT_SCREEN) + c.privacyMediaBlurRadius = def.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) c.notificationMode = AppSettingsNotificationMode.from(ChatModel.shared.notificationMode) c.notificationPreviewMode = ntfPreviewModeGroupDefault.get() c.webrtcPolicyRelay = def.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY) @@ -67,6 +93,15 @@ extension AppSettings { c.androidCallOnLockScreen = AppSettingsLockScreenCalls(rawValue: def.string(forKey: ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN)!) c.iosCallKitEnabled = callKitEnabledGroupDefault.get() c.iosCallKitCallsInRecents = def.bool(forKey: DEFAULT_CALL_KIT_CALLS_IN_RECENTS) + c.uiProfileImageCornerRadius = def.double(forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) + c.uiChatItemRoundness = def.double(forKey: DEFAULT_CHAT_ITEM_ROUNDNESS) + c.uiChatItemTail = def.bool(forKey: DEFAULT_CHAT_ITEM_TAIL) + c.uiColorScheme = currentThemeDefault.get() + c.uiDarkColorScheme = systemDarkThemeDefault.get() + c.uiCurrentThemeIds = currentThemeIdsDefault.get() + c.uiThemes = themeOverridesDefault.get() + c.oneHandUI = groupDefaults.bool(forKey: GROUP_DEFAULT_ONE_HAND_UI) + c.chatBottomBar = groupDefaults.bool(forKey: GROUP_DEFAULT_CHAT_BOTTOM_BAR) return c } } diff --git a/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift b/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift index 1f648b09dc..c6d0e27289 100644 --- a/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift +++ b/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift @@ -7,21 +7,51 @@ // import SwiftUI +import SimpleXChat +import Yams -let defaultAccentColor = CGColor.init(red: 0, green: 0.533, blue: 1, alpha: 1) +let colorModesLocalized: [LocalizedStringKey] = ["System", "Light", "Dark"] +let colorModesNames: [DefaultThemeMode?] = [nil, DefaultThemeMode.light, DefaultThemeMode.dark] -let interfaceStyles: [UIUserInterfaceStyle] = [.unspecified, .light, .dark] +let darkThemesLocalized: [LocalizedStringKey] = ["Dark", "SimpleX", "Black"] +let darkThemesNames: [String] = [DefaultTheme.DARK.themeName, DefaultTheme.SIMPLEX.themeName, DefaultTheme.BLACK.themeName] -let interfaceStyleNames: [LocalizedStringKey] = ["System", "Light", "Dark"] +let darkThemesWithoutBlackLocalized: [LocalizedStringKey] = ["Dark", "SimpleX"] +let darkThemesWithoutBlackNames: [String] = [DefaultTheme.DARK.themeName, DefaultTheme.SIMPLEX.themeName] let appSettingsURL = URL(string: UIApplication.openSettingsURLString)! struct AppearanceSettings: View { + @EnvironmentObject var m: ChatModel + @Environment(\.colorScheme) var colorScheme @EnvironmentObject var sceneDelegate: SceneDelegate + @EnvironmentObject var theme: AppTheme @State private var iconLightTapped = false @State private var iconDarkTapped = false - @State private var userInterfaceStyle = getUserInterfaceStyleDefault() - @State private var uiTintColor = getUIAccentColorDefault() + @State private var colorMode: DefaultThemeMode? = { + if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME { nil as DefaultThemeMode? } else { CurrentColors.base.mode } + }() + @State private var darkModeTheme: String = UserDefaults.standard.string(forKey: DEFAULT_SYSTEM_DARK_THEME) ?? DefaultTheme.DARK.themeName + @AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var profileImageCornerRadius = defaultProfileImageCorner + @AppStorage(DEFAULT_CHAT_ITEM_ROUNDNESS) private var chatItemRoundness = defaultChatItemRoundness + @AppStorage(DEFAULT_CHAT_ITEM_TAIL) private var chatItemTail = true + @AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true + @AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial + + @State var themeUserDestination: (Int64, ThemeModeOverrides?)? = { + if let currentUser = ChatModel.shared.currentUser, let uiThemes = currentUser.uiThemes, uiThemes.preferredMode(!CurrentColors.colors.isLight) != nil { + (currentUser.userId, uiThemes) + } else { + nil + } + }() + + @State var perUserTheme: ThemeModeOverride = { + ChatModel.shared.currentUser?.uiThemes?.preferredMode(!CurrentColors.colors.isLight) ?? ThemeModeOverride(mode: CurrentColors.base.mode) + }() + + @State var showImageImporter: Bool = false + @State var customizeThemeIsOpen: Bool = false var body: some View { VStack{ @@ -36,42 +66,228 @@ struct AppearanceSettings: View { } } - Section("App icon") { + Section("Chat list") { + Toggle("Reachable chat toolbar", isOn: $oneHandUI) + Picker("Toolbar opacity", selection: $toolbarMaterial) { + ForEach(ToolbarMaterial.allCases, id: \.rawValue) { tm in + Text(tm.text).tag(tm.rawValue) + } + } + .frame(height: 36) + } + + Section { + ThemeDestinationPicker(themeUserDestination: $themeUserDestination, themeUserDest: themeUserDestination?.0, customizeThemeIsOpen: $customizeThemeIsOpen) + + WallpaperPresetSelector( + selectedWallpaper: theme.wallpaper.type, + currentColors: currentColors, + onChooseType: onChooseType + ) + .padding(.bottom, 10) + .listRowInsets(.init()) + .listRowBackground(Color.clear) + .modifier(WallpaperImporter(showImageImporter: $showImageImporter, onChooseImage: { image in + if let filename = saveWallpaperFile(image: image) { + if themeUserDestination == nil, case let WallpaperType.image(filename, _, _) = theme.wallpaper.type { + removeWallpaperFile(fileName: filename) + } else if let type = perUserTheme.type, case let WallpaperType.image(filename, _, _) = type { + removeWallpaperFile(fileName: filename) + } + onTypeChange(WallpaperType.image(filename, 1, WallpaperScaleType.fill)) + } + })) + + if case let WallpaperType.image(filename, _, _) = theme.wallpaper.type, (themeUserDestination == nil || perUserTheme.wallpaper?.imageFile != nil) { + Button { + if themeUserDestination == nil { + let defaultActiveTheme = ThemeManager.defaultActiveTheme(themeOverridesDefault.get()) + ThemeManager.saveAndApplyWallpaper(theme.base, nil, themeOverridesDefault) + ThemeManager.removeTheme(defaultActiveTheme?.themeId) + removeWallpaperFile(fileName: filename) + } else { + removeUserThemeModeOverrides($themeUserDestination, $perUserTheme) + } + saveThemeToDatabase(themeUserDestination) + } label: { + Text("Remove image") + .foregroundColor(theme.colors.primary) + } + .listRowBackground(Color.clear) + } + + Picker("Color mode", selection: $colorMode) { + ForEach(Array(colorModesNames.enumerated()), id: \.element) { index, mode in + Text(colorModesLocalized[index]) + } + } + .frame(height: 36) + Picker("Dark mode colors", selection: $darkModeTheme) { + if theme.base == .BLACK || themeOverridesDefault.get().contains(where: { $0.base == .BLACK }) { + ForEach(Array(darkThemesNames.enumerated()), id: \.element) { index, darkTheme in + Text(darkThemesLocalized[index]) + } + } else { + ForEach(Array(darkThemesWithoutBlackNames.enumerated()), id: \.element) { index, darkTheme in + Text(darkThemesLocalized[index]) + } + } + } + .frame(height: 36) + + NavigationLink { + let userId = themeUserDestination?.0 + if let userId { + UserWallpaperEditorSheet(userId: userId) + .onAppear { + customizeThemeIsOpen = true + } + } else { + CustomizeThemeView(onChooseType: onChooseType) + .navigationTitle("Customize theme") + .modifier(ThemedBackground(grouped: true)) + .onAppear { + customizeThemeIsOpen = true + } + } + } label: { + Text("Customize theme") + } + } header: { + Text("Themes") + .foregroundColor(theme.colors.secondary) + } + .onChange(of: profileImageCornerRadius) { cornerRadius in + profileImageCornerRadiusGroupDefault.set(cornerRadius) + saveThemeToDatabase(nil) + } + .onChange(of: colorMode) { mode in + guard let mode else { + ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME) + return + } + if case DefaultThemeMode.light = mode { + ThemeManager.applyTheme(DefaultTheme.LIGHT.themeName) + } else if case DefaultThemeMode.dark = mode { + ThemeManager.applyTheme(systemDarkThemeDefault.get()) + } + } + .onChange(of: darkModeTheme) { darkTheme in + ThemeManager.changeDarkTheme(darkTheme) + if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME { + ThemeManager.applyTheme(currentThemeDefault.get()) + } else if currentThemeDefault.get() != DefaultTheme.LIGHT.themeName { + ThemeManager.applyTheme(systemDarkThemeDefault.get()) + } + } + + Section(header: Text("Message shape").foregroundColor(theme.colors.secondary)) { + HStack { + Text("Corner") + Slider(value: $chatItemRoundness, in: 0...1, step: 0.05) + } + Toggle("Tail", isOn: $chatItemTail) + } + + Section(header: Text("Profile images").foregroundColor(theme.colors.secondary)) { + HStack(spacing: 16) { + if let img = m.currentUser?.image, img != "" { + ProfileImage(imageStr: img, size: 60) + } else { + clipProfileImage(Image(colorScheme == .light ? "icon-dark" : "icon-light"), size: 60, radius: profileImageCornerRadius) + } + + Slider( + value: $profileImageCornerRadius, + in: 0...50, + step: 2.5 + ) + } + .foregroundColor(theme.colors.secondary) + } + + Section(header: Text("App icon").foregroundColor(theme.colors.secondary)) { HStack { updateAppIcon(image: "icon-light", icon: nil, tapped: $iconLightTapped) Spacer().frame(width: 16) updateAppIcon(image: "icon-dark", icon: "DarkAppIcon", tapped: $iconDarkTapped) } } - - Section { - Picker("Theme", selection: $userInterfaceStyle) { - ForEach(interfaceStyles, id: \.self) { style in - Text(interfaceStyleNames[interfaceStyles.firstIndex(of: style) ?? 0]) - } - } - .frame(height: 36) - ColorPicker("Accent color", selection: $uiTintColor, supportsOpacity: false) - } header: { - Text("Colors") - } footer: { - Button { - uiTintColor = defaultAccentColor - setUIAccentColorDefault(defaultAccentColor) - } label: { - Text("Reset colors").font(.callout) - } - } - .onChange(of: userInterfaceStyle) { _ in - sceneDelegate.window?.overrideUserInterfaceStyle = userInterfaceStyle - setUserInterfaceStyleDefault(userInterfaceStyle) - } - .onChange(of: uiTintColor) { _ in - sceneDelegate.window?.tintColor = UIColor(cgColor: uiTintColor) - setUIAccentColorDefault(uiTintColor) - } } } + .onAppear { + customizeThemeIsOpen = false + } + } + + private func updateThemeUserDestination() { + if let dest = themeUserDestination { + var (userId, themes) = dest + themes = themes ?? ThemeModeOverrides() + if case DefaultThemeMode.light = perUserTheme.mode { + themes?.light = perUserTheme + } else { + themes?.dark = perUserTheme + } + themeUserDestination = (userId, themes) + } + } + + private func onTypeCopyFromSameTheme(_ type: WallpaperType?) -> Bool { + if themeUserDestination == nil { + ThemeManager.saveAndApplyWallpaper(theme.base, type, themeOverridesDefault) + } else { + var wallpaperFiles = Set([perUserTheme.wallpaper?.imageFile]) + _ = ThemeManager.copyFromSameThemeOverrides(type, nil, $perUserTheme) + wallpaperFiles.remove(perUserTheme.wallpaper?.imageFile) + wallpaperFiles.forEach(removeWallpaperFile) + updateThemeUserDestination() + } + saveThemeToDatabase(themeUserDestination) + return true + } + + private func onTypeChange(_ type: WallpaperType?) { + if themeUserDestination == nil { + ThemeManager.saveAndApplyWallpaper(theme.base, type, themeOverridesDefault) + } else { + ThemeManager.applyWallpaper(type, $perUserTheme) + updateThemeUserDestination() + } + saveThemeToDatabase(themeUserDestination) + } + + private func currentColors(_ type: WallpaperType?) -> ThemeManager.ActiveTheme { + // If applying for : + // - all themes: no overrides needed + // - specific user: only user overrides for currently selected theme are needed, because they will NOT be copied when other wallpaper is selected + let perUserOverride: ThemeModeOverrides? = themeUserDestination == nil + ? nil + : theme.wallpaper.type.sameType(type) + ? m.currentUser?.uiThemes + : nil + return ThemeManager.currentColors(type, nil, perUserOverride, themeOverridesDefault.get()) + } + + private func onChooseType(_ type: WallpaperType?) { + // don't have image in parent or already selected wallpaper with custom image + if let type, case WallpaperType.image = type { + if case WallpaperType.image = theme.wallpaper.type, themeUserDestination?.1 != nil { + showImageImporter = true + } else if currentColors(type).wallpaper.type.image == nil { + showImageImporter = true + } else if currentColors(type).wallpaper.type.image != nil, case WallpaperType.image = theme.wallpaper.type, themeUserDestination == nil { + showImageImporter = true + } else if themeUserDestination == nil { + onTypeChange(currentColors(type).wallpaper.type) + } else { + _ = onTypeCopyFromSameTheme(currentColors(type).wallpaper.type) + } + } else if (themeUserDestination != nil && themeUserDestination?.1?.preferredMode(!CurrentColors.colors.isLight)?.type != type) || theme.wallpaper.type != type { + _ = onTypeCopyFromSameTheme(type) + } else { + onTypeChange(type) + } } private var currentLanguage: String { @@ -93,10 +309,789 @@ struct AppearanceSettings: View { } ._onButtonGesture { tapped.wrappedValue = $0 } perform: {} .overlay(tapped.wrappedValue ? Color.secondary : Color.clear) - .cornerRadius(20) + .cornerRadius(13.5) } } +enum ToolbarMaterial: String, CaseIterable { + case bar + case ultraThin + case thin + case regular + case thick + case ultraThick + + static func material(_ s: String) -> Material { + ToolbarMaterial(rawValue: s)?.material ?? Material.bar + } + + static let defaultMaterial: String = ToolbarMaterial.regular.rawValue + + var material: Material { + switch self { + case .bar: .bar + case .ultraThin: .ultraThin + case .thin: .thin + case .regular: .regular + case .thick: .thick + case .ultraThick: .ultraThick + } + } + + var text: String { + switch self { + case .bar: "System" + case .ultraThin: "Ultra thin" + case .thin: "Thin" + case .regular: "Regular" + case .thick: "Thick" + case .ultraThick: "Ultra thick" + } + } +} + +struct ChatThemePreview: View { + @EnvironmentObject var theme: AppTheme + var base: DefaultTheme + var wallpaperType: WallpaperType? + var backgroundColor: Color? + var tintColor: Color? + var withMessages: Bool = true + + var body: some View { + let themeBackgroundColor = theme.colors.background + let backgroundColor = backgroundColor ?? wallpaperType?.defaultBackgroundColor(theme.base, theme.colors.background) + let tintColor = tintColor ?? wallpaperType?.defaultTintColor(theme.base) + let view = VStack { + if withMessages { + let alice = ChatItem.getSample(1, CIDirection.directRcv, Date.now, NSLocalizedString("Good afternoon!", comment: "message preview")) + let bob = ChatItem.getSample(2, CIDirection.directSnd, Date.now, NSLocalizedString("Good morning!", comment: "message preview"), quotedItem: CIQuote.getSample(alice.id, alice.meta.itemTs, alice.content.text, chatDir: alice.chatDir)) + HStack { + ChatItemView(chat: Chat.sampleData, chatItem: alice, scrollToItemId: { _ in }) + .modifier(ChatItemClipped(alice, tailVisible: true)) + Spacer() + } + HStack { + Spacer() + ChatItemView(chat: Chat.sampleData, chatItem: bob, scrollToItemId: { _ in }) + .modifier(ChatItemClipped(bob, tailVisible: true)) + .frame(alignment: .trailing) + } + } else { + Rectangle().fill(.clear) + } + } + .padding(.vertical, 10) + .padding(.horizontal, 16) + .frame(maxWidth: .infinity) + + if let wallpaperType, let wallpaperImage = wallpaperType.image, let backgroundColor, let tintColor { + view.modifier(ChatViewBackground(image: wallpaperImage, imageType: wallpaperType, background: backgroundColor, tint: tintColor)) + } else { + view.background(themeBackgroundColor) + } + } +} + +struct WallpaperPresetSelector: View { + @EnvironmentObject var theme: AppTheme + var selectedWallpaper: WallpaperType? + var activeBackgroundColor: Color? = nil + var activeTintColor: Color? = nil + var currentColors: (WallpaperType?) -> ThemeManager.ActiveTheme + var onChooseType: (WallpaperType?) -> Void + let width: Double = 80 + let height: Double = 80 + let backgrounds = PresetWallpaper.allCases + + private let cornerRadius: Double = 22.5 + + var baseTheme: DefaultTheme { theme.base } + + var body: some View { + VStack { + ChatThemePreview( + base: theme.base, + wallpaperType: selectedWallpaper, + backgroundColor: activeBackgroundColor ?? theme.wallpaper.background, + tintColor: activeTintColor ?? theme.wallpaper.tint + ) + .environmentObject(currentColors(selectedWallpaper).toAppTheme()) + ScrollView(.horizontal, showsIndicators: false) { + HStack { + BackgroundItem(nil) + ForEach(backgrounds, id: \.self) { background in + BackgroundItem(background) + } + OwnBackgroundItem(selectedWallpaper) + } + } + } + } + + func plus() -> some View { + Image(systemName: "plus") + .tint(theme.colors.primary) + .frame(width: 25, height: 25) + } + + func BackgroundItem(_ background: PresetWallpaper?) -> some View { + let checked = (background == nil && (selectedWallpaper == nil || selectedWallpaper?.isEmpty == true)) || selectedWallpaper?.samePreset(other: background) == true + let type = background?.toType(baseTheme, checked ? selectedWallpaper?.scale : nil) + let overrides = currentColors(type).toAppTheme() + return ZStack { + if let type { + ChatThemePreview( + base: baseTheme, + wallpaperType: type, + backgroundColor: checked ? activeBackgroundColor ?? overrides.wallpaper.background : overrides.wallpaper.background, + tintColor: checked ? activeTintColor ?? overrides.wallpaper.tint : overrides.wallpaper.tint, + withMessages: false + ) + .environmentObject(overrides) + } else { + Rectangle().fill(overrides.colors.background) + } + } + .frame(width: CGFloat(width), height: CGFloat(height)) + .clipShape(RoundedRectangle(cornerRadius: width / 100 * cornerRadius)) + .overlay(RoundedRectangle(cornerRadius: width / 100 * cornerRadius) + .strokeBorder(checked ? theme.colors.primary.opacity(0.8) : theme.colors.onBackground.opacity(isInDarkTheme() ? 0.2 : 0.1), lineWidth: 1) + ) + .onTapGesture { + onChooseType(background?.toType(baseTheme)) + } + } + + func OwnBackgroundItem(_ type: WallpaperType?) -> some View { + let overrides = currentColors(WallpaperType.image("", nil, nil)) + let appWallpaper = overrides.wallpaper + let backgroundColor = appWallpaper.background + let tintColor = appWallpaper.tint + let wallpaperImage = appWallpaper.type.image + let checked = if let type, case WallpaperType.image = type, wallpaperImage != nil { true } else { false } + let borderColor = if let type, case WallpaperType.image = type { theme.colors.primary.opacity(0.8) } else { theme.colors.onBackground.opacity(0.1) } + return ZStack { + if checked || wallpaperImage != nil { + ChatThemePreview( + base: baseTheme, + wallpaperType: checked ? type : appWallpaper.type, + backgroundColor: checked ? activeBackgroundColor ?? backgroundColor : backgroundColor, + tintColor: checked ? activeTintColor ?? tintColor : tintColor, + withMessages: false + ) + .environmentObject(currentColors(type).toAppTheme()) + } else { + plus() + } + } + .frame(width: width, height: height) + .clipShape(RoundedRectangle(cornerRadius: width / 100 * cornerRadius)) + .overlay(RoundedRectangle(cornerRadius: width / 100 * cornerRadius) + .strokeBorder(borderColor, lineWidth: 1) + ) + .onTapGesture { + onChooseType(WallpaperType.image("", nil, nil)) + } + } +} + +struct CustomizeThemeView: View { + @EnvironmentObject var theme: AppTheme + var onChooseType: (WallpaperType?) -> Void + @State private var showFileImporter = false + + var body: some View { + List { + let wallpaperImage = theme.wallpaper.type.image + let wallpaperType = theme.wallpaper.type + let baseTheme = theme.base + + let editColor: (ThemeColor) -> Binding = { name in + editColorBinding( + name: name, + wallpaperType: wallpaperType, + wallpaperImage: wallpaperImage, + theme: theme, + onColorChange: { color in + updateBackendTask.cancel() + updateBackendTask = Task { + if (try? await Task.sleep(nanoseconds: 200_000000)) != nil { + ThemeManager.saveAndApplyThemeColor(baseTheme, name, color) + saveThemeToDatabase(nil) + } + } + }) + } + WallpaperPresetSelector( + selectedWallpaper: wallpaperType, + currentColors: { type in + ThemeManager.currentColors(type, nil, nil, themeOverridesDefault.get()) + }, + onChooseType: onChooseType + ) + .listRowInsets(.init()) + .listRowBackground(Color.clear) + + if case let WallpaperType.image(filename, _, _) = theme.wallpaper.type { + Button { + let defaultActiveTheme = ThemeManager.defaultActiveTheme(themeOverridesDefault.get()) + ThemeManager.saveAndApplyWallpaper(baseTheme, nil, themeOverridesDefault) + ThemeManager.removeTheme(defaultActiveTheme?.themeId) + removeWallpaperFile(fileName: filename) + saveThemeToDatabase(nil) + } label: { + Text("Remove image") + .foregroundColor(theme.colors.primary) + } + .listRowBackground(Color.clear) + } + + Section { + WallpaperSetupView( + wallpaperType: wallpaperType, + base: baseTheme, + initialWallpaper: theme.wallpaper, + editColor: { name in + editColor(name) + }, + onTypeChange: { type in + ThemeManager.saveAndApplyWallpaper(baseTheme, type, themeOverridesDefault) + updateBackendTask.cancel() + updateBackendTask = Task { + if (try? await Task.sleep(nanoseconds: 200_000000)) != nil { + saveThemeToDatabase(nil) + } + } + } + ) + } header: { + Text("Chat colors") + .foregroundColor(theme.colors.secondary) + } + + CustomizeThemeColorsSection(editColor: editColor) + + let currentOverrides = ThemeManager.defaultActiveTheme(themeOverridesDefault.get()) + let canResetColors = theme.base.hasChangedAnyColor(currentOverrides) + if canResetColors { + Button { + ThemeManager.resetAllThemeColors() + saveThemeToDatabase(nil) + } label: { + Text("Reset colors").font(.callout).foregroundColor(theme.colors.primary) + } + } + + ImportExportThemeSection(showFileImporter: $showFileImporter, perChat: nil, perUser: nil) + } + .modifier( + ThemeImporter(isPresented: $showFileImporter) { theme in + ThemeManager.saveAndApplyThemeOverrides(theme) + saveThemeToDatabase(nil) + } + ) + /// When changing app theme, user overrides are hidden. User overrides will be returned back after closing Appearance screen, see ThemeDestinationPicker() + .interactiveDismissDisabled(true) + } +} + +struct ImportExportThemeSection: View { + @EnvironmentObject var theme: AppTheme + @Binding var showFileImporter: Bool + var perChat: ThemeModeOverride? + var perUser: ThemeModeOverrides? + + var body: some View { + Section { + Button { + let overrides = ThemeManager.currentThemeOverridesForExport(nil, perChat, perUser) + do { + let encoded = try encodeThemeOverrides(overrides) + var lines = encoded.split(separator: "\n") + // Removing theme id without using custom serializer or data class + lines.remove(at: 0) + let theme = lines.joined(separator: "\n") + let tempUrl = getTempFilesDirectory().appendingPathComponent("simplex.theme") + try? FileManager.default.removeItem(at: tempUrl) + if FileManager.default.createFile(atPath: tempUrl.path, contents: theme.data(using: .utf8)) { + showShareSheet(items: [tempUrl]) + } + } catch { + AlertManager.shared.showAlertMsg(title: "Error", message: "Error exporting theme: \(error.localizedDescription)") + } + } label: { + Text("Export theme").foregroundColor(theme.colors.primary) + } + Button { + showFileImporter = true + } label: { + Text("Import theme").foregroundColor(theme.colors.primary) + } + } + } +} + +struct ThemeImporter: ViewModifier { + @Binding var isPresented: Bool + var save: (ThemeOverrides) -> Void + + func body(content: Content) -> some View { + content.fileImporter( + isPresented: $isPresented, + allowedContentTypes: [.data/*.plainText*/], + allowsMultipleSelection: false + ) { result in + if case let .success(files) = result, let fileURL = files.first { + do { + var fileSize: Int? = nil + if fileURL.startAccessingSecurityScopedResource() { + let resourceValues = try fileURL.resourceValues(forKeys: [.fileSizeKey]) + fileSize = resourceValues.fileSize + } + if let fileSize = fileSize, + // Same as Android/desktop + fileSize <= 5_500_000 { + if let string = try? String(contentsOf: fileURL, encoding: .utf8), let theme: ThemeOverrides = decodeYAML("themeId: \(UUID().uuidString)\n" + string) { + save(theme) + logger.error("Saved theme from file") + } else { + logger.error("Error decoding theme file") + } + fileURL.stopAccessingSecurityScopedResource() + } else { + fileURL.stopAccessingSecurityScopedResource() + let prettyMaxFileSize = ByteCountFormatter.string(fromByteCount: 5_500_000, countStyle: .binary) + AlertManager.shared.showAlertMsg( + title: "Large file!", + message: "Currently maximum supported file size is \(prettyMaxFileSize)." + ) + } + } catch { + logger.error("Appearance fileImporter error \(error.localizedDescription)") + } + } + } + } +} + +struct UserWallpaperEditorSheet: View { + @Environment(\.dismiss) var dismiss + @EnvironmentObject var theme: AppTheme + @State var userId: Int64 + @State private var globalThemeUsed: Bool = false + + @State private var themes = ChatModel.shared.currentUser?.uiThemes ?? ThemeModeOverrides() + + var body: some View { + let preferred = themes.preferredMode(!theme.colors.isLight) + let initialTheme = preferred ?? ThemeManager.defaultActiveTheme(ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + UserWallpaperEditor( + initialTheme: initialTheme, + themeModeOverride: initialTheme, + applyToMode: themes.light == themes.dark ? nil : initialTheme.mode, + globalThemeUsed: $globalThemeUsed, + save: { applyToMode, newTheme in + updateBackendTask.cancel() + updateBackendTask = Task { + let themes = ChatModel.shared.currentUser?.uiThemes ?? ThemeModeOverrides() + let initialTheme = themes.preferredMode(!theme.colors.isLight) ?? ThemeManager.defaultActiveTheme(ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) + + await save( + applyToMode, + newTheme, + themes, + userId, + realtimeUpdate: + initialTheme.wallpaper?.preset != newTheme?.wallpaper?.preset || + initialTheme.wallpaper?.imageFile != newTheme?.wallpaper?.imageFile || + initialTheme.wallpaper?.scale != newTheme?.wallpaper?.scale || + initialTheme.wallpaper?.scaleType != newTheme?.wallpaper?.scaleType + ) + } + } + ) + .navigationTitle("Profile theme") + .modifier(ThemedBackground(grouped: true)) + .onAppear { + globalThemeUsed = preferred == nil + } + .onChange(of: theme.base.mode) { _ in + globalThemeUsed = (ChatModel.shared.currentUser?.uiThemes ?? ThemeModeOverrides()).preferredMode(!theme.colors.isLight) == nil + } + .onChange(of: ChatModel.shared.currentUser?.userId) { _ in + dismiss() + } + } + + private func save( + _ applyToMode: DefaultThemeMode?, + _ newTheme: ThemeModeOverride?, + _ themes: ThemeModeOverrides?, + _ userId: Int64, + realtimeUpdate: Bool + ) async { + let unchangedThemes: ThemeModeOverrides = themes ?? ThemeModeOverrides() + var wallpaperFiles = Set([unchangedThemes.light?.wallpaper?.imageFile, unchangedThemes.dark?.wallpaper?.imageFile]) + var changedThemes: ThemeModeOverrides? = unchangedThemes + let light: ThemeModeOverride? = if let newTheme { + ThemeModeOverride(mode: DefaultThemeMode.light, colors: newTheme.colors, wallpaper: newTheme.wallpaper?.withFilledWallpaperPath()) + } else { + nil + } + let dark: ThemeModeOverride? = if let newTheme { + ThemeModeOverride(mode: DefaultThemeMode.dark, colors: newTheme.colors, wallpaper: newTheme.wallpaper?.withFilledWallpaperPath()) + } else { + nil + } + + if let applyToMode { + switch applyToMode { + case DefaultThemeMode.light: + changedThemes?.light = light + case DefaultThemeMode.dark: + changedThemes?.dark = dark + } + } else { + changedThemes?.light = light + changedThemes?.dark = dark + } + if changedThemes?.light != nil || changedThemes?.dark != nil { + let light = changedThemes?.light + let dark = changedThemes?.dark + let currentMode = CurrentColors.base.mode + // same image file for both modes, copy image to make them as different files + if var light, var dark, let lightWallpaper = light.wallpaper, let darkWallpaper = dark.wallpaper, let lightImageFile = lightWallpaper.imageFile, let darkImageFile = darkWallpaper.imageFile, lightWallpaper.imageFile == darkWallpaper.imageFile { + let imageFile = if currentMode == DefaultThemeMode.light { + darkImageFile + } else { + lightImageFile + } + let filePath = saveWallpaperFile(url: getWallpaperFilePath(imageFile)) + if currentMode == DefaultThemeMode.light { + dark.wallpaper?.imageFile = filePath + changedThemes = ThemeModeOverrides(light: changedThemes?.light, dark: dark) + } else { + light.wallpaper?.imageFile = filePath + changedThemes = ThemeModeOverrides(light: light, dark: changedThemes?.dark) + } + } + } else { + changedThemes = nil + } + wallpaperFiles.remove(changedThemes?.light?.wallpaper?.imageFile) + wallpaperFiles.remove(changedThemes?.dark?.wallpaper?.imageFile) + wallpaperFiles.forEach(removeWallpaperFile) + + let oldThemes = ChatModel.shared.currentUser?.uiThemes + let changedThemesConstant = changedThemes + if realtimeUpdate { + await MainActor.run { + ChatModel.shared.updateCurrentUserUiThemes(uiThemes: changedThemesConstant) + } + } + do { + try await Task.sleep(nanoseconds: 200_000000) + } catch { + return + } + if !realtimeUpdate { + await MainActor.run { + ChatModel.shared.updateCurrentUserUiThemes(uiThemes: changedThemesConstant) + } + } + + if await !apiSetUserUIThemes(userId: userId, themes: changedThemesConstant) { + await MainActor.run { + // If failed to apply for some reason return the old themes + ChatModel.shared.updateCurrentUserUiThemes(uiThemes: oldThemes) + } + } + } +} + +struct ThemeDestinationPicker: View { + @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme + @Binding var themeUserDestination: (Int64, ThemeModeOverrides?)? + @State var themeUserDest: Int64? + @Binding var customizeThemeIsOpen: Bool + + var body: some View { + let values = [(nil, NSLocalizedString("All profiles", comment: "profile dropdown"))] + m.users.filter { $0.user.activeUser }.map { ($0.user.userId, $0.user.chatViewName)} + + if values.contains(where: { (userId, text) in userId == themeUserDestination?.0 }) { + Picker("Apply to", selection: $themeUserDest) { + ForEach(values, id: \.0) { (_, text) in + Text(text) + } + } + .frame(height: 36) + .onChange(of: themeUserDest) { userId in + themeUserDest = userId + if let userId { + themeUserDestination = (userId, m.users.first { $0.user.userId == userId }?.user.uiThemes) + } else { + themeUserDestination = nil + } + if let userId, userId != m.currentUser?.userId { + changeActiveUser(userId, viewPwd: nil) + } + } + .onChange(of: themeUserDestination == nil) { isNil in + if isNil { + // Easiest way to hide per-user customization. + // Otherwise, it would be needed to make global variable and to use it everywhere for making a decision to include these overrides into active theme constructing or not + m.currentUser?.uiThemes = nil + } else { + m.updateCurrentUserUiThemes(uiThemes: m.users.first(where: { $0.user.userId == m.currentUser?.userId })?.user.uiThemes) + } + } + .onDisappear { + // Skip when Appearance screen is not hidden yet + if customizeThemeIsOpen { return } + // Restore user overrides from stored list of users + m.updateCurrentUserUiThemes(uiThemes: m.users.first(where: { $0.user.userId == m.currentUser?.userId })?.user.uiThemes) + themeUserDestination = if let currentUser = m.currentUser, let uiThemes = currentUser.uiThemes { + (currentUser.userId, uiThemes) + } else { + nil + } + } + } else { + EmptyView() + .onAppear { + themeUserDestination = nil + themeUserDest = nil + } + } + } +} + +struct CustomizeThemeColorsSection: View { + @EnvironmentObject var theme: AppTheme + var editColor: (ThemeColor) -> Binding + + var body: some View { + Section { + picker(.primary, editColor) + picker(.primaryVariant, editColor) + picker(.secondary, editColor) + picker(.secondaryVariant, editColor) + picker(.background, editColor) + picker(.surface, editColor) + //picker(.title, editColor) + picker(.primaryVariant2, editColor) + } header: { + Text("Interface colors") + .foregroundColor(theme.colors.secondary) + } + } +} + +func editColorBinding(name: ThemeColor, wallpaperType: WallpaperType?, wallpaperImage: Image?, theme: AppTheme, onColorChange: @escaping (Color?) -> Void) -> Binding { + Binding(get: { + let baseTheme = theme.base + let wallpaperBackgroundColor = theme.wallpaper.background ?? wallpaperType?.defaultBackgroundColor(baseTheme, theme.colors.background) ?? Color.clear + let wallpaperTintColor = theme.wallpaper.tint ?? wallpaperType?.defaultTintColor(baseTheme) ?? Color.clear + return switch name { + case ThemeColor.wallpaperBackground: wallpaperBackgroundColor + case ThemeColor.wallpaperTint: wallpaperTintColor + case ThemeColor.primary: theme.colors.primary + case ThemeColor.primaryVariant: theme.colors.primaryVariant + case ThemeColor.secondary: theme.colors.secondary + case ThemeColor.secondaryVariant: theme.colors.secondaryVariant + case ThemeColor.background: theme.colors.background + case ThemeColor.surface: theme.colors.surface + case ThemeColor.title: theme.appColors.title + case ThemeColor.primaryVariant2: theme.appColors.primaryVariant2 + case ThemeColor.sentMessage: theme.appColors.sentMessage + case ThemeColor.sentQuote: theme.appColors.sentQuote + case ThemeColor.receivedMessage: theme.appColors.receivedMessage + case ThemeColor.receivedQuote: theme.appColors.receivedQuote + } + }, set: onColorChange) +} + +struct WallpaperSetupView: View { + var wallpaperType: WallpaperType? + var base: DefaultTheme + var initialWallpaper: AppWallpaper? + var editColor: (ThemeColor) -> Binding + var onTypeChange: (WallpaperType?) -> Void + + var body: some View { + if let wallpaperType, case let WallpaperType.image(_, _, scaleType) = wallpaperType { + let wallpaperScaleType = if let scaleType { + scaleType + } else if let initialWallpaper, case let WallpaperType.image(_, _, scaleType) = initialWallpaper.type, let scaleType { + scaleType + } else { + WallpaperScaleType.fill + } + WallpaperScaleTypeChooser(wallpaperScaleType: Binding.constant(wallpaperScaleType), wallpaperType: wallpaperType, onTypeChange: onTypeChange) + } + + + if let wallpaperType, wallpaperType.isPreset { + WallpaperScaleChooser(wallpaperScale: Binding.constant(initialWallpaper?.type.scale ?? 1), wallpaperType: wallpaperType, onTypeChange: onTypeChange) + } else if let wallpaperType, case let WallpaperType.image(_, _, scaleType) = wallpaperType, scaleType == WallpaperScaleType.repeat { + WallpaperScaleChooser(wallpaperScale: Binding.constant(initialWallpaper?.type.scale ?? 1), wallpaperType: wallpaperType, onTypeChange: onTypeChange) + } + + if wallpaperType?.isPreset == true || wallpaperType?.isImage == true { + picker(.wallpaperBackground, editColor) + picker(.wallpaperTint, editColor) + } + + picker(.sentMessage, editColor) + picker(.sentQuote, editColor) + picker(.receivedMessage, editColor) + picker(.receivedQuote, editColor) + + } + + private struct WallpaperScaleChooser: View { + @Binding var wallpaperScale: Float + var wallpaperType: WallpaperType? + var onTypeChange: (WallpaperType?) -> Void + + var body: some View { + HStack { + Text("\(wallpaperScale)".prefix(4)) + .frame(width: 40, height: 36, alignment: .leading) + Slider( + value: Binding(get: { wallpaperScale }, set: { scale in + if let wallpaperType, case let WallpaperType.preset(filename, _) = wallpaperType { + onTypeChange(WallpaperType.preset(filename, Float("\(scale)".prefix(9)))) + } else if let wallpaperType, case let WallpaperType.image(filename, _, scaleType) = wallpaperType { + onTypeChange(WallpaperType.image(filename, Float("\(scale)".prefix(9)), scaleType)) + } + }), + in: 0.5...2, + step: 0.0000001 + ) + .frame(height: 36) + } + } + } + + private struct WallpaperScaleTypeChooser: View { + @Binding var wallpaperScaleType: WallpaperScaleType + var wallpaperType: WallpaperType? + var onTypeChange: (WallpaperType?) -> Void + + var body: some View { + Picker("Scale", selection: Binding(get: { wallpaperScaleType }, set: { scaleType in + if let wallpaperType, case let WallpaperType.image(filename, scale, _) = wallpaperType { + onTypeChange(WallpaperType.image(filename, scale, scaleType)) + } + })) { + ForEach(Array(WallpaperScaleType.allCases), id: \.self) { type in + Text(type.text) + } + } + .frame(height: 36) + } + } +} + +private struct picker: View { + var name: ThemeColor + @State var color: Color + var editColor: (ThemeColor) -> Binding + // Prevent a race between setting a color here and applying externally changed color to the binding + @State private var lastColorUpdate: Date = .now + + init(_ name: ThemeColor, _ editColor: @escaping (ThemeColor) -> Binding) { + self.name = name + self.color = editColor(name).wrappedValue + self.editColor = editColor + } + + var body: some View { + ColorPickerView(name: name, selection: $color) + .onChange(of: color) { newColor in + let editedColor = editColor(name) + if editedColor.wrappedValue != newColor { + editedColor.wrappedValue = newColor + lastColorUpdate = .now + } + } + .onChange(of: editColor(name).wrappedValue) { newValue in + // Allows to update underlying color in the picker when color changed externally, for example, by reseting colors of a theme or changing the theme + if lastColorUpdate < Date.now - 1 && newValue != color { + color = newValue + } + } + } +} + +struct ColorPickerView: View { + var name: ThemeColor + @State var selection: Binding + + var body: some View { + let supportsOpacity = switch name { + case .wallpaperTint: true + case .sentMessage: true + case .sentQuote: true + case .receivedMessage: true + case .receivedQuote: true + default: UIColor(selection.wrappedValue).cgColor.alpha < 1 + } + ColorPicker(name.text, selection: selection, supportsOpacity: supportsOpacity) + } +} + +struct WallpaperImporter: ViewModifier { + @Binding var showImageImporter: Bool + var onChooseImage: (UIImage) -> Void + + func body(content: Content) -> some View { + content.sheet(isPresented: $showImageImporter) { + // LALAL TODO: limit by 5 mb + LibraryMediaListPicker(addMedia: { onChooseImage($0.uiImage) }, selectionLimit: 1, filter: .images, finishedPreprocessing: { }) { itemsSelected in + await MainActor.run { + showImageImporter = false + } + } + } + // content.fileImporter( + // isPresented: $showImageImporter, + // allowedContentTypes: [.image], + // allowsMultipleSelection: false + // ) { result in + // if case let .success(files) = result, let fileURL = files.first { + // do { + // var fileSize: Int? = nil + // if fileURL.startAccessingSecurityScopedResource() { + // let resourceValues = try fileURL.resourceValues(forKeys: [.fileSizeKey]) + // fileSize = resourceValues.fileSize + // } + // fileURL.stopAccessingSecurityScopedResource() + // if let fileSize = fileSize, + // // Same as Android/desktop + // fileSize <= 5_000_000, + // let image = UIImage(contentsOfFile: fileURL.path){ + // onChooseImage(image) + // } else { + // let prettyMaxFileSize = ByteCountFormatter.string(fromByteCount: 5_500_000, countStyle: .binary) + // AlertManager.shared.showAlertMsg( + // title: "Large file!", + // message: "Currently maximum supported file size is \(prettyMaxFileSize)." + // ) + // } + // } catch { + // logger.error("Appearance fileImporter error \(error.localizedDescription)") + // } + // } + // } + } +} + + +/// deprecated. Remove in 2025 func getUIAccentColorDefault() -> CGColor { let defs = UserDefaults.standard return CGColor( @@ -107,15 +1102,78 @@ func getUIAccentColorDefault() -> CGColor { ) } -func setUIAccentColorDefault(_ color: CGColor) { - if let cs = color.components { - let defs = UserDefaults.standard - defs.set(cs[0], forKey: DEFAULT_ACCENT_COLOR_RED) - defs.set(cs[1], forKey: DEFAULT_ACCENT_COLOR_GREEN) - defs.set(cs[2], forKey: DEFAULT_ACCENT_COLOR_BLUE) +private var updateBackendTask: Task = Task {} + +private func saveThemeToDatabase(_ themeUserDestination: (Int64, ThemeModeOverrides?)?) { + let m = ChatModel.shared + let oldThemes = m.currentUser?.uiThemes + if let themeUserDestination { + DispatchQueue.main.async { + // Update before save to make it work seamless + m.updateCurrentUserUiThemes(uiThemes: themeUserDestination.1) + } + } + Task { + if themeUserDestination == nil { + do { + try apiSaveAppSettings(settings: AppSettings.current.prepareForExport()) + } catch { + logger.error("Error saving settings: \(error)") + } + } else if let themeUserDestination, await !apiSetUserUIThemes(userId: themeUserDestination.0, themes: themeUserDestination.1) { + // If failed to apply for some reason return the old themes + m.updateCurrentUserUiThemes(uiThemes: oldThemes) + } } } +private func removeUserThemeModeOverrides(_ themeUserDestination: Binding<(Int64, ThemeModeOverrides?)?>, _ perUserTheme: Binding) { + guard let dest = themeUserDestination.wrappedValue else { return } + perUserTheme.wrappedValue = ThemeModeOverride(mode: CurrentColors.base.mode) + themeUserDestination.wrappedValue = (dest.0, nil) + var wallpaperFilesToDelete: [String] = [] + if let type = ChatModel.shared.currentUser?.uiThemes?.light?.type, case let WallpaperType.image(filename, _, _) = type { + wallpaperFilesToDelete.append(filename) + } + if let type = ChatModel.shared.currentUser?.uiThemes?.dark?.type, case let WallpaperType.image(filename, _, _) = type { + wallpaperFilesToDelete.append(filename) + } + wallpaperFilesToDelete.forEach(removeWallpaperFile) +} + +private func decodeYAML(_ string: String) -> T? { + do { + return try YAMLDecoder().decode(T.self, from: string) + } catch { + logger.error("Error decoding YAML: \(error)") + return nil + } +} + +private func encodeThemeOverrides(_ value: ThemeOverrides) throws -> String { + let encoder = YAMLEncoder() + encoder.options = YAMLEncoder.Options(sequenceStyle: .block, mappingStyle: .block, newLineScalarStyle: .doubleQuoted) + + guard var node = try Yams.compose(yaml: try encoder.encode(value)) else { + throw RuntimeError("Error while composing a node from object") + } + node["base"]?.scalar?.style = .doubleQuoted + + ThemeColors.CodingKeys.allCases.forEach { key in + node["colors"]?[key.stringValue]?.scalar?.style = .doubleQuoted + } + + ThemeWallpaper.CodingKeys.allCases.forEach { key in + if case .scale = key { + // let number be without quotes + } else { + node["wallpaper"]?[key.stringValue]?.scalar?.style = .doubleQuoted + } + } + return try Yams.serialize(node: node) +} + +/// deprecated. Remove in 2025 func getUserInterfaceStyleDefault() -> UIUserInterfaceStyle { switch UserDefaults.standard.integer(forKey: DEFAULT_USER_INTERFACE_STYLE) { case 1: return .light @@ -124,17 +1182,6 @@ func getUserInterfaceStyleDefault() -> UIUserInterfaceStyle { } } -func setUserInterfaceStyleDefault(_ style: UIUserInterfaceStyle) { - var v: Int - switch style { - case .unspecified: v = 0 - case .light: v = 1 - case .dark: v = 2 - default: v = 0 - } - UserDefaults.standard.set(v, forKey: DEFAULT_USER_INTERFACE_STYLE) -} - struct AppearanceSettings_Previews: PreviewProvider { static var previews: some View { AppearanceSettings() diff --git a/apps/ios/Shared/Views/UserSettings/CallSettings.swift b/apps/ios/Shared/Views/UserSettings/CallSettings.swift index 3409e7ab0e..bae343ee88 100644 --- a/apps/ios/Shared/Views/UserSettings/CallSettings.swift +++ b/apps/ios/Shared/Views/UserSettings/CallSettings.swift @@ -10,6 +10,7 @@ import SwiftUI import SimpleXChat struct CallSettings: View { + @EnvironmentObject var theme: AppTheme @AppStorage(DEFAULT_WEBRTC_POLICY_RELAY) private var webrtcPolicyRelay = true @AppStorage(GROUP_DEFAULT_CALL_KIT_ENABLED, store: groupDefaults) private var callKitEnabled = true @AppStorage(DEFAULT_CALL_KIT_CALLS_IN_RECENTS) private var callKitCallsInRecents = false @@ -22,17 +23,21 @@ struct CallSettings: View { NavigationLink { RTCServers() .navigationTitle("Your ICE servers") + .modifier(ThemedBackground(grouped: true)) } label: { Text("WebRTC ICE servers") } Toggle("Always use relay", isOn: $webrtcPolicyRelay) } header: { Text("Settings") + .foregroundColor(theme.colors.secondary) } footer: { if webrtcPolicyRelay { Text("Relay server protects your IP address, but it can observe the duration of the call.") + .foregroundColor(theme.colors.secondary) } else { Text("Relay server is only used if necessary. Another party can observe your IP address.") + .foregroundColor(theme.colors.secondary) } } @@ -46,6 +51,7 @@ struct CallSettings: View { } } header: { Text("Interface") + .foregroundColor(theme.colors.secondary) } footer: { if callKitEnabled { Text("You can accept calls from lock screen, without device and app authentication.") @@ -55,7 +61,7 @@ struct CallSettings: View { } } - Section("Limitations") { + Section(header: Text("Limitations").foregroundColor(theme.colors.secondary)) { VStack(alignment: .leading, spacing: 8) { textListItem("1.", "Do NOT use SimpleX for emergency calls.") textListItem("2.", "Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions.") diff --git a/apps/ios/Shared/Views/UserSettings/DeveloperView.swift b/apps/ios/Shared/Views/UserSettings/DeveloperView.swift index 9b11c6d0f7..54454b7cef 100644 --- a/apps/ios/Shared/Views/UserSettings/DeveloperView.swift +++ b/apps/ios/Shared/Views/UserSettings/DeveloperView.swift @@ -10,9 +10,11 @@ import SwiftUI import SimpleXChat struct DeveloperView: View { + @EnvironmentObject var theme: AppTheme @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false @AppStorage(GROUP_DEFAULT_CONFIRM_DB_UPGRADES, store: groupDefaults) private var confirmDatabaseUpgrades = false - @AppStorage(GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED, store: groupDefaults) private var pqExperimentalEnabled = false + @State private var hintsUnchanged = hintDefaultsUnchanged() + @Environment(\.colorScheme) var colorScheme var body: some View { @@ -24,51 +26,62 @@ struct DeveloperView: View { .resizable() .frame(width: 24, height: 24) .opacity(0.5) + .colorMultiply(theme.colors.secondary) Text("Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)") .padding(.leading, 36) } NavigationLink { TerminalView() } label: { - settingsRow("terminal") { Text("Chat console") } + settingsRow("terminal", color: theme.colors.secondary) { Text("Chat console") } } - settingsRow("internaldrive") { - Toggle("Confirm database upgrades", isOn: $confirmDatabaseUpgrades) + settingsRow("lightbulb.max", color: theme.colors.secondary) { + Button("Reset all hints", action: resetHintDefaults) + .disabled(hintsUnchanged) } - settingsRow("chevron.left.forwardslash.chevron.right") { + settingsRow("chevron.left.forwardslash.chevron.right", color: theme.colors.secondary) { Toggle("Show developer options", isOn: $developerTools) } } header: { Text("") } footer: { - (developerTools ? Text("Show:") : Text("Hide:")) + Text(" ") + Text("Database IDs and Transport isolation option.") + ((developerTools ? Text("Show:") : Text("Hide:")) + textSpace + Text("Database IDs and Transport isolation option.")) + .foregroundColor(theme.colors.secondary) } - + if developerTools { Section { - settingsRow("key") { - Toggle("Post-quantum E2EE", isOn: $pqExperimentalEnabled) - .onChange(of: pqExperimentalEnabled) { - setPQExperimentalEnabled($0) - } + settingsRow("internaldrive", color: theme.colors.secondary) { + Toggle("Confirm database upgrades", isOn: $confirmDatabaseUpgrades) + } + NavigationLink { + StorageView() + .navigationTitle("Storage") + .navigationBarTitleDisplayMode(.large) + } label: { + settingsRow("internaldrive", color: theme.colors.secondary) { Text("Storage") } } } header: { - Text(String("Experimental")) - } footer: { - Text(String("In this version applies only to new contacts.")) + Text("Developer options") } } } } } - - private func setPQExperimentalEnabled(_ enable: Bool) { - do { - try apiSetPQEncryption(enable) - } catch let error { - let err = responseError(error) - logger.error("apiSetPQEncryption \(err)") + + private func resetHintDefaults() { + for def in hintDefaults { + if let val = appDefaults[def] as? Bool { + UserDefaults.standard.set(val, forKey: def) + } } + hintsUnchanged = true + } +} + +private func hintDefaultsUnchanged() -> Bool { + hintDefaults.allSatisfy { def in + appDefaults[def] as? Bool == UserDefaults.standard.bool(forKey: def) } } diff --git a/apps/ios/Shared/Views/UserSettings/HiddenProfileView.swift b/apps/ios/Shared/Views/UserSettings/HiddenProfileView.swift index 509874619f..5f20055b2b 100644 --- a/apps/ios/Shared/Views/UserSettings/HiddenProfileView.swift +++ b/apps/ios/Shared/Views/UserSettings/HiddenProfileView.swift @@ -13,6 +13,7 @@ struct HiddenProfileView: View { @State var user: User @Binding var profileHidden: Bool @EnvironmentObject private var m: ChatModel + @EnvironmentObject var theme: AppTheme @Environment(\.dismiss) var dismiss: DismissAction @State private var hidePassword = "" @State private var confirmHidePassword = "" @@ -36,7 +37,7 @@ struct HiddenProfileView: View { PassphraseField(key: $hidePassword, placeholder: "Password to show", valid: passwordValid, showStrength: true) PassphraseField(key: $confirmHidePassword, placeholder: "Confirm password", valid: confirmValid) - settingsRow("lock") { + settingsRow("lock", color: theme.colors.secondary) { Button("Save profile password") { Task { do { @@ -58,8 +59,10 @@ struct HiddenProfileView: View { .disabled(saveDisabled) } header: { Text("Hidden profile password") + .foregroundColor(theme.colors.secondary) } footer: { Text("To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page.") + .foregroundColor(theme.colors.secondary) .font(.body) .padding(.top, 8) } @@ -70,6 +73,7 @@ struct HiddenProfileView: View { message: Text(savePasswordError ?? "") ) } + .modifier(ThemedBackground(grouped: true)) } var passwordValid: Bool { hidePassword == hidePassword.trimmingCharacters(in: .whitespaces) } diff --git a/apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift b/apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift index fc478596a9..d9862aaac8 100644 --- a/apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift +++ b/apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift @@ -26,7 +26,9 @@ struct IncognitoHelp: View { Text("Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).") } .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) } + .modifier(ThemedBackground()) } } diff --git a/apps/ios/Shared/Views/UserSettings/MarkdownHelp.swift b/apps/ios/Shared/Views/UserSettings/MarkdownHelp.swift index afb0af66c1..71c284e9ab 100644 --- a/apps/ios/Shared/Views/UserSettings/MarkdownHelp.swift +++ b/apps/ios/Shared/Views/UserSettings/MarkdownHelp.swift @@ -9,6 +9,8 @@ import SwiftUI struct MarkdownHelp: View { + @EnvironmentObject var theme: AppTheme + var body: some View { VStack(alignment: .leading, spacing: 8) { Text("You can use markdown to format messages:") @@ -17,11 +19,11 @@ struct MarkdownHelp: View { mdFormat("_italic_", Text("italic").italic()) mdFormat("~strike~", Text("strike").strikethrough()) mdFormat("`a + b`", Text("`a + b`").font(.body.monospaced())) - mdFormat("!1 colored!", Text("colored").foregroundColor(.red) + Text(" (") + color("1", .red) + color("2", .green) + color("3", .blue) + color("4", .yellow) + color("5", .cyan) + Text("6").foregroundColor(.purple) + Text(")")) + mdFormat("!1 colored!", Text("colored").foregroundColor(.red) + Text(verbatim: " (") + color("1", .red) + color("2", .green) + color("3", .blue) + color("4", .yellow) + color("5", .cyan) + Text("6").foregroundColor(.purple) + Text(verbatim: ")")) ( mdFormat("#secret#", Text("secret") .foregroundColor(.clear) - .underline(color: .primary) + Text(" (can be copied)")) + .underline(color: theme.colors.onBackground) + Text(" (can be copied)")) ) .textSelection(.enabled) } @@ -37,7 +39,7 @@ private func mdFormat(_ format: LocalizedStringKey, _ example: Text) -> some Vie } private func color(_ s: String, _ c: Color) -> Text { - Text(s).foregroundColor(c) + Text(", ") + Text(s).foregroundColor(c) + Text(verbatim: ", ") } struct MarkdownHelp_Previews: PreviewProvider { diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers.swift deleted file mode 100644 index d721cfad50..0000000000 --- a/apps/ios/Shared/Views/UserSettings/NetworkAndServers.swift +++ /dev/null @@ -1,182 +0,0 @@ -// -// NetworkServersView.swift -// SimpleX (iOS) -// -// Created by Evgeny on 02/08/2022. -// Copyright © 2022 SimpleX Chat. All rights reserved. -// - -import SwiftUI -import SimpleXChat - -private enum NetworkAlert: Identifiable { - case updateOnionHosts(hosts: OnionHosts) - case updateSessionMode(mode: TransportSessionMode) - case error(err: String) - - var id: String { - switch self { - case let .updateOnionHosts(hosts): return "updateOnionHosts \(hosts)" - case let .updateSessionMode(mode): return "updateSessionMode \(mode)" - case let .error(err): return "error \(err)" - } - } -} - -struct NetworkAndServers: View { - @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false - @State private var cfgLoaded = false - @State private var currentNetCfg = NetCfg.defaults - @State private var netCfg = NetCfg.defaults - @State private var onionHosts: OnionHosts = .no - @State private var sessionMode: TransportSessionMode = .user - @State private var alert: NetworkAlert? - - var body: some View { - VStack { - List { - Section { - NavigationLink { - ProtocolServersView(serverProtocol: .smp) - .navigationTitle("Your SMP servers") - } label: { - Text("SMP servers") - } - - NavigationLink { - ProtocolServersView(serverProtocol: .xftp) - .navigationTitle("Your XFTP servers") - } label: { - Text("XFTP servers") - } - - Picker("Use .onion hosts", selection: $onionHosts) { - ForEach(OnionHosts.values, id: \.self) { Text($0.text) } - } - .frame(height: 36) - - if developerTools { - Picker("Transport isolation", selection: $sessionMode) { - ForEach(TransportSessionMode.values, id: \.self) { Text($0.text) } - } - .frame(height: 36) - } - - NavigationLink { - AdvancedNetworkSettings() - .navigationTitle("Network settings") - } label: { - Text("Advanced network settings") - } - } header: { - Text("Messages & files") - } footer: { - Text("Using .onion hosts requires compatible VPN provider.") - } - - Section("Calls") { - NavigationLink { - RTCServers() - .navigationTitle("Your ICE servers") - } label: { - Text("WebRTC ICE servers") - } - } - } - } - .onAppear { - if cfgLoaded { return } - cfgLoaded = true - currentNetCfg = getNetCfg() - resetNetCfgView() - } - .onChange(of: onionHosts) { _ in - if onionHosts != OnionHosts(netCfg: currentNetCfg) { - alert = .updateOnionHosts(hosts: onionHosts) - } - } - .onChange(of: sessionMode) { _ in - if sessionMode != netCfg.sessionMode { - alert = .updateSessionMode(mode: sessionMode) - } - } - .alert(item: $alert) { a in - switch a { - case let .updateOnionHosts(hosts): - return Alert( - title: Text("Update .onion hosts setting?"), - message: Text(onionHostsInfo(hosts)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."), - primaryButton: .default(Text("Ok")) { - let (hostMode, requiredHostMode) = hosts.hostMode - netCfg.hostMode = hostMode - netCfg.requiredHostMode = requiredHostMode - saveNetCfg() - }, - secondaryButton: .cancel() { - resetNetCfgView() - } - ) - case let .updateSessionMode(mode): - return Alert( - title: Text("Update transport isolation mode?"), - message: Text(sessionModeInfo(mode)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."), - primaryButton: .default(Text("Ok")) { - netCfg.sessionMode = mode - saveNetCfg() - }, - secondaryButton: .cancel() { - resetNetCfgView() - } - ) - case let .error(err): - return Alert( - title: Text("Error updating settings"), - message: Text(err) - ) - } - } - } - - private func saveNetCfg() { - do { - let def = netCfg.hostMode == .onionHost ? NetCfg.proxyDefaults : NetCfg.defaults - netCfg.tcpConnectTimeout = def.tcpConnectTimeout - netCfg.tcpTimeout = def.tcpTimeout - try setNetworkConfig(netCfg) - currentNetCfg = netCfg - setNetCfg(netCfg) - } catch let error { - let err = responseError(error) - resetNetCfgView() - alert = .error(err: err) - logger.error("\(err)") - } - } - - private func resetNetCfgView() { - netCfg = currentNetCfg - onionHosts = OnionHosts(netCfg: netCfg) - sessionMode = netCfg.sessionMode - } - - private func onionHostsInfo(_ hosts: OnionHosts) -> LocalizedStringKey { - switch hosts { - case .no: return "Onion hosts will not be used." - case .prefer: return "Onion hosts will be used when available. Requires enabling VPN." - case .require: return "Onion hosts will be required for connection. Requires enabling VPN." - } - } - - private func sessionModeInfo(_ mode: TransportSessionMode) -> LocalizedStringKey { - switch mode { - case .user: return "A separate TCP connection will be used **for each chat profile you have in the app**." - case .entity: return "A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." - } - } -} - -struct NetworkServersView_Previews: PreviewProvider { - static var previews: some View { - NetworkAndServers() - } -} diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/AdvancedNetworkSettings.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/AdvancedNetworkSettings.swift new file mode 100644 index 0000000000..fa698f8b7c --- /dev/null +++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/AdvancedNetworkSettings.swift @@ -0,0 +1,404 @@ +// +// AdvancedNetworkSettings.swift +// SimpleX (iOS) +// +// Created by Evgeny on 02/08/2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +private let secondsLabel = NSLocalizedString("sec", comment: "network option") + +enum NetworkSettingsAlert: Identifiable { + case update + case error(err: String) + + var id: String { + switch self { + case .update: return "update" + case let .error(err): return "error \(err)" + } + } +} + +struct AdvancedNetworkSettings: View { + @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var theme: AppTheme + @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false + @AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false + @State private var netCfg = NetCfg.defaults + @State private var currentNetCfg = NetCfg.defaults + @State private var cfgLoaded = false + @State private var enableKeepAlive = true + @State private var keepAliveOpts = KeepAliveOpts.defaults + @State private var showSettingsAlert: NetworkSettingsAlert? + @State private var onionHosts: OnionHosts = .no + @State private var showSaveDialog = false + @State private var netProxy = networkProxyDefault.get() + @State private var currentNetProxy = networkProxyDefault.get() + @State private var useNetProxy = false + @State private var netProxyAuth = false + + var body: some View { + VStack { + List { + Section { + NavigationLink { + List { + Section { + SelectionListView(list: SMPProxyMode.values, selection: $netCfg.smpProxyMode) { mode in + netCfg.smpProxyMode = mode + } + } footer: { + Text(proxyModeInfo(netCfg.smpProxyMode)) + .font(.callout) + .foregroundColor(theme.colors.secondary) + } + } + .navigationTitle("Private routing") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.inline) + } label: { + HStack { + Text("Private routing") + Spacer() + Text(netCfg.smpProxyMode.label) + } + } + + NavigationLink { + List { + Section { + SelectionListView(list: SMPProxyFallback.values, selection: $netCfg.smpProxyFallback) { mode in + netCfg.smpProxyFallback = mode + } + .disabled(netCfg.smpProxyMode == .never) + } footer: { + Text(proxyFallbackInfo(netCfg.smpProxyFallback)) + .font(.callout) + .foregroundColor(theme.colors.secondary) + } + } + .navigationTitle("Allow downgrade") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.inline) + } label: { + HStack { + Text("Allow downgrade") + Spacer() + Text(netCfg.smpProxyFallback.label) + } + } + + Toggle("Show message status", isOn: $showSentViaProxy) + } header: { + Text("Private message routing") + .foregroundColor(theme.colors.secondary) + } footer: { + VStack(alignment: .leading) { + Text("To protect your IP address, private routing uses your SMP servers to deliver messages.") + if showSentViaProxy { + Text("Show → on messages sent via private routing.") + } + } + .foregroundColor(theme.colors.secondary) + } + + Section { + Toggle("Use SOCKS proxy", isOn: $useNetProxy) + Group { + TextField("IP address", text: $netProxy.host) + TextField( + "Port", + text: Binding( + get: { netProxy.port > 0 ? "\(netProxy.port)" : "" }, + set: { s in + netProxy.port = if let port = Int(s), port > 0 { + port + } else { + 0 + } + } + ) + ) + Toggle("Proxy requires password", isOn: $netProxyAuth) + if netProxyAuth { + TextField("Username", text: $netProxy.username) + PassphraseField( + key: $netProxy.password, + placeholder: "Password", + valid: NetworkProxy.validCredential(netProxy.password) + ) + } + } + .if(!useNetProxy) { $0.foregroundColor(theme.colors.secondary) } + .disabled(!useNetProxy) + } header: { + HStack { + Text("SOCKS proxy").foregroundColor(theme.colors.secondary) + if useNetProxy && !netProxy.valid { + Spacer() + Image(systemName: "exclamationmark.circle.fill").foregroundColor(.red) + } + } + } footer: { + if netProxyAuth { + Text("Your credentials may be sent unencrypted.") + .foregroundColor(theme.colors.secondary) + } else { + Text("Do not use credentials with proxy.") + .foregroundColor(theme.colors.secondary) + } + } + .onChange(of: useNetProxy) { useNetProxy in + netCfg.socksProxy = useNetProxy && currentNetProxy.valid + ? currentNetProxy.toProxyString() + : nil + netProxy = currentNetProxy + netProxyAuth = netProxy.username != "" || netProxy.password != "" + } + .onChange(of: netProxyAuth) { netProxyAuth in + if netProxyAuth { + netProxy.auth = currentNetProxy.auth + netProxy.username = currentNetProxy.username + netProxy.password = currentNetProxy.password + } else { + netProxy.auth = .username + netProxy.username = "" + netProxy.password = "" + } + } + .onChange(of: netProxy) { netProxy in + netCfg.socksProxy = useNetProxy && netProxy.valid + ? netProxy.toProxyString() + : nil + } + + Section { + Picker("Use .onion hosts", selection: $onionHosts) { + ForEach(OnionHosts.values, id: \.self) { Text($0.text) } + } + .frame(height: 36) + } footer: { + Text(onionHostsInfo(onionHosts)) + .foregroundColor(theme.colors.secondary) + } + .onChange(of: onionHosts) { hosts in + if hosts != OnionHosts(netCfg: currentNetCfg) { + let (hostMode, requiredHostMode) = hosts.hostMode + netCfg.hostMode = hostMode + netCfg.requiredHostMode = requiredHostMode + } + } + + if developerTools { + Section { + Picker("Transport isolation", selection: $netCfg.sessionMode) { + let modes = TransportSessionMode.values.contains(netCfg.sessionMode) + ? TransportSessionMode.values + : TransportSessionMode.values + [netCfg.sessionMode] + ForEach(modes, id: \.self) { Text($0.text) } + } + .frame(height: 36) + } footer: { + sessionModeInfo(netCfg.sessionMode) + .foregroundColor(theme.colors.secondary) + } + } + + Section { + Picker("Use web port", selection: $netCfg.smpWebPortServers) { + ForEach(SMPWebPortServers.allCases, id: \.self) { Text($0.text) } + } + .frame(height: 36) + } header: { + Text("TCP port for messaging") + } footer: { + netCfg.smpWebPortServers == .preset + ? Text("Use TCP port 443 for preset servers only.") + : Text("Use TCP port \(netCfg.smpWebPortServers == .all ? "443" : "5223") when no port is specified.") + } + + Section("TCP connection") { + timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [10_000000, 15_000000, 20_000000, 30_000000, 45_000000, 60_000000, 90_000000], label: secondsLabel) + timeoutSettingPicker("Protocol timeout", selection: $netCfg.tcpTimeout, values: [5_000000, 7_000000, 10_000000, 15_000000, 20_000000, 30_000000], label: secondsLabel) + timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [2_500, 5_000, 10_000, 15_000, 20_000, 30_000], label: secondsLabel) + // intSettingPicker("Receiving concurrency", selection: $netCfg.rcvConcurrency, values: [1, 2, 4, 8, 12, 16, 24], label: "") + timeoutSettingPicker("PING interval", selection: $netCfg.smpPingInterval, values: [120_000000, 300_000000, 600_000000, 1200_000000, 2400_000000, 3600_000000], label: secondsLabel) + intSettingPicker("PING count", selection: $netCfg.smpPingCount, values: [1, 2, 3, 5, 8], label: "") + Toggle("Enable TCP keep-alive", isOn: $enableKeepAlive) + + if enableKeepAlive { + intSettingPicker("TCP_KEEPIDLE", selection: $keepAliveOpts.keepIdle, values: [15, 30, 60, 120, 180], label: secondsLabel) + intSettingPicker("TCP_KEEPINTVL", selection: $keepAliveOpts.keepIntvl, values: [5, 10, 15, 30, 60], label: secondsLabel) + intSettingPicker("TCP_KEEPCNT", selection: $keepAliveOpts.keepCnt, values: [1, 2, 4, 6, 8], label: "") + } else { + Group { + Text("TCP_KEEPIDLE") + Text("TCP_KEEPINTVL") + Text("TCP_KEEPCNT") + } + .foregroundColor(theme.colors.secondary) + } + } + + Section { + Button("Reset to defaults") { + updateNetCfgView(NetCfg.defaults, NetworkProxy.def) + } + .disabled(netCfg == NetCfg.defaults) + + Button("Set timeouts for proxy/VPN") { + updateNetCfgView(netCfg.withProxyTimeouts, netProxy) + } + .disabled(netCfg.hasProxyTimeouts) + + Button("Save and reconnect") { + showSettingsAlert = .update + } + .disabled(netCfg == currentNetCfg || (useNetProxy && !netProxy.valid)) + } + } + } + .onChange(of: keepAliveOpts) { opts in + netCfg.tcpKeepAlive = keepAliveOpts + } + .onChange(of: enableKeepAlive) { on in + netCfg.tcpKeepAlive = on ? (currentNetCfg.tcpKeepAlive ?? KeepAliveOpts.defaults) : nil + } + .onAppear { + if cfgLoaded { return } + cfgLoaded = true + currentNetCfg = getNetCfg() + currentNetProxy = networkProxyDefault.get() + updateNetCfgView(currentNetCfg, currentNetProxy) + } + .alert(item: $showSettingsAlert) { a in + switch a { + case .update: + return Alert( + title: Text("Update settings?"), + message: Text("Updating settings will re-connect the client to all servers."), + primaryButton: .default(Text("Ok")) { + _ = saveNetCfg() + }, + secondaryButton: .cancel() + ) + case let .error(err): + return Alert( + title: Text("Error updating settings"), + message: Text(err) + ) + } + } + .modifier(BackButton(disabled: Binding.constant(false)) { + if netCfg == currentNetCfg { + dismiss() + cfgLoaded = false + } else if !useNetProxy || netProxy.valid { + showSaveDialog = true + } + }) + .confirmationDialog("Update network settings?", isPresented: $showSaveDialog, titleVisibility: .visible) { + Button("Save and reconnect") { + if saveNetCfg() { + dismiss() + cfgLoaded = false + } + } + Button("Exit without saving") { dismiss() } + } + } + + private func updateNetCfgView(_ cfg: NetCfg, _ proxy: NetworkProxy) { + netCfg = cfg + netProxy = proxy + onionHosts = OnionHosts(netCfg: netCfg) + enableKeepAlive = netCfg.enableKeepAlive + keepAliveOpts = netCfg.tcpKeepAlive ?? KeepAliveOpts.defaults + useNetProxy = netCfg.socksProxy != nil + netProxyAuth = switch netProxy.auth { + case .username: netProxy.username != "" || netProxy.password != "" + case .isolate: false + } + } + + private func saveNetCfg() -> Bool { + do { + try setNetworkConfig(netCfg) + currentNetCfg = netCfg + setNetCfg(netCfg, networkProxy: useNetProxy ? netProxy : nil) + currentNetProxy = netProxy + networkProxyDefault.set(netProxy) + return true + } catch let error { + let err = responseError(error) + showSettingsAlert = .error(err: err) + logger.error("\(err)") + return false + } + } + + private func intSettingPicker(_ title: LocalizedStringKey, selection: Binding, values: [Int], label: String) -> some View { + Picker(title, selection: selection) { + ForEach(values, id: \.self) { value in + Text("\(value) \(label)") + } + } + .frame(height: 36) + } + + private func timeoutSettingPicker(_ title: LocalizedStringKey, selection: Binding, values: [Int], label: String) -> some View { + Picker(title, selection: selection) { + let v = selection.wrappedValue + let vs = values.contains(v) ? values : values + [v] + ForEach(vs, id: \.self) { value in + Text("\(String(format: "%g", (Double(value) / 1000000))) \(secondsLabel)") + } + } + .frame(height: 36) + } + + private func onionHostsInfo(_ hosts: OnionHosts) -> LocalizedStringKey { + switch hosts { + case .no: return "Onion hosts will not be used." + case .prefer: return "Onion hosts will be used when available.\nRequires compatible VPN." + case .require: return "Onion hosts will be **required** for connection.\nRequires compatible VPN." + } + } + + private func sessionModeInfo(_ mode: TransportSessionMode) -> Text { + let userMode = Text("A separate TCP connection will be used **for each chat profile you have in the app**.") + return switch mode { + case .user: userMode + case .session: userMode + textNewLine + Text("New SOCKS credentials will be used every time you start the app.") + case .server: userMode + textNewLine + Text("New SOCKS credentials will be used for each server.") + case .entity: Text("A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail.") + } + } + + private func proxyModeInfo(_ mode: SMPProxyMode) -> LocalizedStringKey { + switch mode { + case .always: return "Always use private routing." + case .unknown: return "Use private routing with unknown servers." + case .unprotected: return "Use private routing with unknown servers when IP address is not protected." + case .never: return "Do NOT use private routing." + } + } + + private func proxyFallbackInfo(_ proxyFallback: SMPProxyFallback) -> LocalizedStringKey { + switch proxyFallback { + case .allow: return "Send messages directly when your or destination server does not support private routing." + case .allowProtected: return "Send messages directly when IP address is protected and your or destination server does not support private routing." + case .prohibit: return "Do NOT send messages directly, even if your or destination server does not support private routing." + } + } +} + +struct AdvancedNetworkSettings_Previews: PreviewProvider { + static var previews: some View { + AdvancedNetworkSettings() + } +} diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ConditionsWebView.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ConditionsWebView.swift new file mode 100644 index 0000000000..1e38b7d5ec --- /dev/null +++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ConditionsWebView.swift @@ -0,0 +1,83 @@ +// +// ConditionsWebView.swift +// SimpleX (iOS) +// +// Created by Stanislav Dmitrenko on 26.11.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import WebKit + +struct ConditionsWebView: UIViewRepresentable { + @State var html: String + @EnvironmentObject var theme: AppTheme + @State var pageLoaded = false + + func makeUIView(context: Context) -> WKWebView { + let view = WKWebView() + view.backgroundColor = .clear + view.isOpaque = false + view.navigationDelegate = context.coordinator + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + // just to make sure that even if updateUIView will not be called for any reason, the page + // will be rendered anyway + if !pageLoaded { + loadPage(view) + } + } + return view + } + + func updateUIView(_ view: WKWebView, context: Context) { + loadPage(view) + } + + private func loadPage(_ webView: WKWebView) { + let styles = """ + + """ + let head = "\(styles)" + webView.loadHTMLString(head + html, baseURL: nil) + DispatchQueue.main.async { + pageLoaded = true + } + } + + func makeCoordinator() -> Cordinator { + Cordinator() + } + + class Cordinator: NSObject, WKNavigationDelegate { + func webView(_ webView: WKWebView, + decidePolicyFor navigationAction: WKNavigationAction, + decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + + guard let url = navigationAction.request.url else { return decisionHandler(.allow) } + + switch navigationAction.navigationType { + case .linkActivated: + decisionHandler(.cancel) + if url.absoluteString.starts(with: "https://simplex.chat/contact#") { + ChatModel.shared.appOpenUrl = url + } else { + UIApplication.shared.open(url) + } + default: + decisionHandler(.allow) + } + } + } +} diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/NetworkAndServers.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/NetworkAndServers.swift new file mode 100644 index 0000000000..6f4710396a --- /dev/null +++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/NetworkAndServers.swift @@ -0,0 +1,480 @@ +// +// NetworkServersView.swift +// SimpleX (iOS) +// +// Created by Evgeny on 02/08/2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +private enum NetworkAlert: Identifiable { + case error(err: String) + + var id: String { + switch self { + case let .error(err): return "error \(err)" + } + } +} + +private enum NetworkAndServersSheet: Identifiable { + case showConditions + + var id: String { + switch self { + case .showConditions: return "showConditions" + } + } +} + +struct NetworkAndServers: View { + @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var m: ChatModel + @Environment(\.colorScheme) var colorScheme: ColorScheme + @EnvironmentObject var theme: AppTheme + @EnvironmentObject var ss: SaveableSettings + @State private var sheetItem: NetworkAndServersSheet? = nil + @State private var justOpened = true + @State private var showSaveDialog = false + + var body: some View { + VStack { + List { + let conditionsAction = m.conditions.conditionsAction + let anyOperatorEnabled = ss.servers.userServers.contains(where: { $0.operator?.enabled ?? false }) + Section { + ForEach(ss.servers.userServers.enumerated().map { $0 }, id: \.element.id) { idx, userOperatorServers in + if let serverOperator = userOperatorServers.operator { + serverOperatorView(idx, serverOperator) + } else { + EmptyView() + } + } + + if let conditionsAction = conditionsAction, anyOperatorEnabled { + conditionsButton(conditionsAction) + } + } header: { + Text("Preset servers") + .foregroundColor(theme.colors.secondary) + } footer: { + switch conditionsAction { + case let .review(_, deadline, _): + if let deadline = deadline, anyOperatorEnabled { + Text("Conditions will be accepted on: \(conditionsTimestamp(deadline)).") + .foregroundColor(theme.colors.secondary) + } + default: + EmptyView() + } + } + + Section { + if let idx = ss.servers.userServers.firstIndex(where: { $0.operator == nil }) { + NavigationLink { + YourServersView( + userServers: $ss.servers.userServers, + serverErrors: $ss.servers.serverErrors, + operatorIndex: idx + ) + .navigationTitle("Your servers") + .modifier(ThemedBackground(grouped: true)) + } label: { + HStack { + Text("Your servers") + + if ss.servers.userServers[idx] != ss.servers.currUserServers[idx] { + Spacer() + unsavedChangesIndicator() + } + } + } + } + + NavigationLink { + AdvancedNetworkSettings() + .navigationTitle("Advanced settings") + .modifier(ThemedBackground(grouped: true)) + } label: { + Text("Advanced network settings") + } + } header: { + Text("Messages & files") + .foregroundColor(theme.colors.secondary) + } + + Section { + Button("Save servers", action: { saveServers($ss.servers.currUserServers, $ss.servers.userServers) }) + .disabled(!serversCanBeSaved(ss.servers.currUserServers, ss.servers.userServers, ss.servers.serverErrors)) + } footer: { + if let errStr = globalServersError(ss.servers.serverErrors) { + ServersErrorView(errStr: errStr) + } else if !ss.servers.serverErrors.isEmpty { + ServersErrorView(errStr: NSLocalizedString("Errors in servers configuration.", comment: "servers error")) + } + } + + Section(header: Text("Calls").foregroundColor(theme.colors.secondary)) { + NavigationLink { + RTCServers() + .navigationTitle("Your ICE servers") + .modifier(ThemedBackground(grouped: true)) + } label: { + Text("WebRTC ICE servers") + } + } + + Section(header: Text("Network connection").foregroundColor(theme.colors.secondary)) { + HStack { + Text(m.networkInfo.networkType.text) + Spacer() + Image(systemName: "circle.fill").foregroundColor(m.networkInfo.online ? .green : .red) + } + } + } + } + .task { + // this condition is needed to prevent re-setting the servers when exiting single server view + if justOpened { + do { + ss.servers.currUserServers = try await getUserServers() + ss.servers.userServers = ss.servers.currUserServers + ss.servers.serverErrors = [] + } catch let error { + await MainActor.run { + showAlert( + NSLocalizedString("Error loading servers", comment: "alert title"), + message: responseError(error) + ) + } + } + justOpened = false + } + } + .modifier(BackButton(disabled: Binding.constant(false)) { + if serversCanBeSaved(ss.servers.currUserServers, ss.servers.userServers, ss.servers.serverErrors) { + showSaveDialog = true + } else { + dismiss() + } + }) + .confirmationDialog("Save servers?", isPresented: $showSaveDialog, titleVisibility: .visible) { + Button("Save") { + saveServers($ss.servers.currUserServers, $ss.servers.userServers) + dismiss() + } + Button("Exit without saving") { dismiss() } + } + .sheet(item: $sheetItem) { item in + switch item { + case .showConditions: + UsageConditionsView( + currUserServers: $ss.servers.currUserServers, + userServers: $ss.servers.userServers + ) + .modifier(ThemedBackground(grouped: true)) + } + } + } + + private func serverOperatorView(_ operatorIndex: Int, _ serverOperator: ServerOperator) -> some View { + NavigationLink() { + OperatorView( + currUserServers: $ss.servers.currUserServers, + userServers: $ss.servers.userServers, + serverErrors: $ss.servers.serverErrors, + operatorIndex: operatorIndex, + useOperator: serverOperator.enabled + ) + .navigationBarTitle("\(serverOperator.tradeName) servers") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.large) + } label: { + HStack { + Image(serverOperator.logo(colorScheme)) + .resizable() + .scaledToFit() + .grayscale(serverOperator.enabled ? 0.0 : 1.0) + .frame(width: 24, height: 24) + Text(serverOperator.tradeName) + .foregroundColor(serverOperator.enabled ? theme.colors.onBackground : theme.colors.secondary) + + if ss.servers.userServers[operatorIndex] != ss.servers.currUserServers[operatorIndex] { + Spacer() + unsavedChangesIndicator() + } + } + } + } + + private func unsavedChangesIndicator() -> some View { + Image(systemName: "pencil") + .foregroundColor(theme.colors.secondary) + .symbolRenderingMode(.monochrome) + .frame(maxWidth: 24, maxHeight: 24, alignment: .center) + } + + private func conditionsButton(_ conditionsAction: UsageConditionsAction) -> some View { + Button { + sheetItem = .showConditions + } label: { + switch conditionsAction { + case .review: + Text("Review conditions") + case .accepted: + Text("Accepted conditions") + } + } + } +} + +struct UsageConditionsView: View { + @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var theme: AppTheme + @Binding var currUserServers: [UserOperatorServers] + @Binding var userServers: [UserOperatorServers] + + var body: some View { + VStack(alignment: .leading, spacing: 20) { + switch ChatModel.shared.conditions.conditionsAction { + + case .none: + regularConditionsHeader() + .padding(.top) + .padding(.top) + ConditionsTextView() + .padding(.bottom) + .padding(.bottom) + + case let .review(operators, deadline, _): + HStack { + Text("Updated conditions").font(.largeTitle).bold() + } + .padding(.top) + .padding(.top) + + Text("Conditions will be accepted for the operator(s): **\(operators.map { $0.legalName_ }.joined(separator: ", "))**.") + ConditionsTextView() + VStack(spacing: 8) { + acceptConditionsButton(operators.map { $0.operatorId }) + if let deadline = deadline { + Text("Conditions will be automatically accepted for enabled operators on: \(conditionsTimestamp(deadline)).") + .foregroundColor(theme.colors.secondary) + .font(.footnote) + .multilineTextAlignment(.center) + .frame(maxWidth: .infinity, alignment: .center) + .padding(.horizontal, 32) + conditionsDiffButton(.footnote) + } else { + conditionsDiffButton() + .padding(.top) + } + } + .padding(.bottom) + .padding(.bottom) + + + case let .accepted(operators): + regularConditionsHeader() + .padding(.top) + .padding(.top) + Text("Conditions are accepted for the operator(s): **\(operators.map { $0.legalName_ }.joined(separator: ", "))**.") + ConditionsTextView() + .padding(.bottom) + .padding(.bottom) + } + } + .padding(.horizontal, 25) + .frame(maxHeight: .infinity) + } + + private func acceptConditionsButton(_ operatorIds: [Int64]) -> some View { + Button { + acceptForOperators(operatorIds) + } label: { + Text("Accept conditions") + } + .buttonStyle(OnboardingButtonStyle()) + } + + func acceptForOperators(_ operatorIds: [Int64]) { + Task { + do { + let conditionsId = ChatModel.shared.conditions.currentConditions.conditionsId + let r = try await acceptConditions(conditionsId: conditionsId, operatorIds: operatorIds) + await MainActor.run { + ChatModel.shared.conditions = r + updateOperatorsConditionsAcceptance($currUserServers, r.serverOperators) + updateOperatorsConditionsAcceptance($userServers, r.serverOperators) + dismiss() + } + } catch let error { + await MainActor.run { + showAlert( + NSLocalizedString("Error accepting conditions", comment: "alert title"), + message: responseError(error) + ) + } + } + } + } + + @ViewBuilder private func conditionsDiffButton(_ font: Font? = nil) -> some View { + let commit = ChatModel.shared.conditions.currentConditions.conditionsCommit + if let commitUrl = URL(string: "https://github.com/simplex-chat/simplex-chat/commit/\(commit)") { + Link(destination: commitUrl) { + HStack { + Text("Open changes") + Image(systemName: "arrow.up.right.circle") + } + .font(font) + } + } + } +} + +private func regularConditionsHeader() -> some View { + HStack { + Text("Conditions of use").font(.largeTitle).bold() + Spacer() + conditionsLinkButton() + } +} + +struct SimpleConditionsView: View { + + var body: some View { + VStack(alignment: .leading, spacing: 20) { + regularConditionsHeader() + .padding(.top) + .padding(.top) + ConditionsTextView() + .padding(.bottom) + .padding(.bottom) + } + .padding(.horizontal, 25) + .frame(maxHeight: .infinity) + } +} + +func validateServers_(_ userServers: Binding<[UserOperatorServers]>, _ serverErrors: Binding<[UserServersError]>) { + let userServersToValidate = userServers.wrappedValue + Task { + do { + let errs = try await validateServers(userServers: userServersToValidate) + await MainActor.run { + serverErrors.wrappedValue = errs + } + } catch let error { + logger.error("validateServers error: \(responseError(error))") + } + } +} + +func serversCanBeSaved( + _ currUserServers: [UserOperatorServers], + _ userServers: [UserOperatorServers], + _ serverErrors: [UserServersError] +) -> Bool { + return userServers != currUserServers && serverErrors.isEmpty +} + +struct ServersErrorView: View { + @EnvironmentObject var theme: AppTheme + var errStr: String + + var body: some View { + HStack { + Image(systemName: "exclamationmark.circle") + .foregroundColor(.red) + Text(errStr) + .foregroundColor(theme.colors.secondary) + } + } +} + +func globalServersError(_ serverErrors: [UserServersError]) -> String? { + for err in serverErrors { + if let errStr = err.globalError { + return errStr + } + } + return nil +} + +func globalSMPServersError(_ serverErrors: [UserServersError]) -> String? { + for err in serverErrors { + if let errStr = err.globalSMPError { + return errStr + } + } + return nil +} + +func globalXFTPServersError(_ serverErrors: [UserServersError]) -> String? { + for err in serverErrors { + if let errStr = err.globalXFTPError { + return errStr + } + } + return nil +} + +func findDuplicateHosts(_ serverErrors: [UserServersError]) -> Set { + let duplicateHostsList = serverErrors.compactMap { err in + if case let .duplicateServer(_, _, duplicateHost) = err { + return duplicateHost + } else { + return nil + } + } + return Set(duplicateHostsList) +} + +func saveServers(_ currUserServers: Binding<[UserOperatorServers]>, _ userServers: Binding<[UserOperatorServers]>) { + let userServersToSave = userServers.wrappedValue + Task { + do { + try await setUserServers(userServers: userServersToSave) + // Get updated servers to learn new server ids (otherwise it messes up delete of newly added and saved servers) + do { + let updatedServers = try await getUserServers() + let updatedOperators = try await getServerOperators() + await MainActor.run { + ChatModel.shared.conditions = updatedOperators + currUserServers.wrappedValue = updatedServers + userServers.wrappedValue = updatedServers + } + } catch let error { + logger.error("saveServers getUserServers error: \(responseError(error))") + await MainActor.run { + currUserServers.wrappedValue = userServersToSave + } + } + } catch let error { + logger.error("saveServers setUserServers error: \(responseError(error))") + await MainActor.run { + showAlert( + NSLocalizedString("Error saving servers", comment: "alert title"), + message: responseError(error) + ) + } + } + } +} + +func updateOperatorsConditionsAcceptance(_ usvs: Binding<[UserOperatorServers]>, _ updatedOperators: [ServerOperator]) { + for i in 0.. some View { + VStack { + let serverAddress = parseServerAddress(serverToEdit.server) + let valid = serverAddress?.valid == true + List { + Section { + TextEditor(text: $serverToEdit.server) + .multilineTextAlignment(.leading) + .autocorrectionDisabled(true) + .autocapitalization(.none) + .allowsTightening(true) + .lineLimit(10) + .frame(height: 144) + .padding(-6) + } header: { + HStack { + Text("Your server address") + .foregroundColor(theme.colors.secondary) + if !valid { + Spacer() + Image(systemName: "exclamationmark.circle").foregroundColor(.red) + } + } + } + useServerSection(valid) + if valid { + Section(header: Text("Add to another device").foregroundColor(theme.colors.secondary)) { + MutableQRCode(uri: $serverToEdit.server) + .listRowInsets(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12)) + } + } + } + } + } + + private func useServerSection(_ valid: Bool) -> some View { + Section(header: Text("Use server").foregroundColor(theme.colors.secondary)) { + HStack { + Button("Test server") { + testing = true + serverToEdit.tested = nil + Task { + if let f = await testServerConnection(server: $serverToEdit) { + showTestFailure = true + testFailure = f + } + await MainActor.run { testing = false } + } + } + .disabled(!valid || testing) + Spacer() + showTestStatus(server: serverToEdit) + } + Toggle("Use for new connections", isOn: $serverToEdit.enabled) + } + } +} + +func serverProtocolAndOperator(_ server: UserServer, _ userServers: [UserOperatorServers]) -> (ServerProtocol, ServerOperator?)? { + if let serverAddress = parseServerAddress(server.server) { + let serverProtocol = serverAddress.serverProtocol + let hostnames = serverAddress.hostnames + let matchingOperator = userServers.compactMap { $0.operator }.first { op in + op.serverDomains.contains { domain in + hostnames.contains { hostname in + hostname.hasSuffix(domain) + } + } + } + return (serverProtocol, matchingOperator) + } else { + return nil + } +} + +func addServer( + _ server: UserServer, + _ userServers: Binding<[UserOperatorServers]>, + _ serverErrors: Binding<[UserServersError]>, + _ dismiss: DismissAction +) { + if let (serverProtocol, matchingOperator) = serverProtocolAndOperator(server, userServers.wrappedValue) { + if let i = userServers.wrappedValue.firstIndex(where: { $0.operator?.operatorId == matchingOperator?.operatorId }) { + switch serverProtocol { + case .smp: userServers[i].wrappedValue.smpServers.append(server) + case .xftp: userServers[i].wrappedValue.xftpServers.append(server) + } + validateServers_(userServers, serverErrors) + dismiss() + if let op = matchingOperator { + showAlert( + NSLocalizedString("Operator server", comment: "alert title"), + message: String.localizedStringWithFormat(NSLocalizedString("Server added to operator %@.", comment: "alert message"), op.tradeName) + ) + } + } else { // Shouldn't happen + dismiss() + showAlert(NSLocalizedString("Error adding server", comment: "alert title")) + } + } else { + dismiss() + if server.server.trimmingCharacters(in: .whitespaces) != "" { + showAlert( + NSLocalizedString("Invalid server address!", comment: "alert title"), + message: NSLocalizedString("Check server address and try again.", comment: "alert title") + ) + } + } +} + +#Preview { + NewServerView( + userServers: Binding.constant([UserOperatorServers.sampleDataNilOperator]), + serverErrors: Binding.constant([]) + ) +} diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/OperatorView.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/OperatorView.swift new file mode 100644 index 0000000000..afbccc109c --- /dev/null +++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/OperatorView.swift @@ -0,0 +1,586 @@ +// +// OperatorView.swift +// SimpleX (iOS) +// +// Created by spaced4ndy on 28.10.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat +import Ink + +struct OperatorView: View { + @Environment(\.dismiss) var dismiss: DismissAction + @Environment(\.colorScheme) var colorScheme: ColorScheme + @EnvironmentObject var theme: AppTheme + @Environment(\.editMode) private var editMode + @Binding var currUserServers: [UserOperatorServers] + @Binding var userServers: [UserOperatorServers] + @Binding var serverErrors: [UserServersError] + var operatorIndex: Int + @State var useOperator: Bool + @State private var useOperatorToggleReset: Bool = false + @State private var showConditionsSheet: Bool = false + @State private var selectedServer: String? = nil + @State private var testing = false + + var body: some View { + operatorView() + .opacity(testing ? 0.4 : 1) + .overlay { + if testing { + ProgressView() + .scaleEffect(2) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + .allowsHitTesting(!testing) + } + + private func operatorView() -> some View { + let duplicateHosts = findDuplicateHosts(serverErrors) + return VStack { + List { + Section { + infoViewLink() + useOperatorToggle() + } header: { + Text("Operator") + .foregroundColor(theme.colors.secondary) + } footer: { + if let errStr = globalServersError(serverErrors) { + ServersErrorView(errStr: errStr) + } else { + switch (userServers[operatorIndex].operator_.conditionsAcceptance) { + case let .accepted(acceptedAt, _): + if let acceptedAt = acceptedAt { + Text("Conditions accepted on: \(conditionsTimestamp(acceptedAt)).") + .foregroundColor(theme.colors.secondary) + } + case let .required(deadline): + if userServers[operatorIndex].operator_.enabled, let deadline = deadline { + Text("Conditions will be accepted on: \(conditionsTimestamp(deadline)).") + .foregroundColor(theme.colors.secondary) + } + } + } + } + + if userServers[operatorIndex].operator_.enabled { + if !userServers[operatorIndex].smpServers.filter({ !$0.deleted }).isEmpty { + Section { + Toggle("To receive", isOn: $userServers[operatorIndex].operator_.smpRoles.storage) + .onChange(of: userServers[operatorIndex].operator_.smpRoles.storage) { _ in + validateServers_($userServers, $serverErrors) + } + Toggle("For private routing", isOn: $userServers[operatorIndex].operator_.smpRoles.proxy) + .onChange(of: userServers[operatorIndex].operator_.smpRoles.proxy) { _ in + validateServers_($userServers, $serverErrors) + } + } header: { + Text("Use for messages") + .foregroundColor(theme.colors.secondary) + } footer: { + if let errStr = globalSMPServersError(serverErrors) { + ServersErrorView(errStr: errStr) + } + } + } + + // Preset servers can't be deleted + if !userServers[operatorIndex].smpServers.filter({ $0.preset }).isEmpty { + Section { + ForEach($userServers[operatorIndex].smpServers) { srv in + if srv.wrappedValue.preset { + ProtocolServerViewLink( + userServers: $userServers, + serverErrors: $serverErrors, + duplicateHosts: duplicateHosts, + server: srv, + serverProtocol: .smp, + backLabel: "\(userServers[operatorIndex].operator_.tradeName) servers", + selectedServer: $selectedServer + ) + } else { + EmptyView() + } + } + } header: { + Text("Message servers") + .foregroundColor(theme.colors.secondary) + } footer: { + if let errStr = globalSMPServersError(serverErrors) { + ServersErrorView(errStr: errStr) + } else { + Text("The servers for new connections of your current chat profile **\(ChatModel.shared.currentUser?.displayName ?? "")**.") + .foregroundColor(theme.colors.secondary) + .lineLimit(10) + } + } + } + + if !userServers[operatorIndex].smpServers.filter({ !$0.preset && !$0.deleted }).isEmpty { + Section { + ForEach($userServers[operatorIndex].smpServers) { srv in + if !srv.wrappedValue.preset && !srv.wrappedValue.deleted { + ProtocolServerViewLink( + userServers: $userServers, + serverErrors: $serverErrors, + duplicateHosts: duplicateHosts, + server: srv, + serverProtocol: .smp, + backLabel: "\(userServers[operatorIndex].operator_.tradeName) servers", + selectedServer: $selectedServer + ) + } else { + EmptyView() + } + } + .onDelete { indexSet in + deleteSMPServer($userServers, operatorIndex, indexSet) + validateServers_($userServers, $serverErrors) + } + } header: { + Text("Added message servers") + .foregroundColor(theme.colors.secondary) + } + } + + if !userServers[operatorIndex].xftpServers.filter({ !$0.deleted }).isEmpty { + Section { + Toggle("To send", isOn: $userServers[operatorIndex].operator_.xftpRoles.storage) + .onChange(of: userServers[operatorIndex].operator_.xftpRoles.storage) { _ in + validateServers_($userServers, $serverErrors) + } + } header: { + Text("Use for files") + .foregroundColor(theme.colors.secondary) + } footer: { + if let errStr = globalXFTPServersError(serverErrors) { + ServersErrorView(errStr: errStr) + } + } + } + + // Preset servers can't be deleted + if !userServers[operatorIndex].xftpServers.filter({ $0.preset }).isEmpty { + Section { + ForEach($userServers[operatorIndex].xftpServers) { srv in + if srv.wrappedValue.preset { + ProtocolServerViewLink( + userServers: $userServers, + serverErrors: $serverErrors, + duplicateHosts: duplicateHosts, + server: srv, + serverProtocol: .xftp, + backLabel: "\(userServers[operatorIndex].operator_.tradeName) servers", + selectedServer: $selectedServer + ) + } else { + EmptyView() + } + } + } header: { + Text("Media & file servers") + .foregroundColor(theme.colors.secondary) + } footer: { + if let errStr = globalXFTPServersError(serverErrors) { + ServersErrorView(errStr: errStr) + } else { + Text("The servers for new files of your current chat profile **\(ChatModel.shared.currentUser?.displayName ?? "")**.") + .foregroundColor(theme.colors.secondary) + .lineLimit(10) + } + } + } + + if !userServers[operatorIndex].xftpServers.filter({ !$0.preset && !$0.deleted }).isEmpty { + Section { + ForEach($userServers[operatorIndex].xftpServers) { srv in + if !srv.wrappedValue.preset && !srv.wrappedValue.deleted { + ProtocolServerViewLink( + userServers: $userServers, + serverErrors: $serverErrors, + duplicateHosts: duplicateHosts, + server: srv, + serverProtocol: .xftp, + backLabel: "\(userServers[operatorIndex].operator_.tradeName) servers", + selectedServer: $selectedServer + ) + } else { + EmptyView() + } + } + .onDelete { indexSet in + deleteXFTPServer($userServers, operatorIndex, indexSet) + validateServers_($userServers, $serverErrors) + } + } header: { + Text("Added media & file servers") + .foregroundColor(theme.colors.secondary) + } + } + + Section { + TestServersButton( + smpServers: $userServers[operatorIndex].smpServers, + xftpServers: $userServers[operatorIndex].xftpServers, + testing: $testing + ) + } + } + } + } + .toolbar { + if ( + !userServers[operatorIndex].smpServers.filter({ !$0.preset && !$0.deleted }).isEmpty || + !userServers[operatorIndex].xftpServers.filter({ !$0.preset && !$0.deleted }).isEmpty + ) { + EditButton() + } + } + .sheet(isPresented: $showConditionsSheet, onDismiss: onUseToggleSheetDismissed) { + SingleOperatorUsageConditionsView( + currUserServers: $currUserServers, + userServers: $userServers, + serverErrors: $serverErrors, + operatorIndex: operatorIndex + ) + .modifier(ThemedBackground(grouped: true)) + } + } + + private func infoViewLink() -> some View { + NavigationLink() { + OperatorInfoView(serverOperator: userServers[operatorIndex].operator_) + .navigationBarTitle("Network operator") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.large) + } label: { + Image(userServers[operatorIndex].operator_.largeLogo(colorScheme)) + .resizable() + .scaledToFit() + .grayscale(userServers[operatorIndex].operator_.enabled ? 0.0 : 1.0) + .frame(height: 40) + } + } + + private func useOperatorToggle() -> some View { + Toggle("Use servers", isOn: $useOperator) + .onChange(of: useOperator) { useOperatorToggle in + if useOperatorToggleReset { + useOperatorToggleReset = false + } else if useOperatorToggle { + switch userServers[operatorIndex].operator_.conditionsAcceptance { + case .accepted: + userServers[operatorIndex].operator_.enabled = true + validateServers_($userServers, $serverErrors) + case let .required(deadline): + if deadline == nil { + showConditionsSheet = true + } else { + userServers[operatorIndex].operator_.enabled = true + validateServers_($userServers, $serverErrors) + } + } + } else { + userServers[operatorIndex].operator_.enabled = false + validateServers_($userServers, $serverErrors) + } + } + } + + private func onUseToggleSheetDismissed() { + if useOperator && !userServers[operatorIndex].operator_.conditionsAcceptance.usageAllowed { + useOperatorToggleReset = true + useOperator = false + } + } +} + +func conditionsTimestamp(_ date: Date) -> String { + let localDateFormatter = DateFormatter() + localDateFormatter.dateStyle = .medium + localDateFormatter.timeStyle = .none + return localDateFormatter.string(from: date) +} + +struct OperatorInfoView: View { + @EnvironmentObject var theme: AppTheme + @Environment(\.colorScheme) var colorScheme: ColorScheme + var serverOperator: ServerOperator + + var body: some View { + VStack { + List { + Section { + VStack(alignment: .leading) { + Image(serverOperator.largeLogo(colorScheme)) + .resizable() + .scaledToFit() + .frame(height: 48) + if let legalName = serverOperator.legalName { + Text(legalName) + } + } + } + Section { + VStack(alignment: .leading, spacing: 12) { + ForEach(serverOperator.info.description, id: \.self) { d in + Text(d) + } + } + Link(serverOperator.info.website.absoluteString, destination: serverOperator.info.website) + } + if let selfhost = serverOperator.info.selfhost { + Section { + Link(selfhost.text, destination: selfhost.link) + } + } + } + } + } +} + +struct ConditionsTextView: View { + @State private var conditionsData: (UsageConditions, String?, UsageConditions?)? + @State private var failedToLoad: Bool = false + @State private var conditionsHTML: String? = nil + + let defaultConditionsLink = "https://github.com/simplex-chat/simplex-chat/blob/stable/PRIVACY.md" + + var body: some View { + viewBody() + .frame(maxWidth: .infinity, maxHeight: .infinity) + .task { + do { + let conditions = try await getUsageConditions() + let conditionsText = conditions.1 + let parentLink = "https://github.com/simplex-chat/simplex-chat/blob/\(conditions.0.conditionsCommit)" + let preparedText: String? + if let conditionsText { + let prepared = prepareMarkdown(conditionsText.trimmingCharacters(in: .whitespacesAndNewlines), parentLink) + conditionsHTML = MarkdownParser().html(from: prepared) + preparedText = prepared + } else { + preparedText = nil + } + conditionsData = (conditions.0, preparedText, conditions.2) + } catch let error { + logger.error("ConditionsTextView getUsageConditions error: \(responseError(error))") + failedToLoad = true + } + } + } + + // TODO Diff rendering + @ViewBuilder private func viewBody() -> some View { + if let (usageConditions, _, _) = conditionsData { + if let conditionsHTML { + ConditionsWebView(html: conditionsHTML) + .padding(6) + .background( + RoundedRectangle(cornerRadius: 12, style: .continuous) + .fill(Color(uiColor: .secondarySystemGroupedBackground)) + ) + } else { + let conditionsLink = "https://github.com/simplex-chat/simplex-chat/blob/\(usageConditions.conditionsCommit)/PRIVACY.md" + conditionsLinkView(conditionsLink) + } + } else if failedToLoad { + conditionsLinkView(defaultConditionsLink) + } else { + ProgressView() + .scaleEffect(2) + } + } + + private func conditionsLinkView(_ conditionsLink: String) -> some View { + VStack(alignment: .leading, spacing: 20) { + Text("Current conditions text couldn't be loaded, you can review conditions via this link:") + Link(destination: URL(string: conditionsLink)!) { + Text(conditionsLink) + .multilineTextAlignment(.leading) + } + } + } + + private func prepareMarkdown(_ text: String, _ parentLink: String) -> String { + let localLinkRegex = try! NSRegularExpression(pattern: "\\[([^\\(]*)\\]\\(#.*\\)") + let h1Regex = try! NSRegularExpression(pattern: "^# ") + var text = localLinkRegex.stringByReplacingMatches(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count), withTemplate: "$1") + text = h1Regex.stringByReplacingMatches(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count), withTemplate: "") + return text + .replacingOccurrences(of: "](/", with: "](\(parentLink)/") + .replacingOccurrences(of: "](./", with: "](\(parentLink)/") + } +} + +struct SingleOperatorUsageConditionsView: View { + @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var theme: AppTheme + @Binding var currUserServers: [UserOperatorServers] + @Binding var userServers: [UserOperatorServers] + @Binding var serverErrors: [UserServersError] + var operatorIndex: Int + + var body: some View { + viewBody() + } + + @ViewBuilder private func viewBody() -> some View { + let operatorsWithConditionsAccepted = ChatModel.shared.conditions.serverOperators.filter { $0.conditionsAcceptance.conditionsAccepted } + if case .accepted = userServers[operatorIndex].operator_.conditionsAcceptance { + + // In current UI implementation this branch doesn't get shown - as conditions can't be opened from inside operator once accepted + VStack(alignment: .leading, spacing: 20) { + viewHeader() + ConditionsTextView() + } + .padding(.bottom) + .padding(.bottom) + .padding(.horizontal) + .frame(maxHeight: .infinity) + + } else if !operatorsWithConditionsAccepted.isEmpty { + + NavigationView { + VStack(alignment: .leading, spacing: 20) { + viewHeader() + Text("Conditions are already accepted for these operator(s): **\(operatorsWithConditionsAccepted.map { $0.legalName_ }.joined(separator: ", "))**.") + Text("The same conditions will apply to operator **\(userServers[operatorIndex].operator_.legalName_)**.") + conditionsAppliedToOtherOperatorsText() + Spacer() + + acceptConditionsButton() + usageConditionsNavLinkButton() + } + .padding(.bottom) + .padding(.bottom) + .padding(.horizontal) + .frame(maxHeight: .infinity) + } + + } else { + + VStack(alignment: .leading, spacing: 20) { + viewHeader() + Text("To use the servers of **\(userServers[operatorIndex].operator_.legalName_)**, accept conditions of use.") + conditionsAppliedToOtherOperatorsText() + ConditionsTextView() + acceptConditionsButton() + .padding(.bottom) + .padding(.bottom) + } + .padding(.horizontal) + .frame(maxHeight: .infinity) + + } + } + + private func viewHeader() -> some View { + HStack { + Text("Use \(userServers[operatorIndex].operator_.tradeName)").font(.largeTitle).bold() + Spacer() + conditionsLinkButton() + } + .padding(.top) + .padding(.top) + } + + @ViewBuilder private func conditionsAppliedToOtherOperatorsText() -> some View { + let otherOperatorsToApply = ChatModel.shared.conditions.serverOperators.filter { + $0.enabled && + !$0.conditionsAcceptance.conditionsAccepted && + $0.operatorId != userServers[operatorIndex].operator_.operatorId + } + if !otherOperatorsToApply.isEmpty { + Text("These conditions will also apply for: **\(otherOperatorsToApply.map { $0.legalName_ }.joined(separator: ", "))**.") + } + } + + private func acceptConditionsButton() -> some View { + let operatorIds = ChatModel.shared.conditions.serverOperators + .filter { + $0.operatorId == userServers[operatorIndex].operator_.operatorId || // Opened operator + ($0.enabled && !$0.conditionsAcceptance.conditionsAccepted) // Other enabled operators with conditions not accepted + } + .map { $0.operatorId } + return Button { + acceptForOperators(operatorIds, operatorIndex) + } label: { + Text("Accept conditions") + } + .buttonStyle(OnboardingButtonStyle()) + } + + func acceptForOperators(_ operatorIds: [Int64], _ operatorIndexToEnable: Int) { + Task { + do { + let conditionsId = ChatModel.shared.conditions.currentConditions.conditionsId + let r = try await acceptConditions(conditionsId: conditionsId, operatorIds: operatorIds) + await MainActor.run { + ChatModel.shared.conditions = r + updateOperatorsConditionsAcceptance($currUserServers, r.serverOperators) + updateOperatorsConditionsAcceptance($userServers, r.serverOperators) + userServers[operatorIndexToEnable].operator?.enabled = true + validateServers_($userServers, $serverErrors) + dismiss() + } + } catch let error { + await MainActor.run { + showAlert( + NSLocalizedString("Error accepting conditions", comment: "alert title"), + message: responseError(error) + ) + } + } + } + } + + private func usageConditionsNavLinkButton() -> some View { + NavigationLink("View conditions") { + ConditionsTextView() + .padding() + .navigationTitle("Conditions of use") + .navigationBarTitleDisplayMode(.large) + .toolbar { ToolbarItem(placement: .navigationBarTrailing, content: conditionsLinkButton) } + .modifier(ThemedBackground(grouped: true)) + } + .font(.callout) + .frame(maxWidth: .infinity, alignment: .center) + } +} + +func conditionsLinkButton() -> some View { + let commit = ChatModel.shared.conditions.currentConditions.conditionsCommit + let mdUrl = URL(string: "https://github.com/simplex-chat/simplex-chat/blob/\(commit)/PRIVACY.md") ?? conditionsURL + return Menu { + Link(destination: mdUrl) { + Label("Open conditions", systemImage: "doc") + } + if let commitUrl = URL(string: "https://github.com/simplex-chat/simplex-chat/commit/\(commit)") { + Link(destination: commitUrl) { + Label("Open changes", systemImage: "ellipsis") + } + } + } label: { + Image(systemName: "arrow.up.right.circle") + .resizable() + .scaledToFit() + .frame(width: 20) + .padding(2) + .contentShape(Circle()) + } +} + +#Preview { + OperatorView( + currUserServers: Binding.constant([UserOperatorServers.sampleData1, UserOperatorServers.sampleDataNilOperator]), + userServers: Binding.constant([UserOperatorServers.sampleData1, UserOperatorServers.sampleDataNilOperator]), + serverErrors: Binding.constant([]), + operatorIndex: 1, + useOperator: ServerOperator.sampleData1.enabled + ) +} diff --git a/apps/ios/Shared/Views/UserSettings/ProtocolServerView.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServerView.swift similarity index 65% rename from apps/ios/Shared/Views/UserSettings/ProtocolServerView.swift rename to apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServerView.swift index 6702ab7ce8..13d01874ed 100644 --- a/apps/ios/Shared/Views/UserSettings/ProtocolServerView.swift +++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServerView.swift @@ -11,15 +11,16 @@ import SimpleXChat struct ProtocolServerView: View { @Environment(\.dismiss) var dismiss: DismissAction - let serverProtocol: ServerProtocol - @Binding var server: ServerCfg - @State var serverToEdit: ServerCfg + @EnvironmentObject var theme: AppTheme + @Binding var userServers: [UserOperatorServers] + @Binding var serverErrors: [UserServersError] + @Binding var server: UserServer + @State var serverToEdit: UserServer + var backLabel: LocalizedStringKey @State private var showTestFailure = false @State private var testing = false @State private var testFailure: ProtocolTestFailure? - var proto: String { serverProtocol.rawValue.uppercased() } - var body: some View { ZStack { if server.preset { @@ -31,9 +32,33 @@ struct ProtocolServerView: View { ProgressView().scaleEffect(2) } } - .modifier(BackButton(label: "Your \(proto) servers", disabled: Binding.constant(false)) { - server = serverToEdit - dismiss() + .modifier(BackButton(label: backLabel, disabled: Binding.constant(false)) { + if let (serverToEditProtocol, serverToEditOperator) = serverProtocolAndOperator(serverToEdit, userServers), + let (serverProtocol, serverOperator) = serverProtocolAndOperator(server, userServers) { + if serverToEditProtocol != serverProtocol { + dismiss() + showAlert( + NSLocalizedString("Error updating server", comment: "alert title"), + message: NSLocalizedString("Server protocol changed.", comment: "alert title") + ) + } else if serverToEditOperator != serverOperator { + dismiss() + showAlert( + NSLocalizedString("Error updating server", comment: "alert title"), + message: NSLocalizedString("Server operator changed.", comment: "alert title") + ) + } else { + server = serverToEdit + validateServers_($userServers, $serverErrors) + dismiss() + } + } else { + dismiss() + showAlert( + NSLocalizedString("Invalid server address!", comment: "alert title"), + message: NSLocalizedString("Check server address and try again.", comment: "alert title") + ) + } }) .alert(isPresented: $showTestFailure) { Alert( @@ -49,7 +74,7 @@ struct ProtocolServerView: View { private func presetServer() -> some View { return VStack { List { - Section("Preset server address") { + Section(header: Text("Preset server address").foregroundColor(theme.colors.secondary)) { Text(serverToEdit.server) .textSelection(.enabled) } @@ -61,7 +86,7 @@ struct ProtocolServerView: View { private func customServer() -> some View { VStack { let serverAddress = parseServerAddress(serverToEdit.server) - let valid = serverAddress?.valid == true && serverAddress?.serverProtocol == serverProtocol + let valid = serverAddress?.valid == true List { Section { TextEditor(text: $serverToEdit.server) @@ -75,6 +100,7 @@ struct ProtocolServerView: View { } header: { HStack { Text("Your server address") + .foregroundColor(theme.colors.secondary) if !valid { Spacer() Image(systemName: "exclamationmark.circle").foregroundColor(.red) @@ -83,7 +109,7 @@ struct ProtocolServerView: View { } useServerSection(valid) if valid { - Section("Add to another device") { + Section(header: Text("Add to another device").foregroundColor(theme.colors.secondary)) { MutableQRCode(uri: $serverToEdit.server) .listRowInsets(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12)) } @@ -93,7 +119,7 @@ struct ProtocolServerView: View { } private func useServerSection(_ valid: Bool) -> some View { - Section("Use server") { + Section(header: Text("Use server").foregroundColor(theme.colors.secondary)) { HStack { Button("Test server") { testing = true @@ -137,7 +163,7 @@ struct BackButton: ViewModifier { } } -@ViewBuilder func showTestStatus(server: ServerCfg) -> some View { +@ViewBuilder func showTestStatus(server: UserServer) -> some View { switch server.tested { case .some(true): Image(systemName: "checkmark") @@ -150,7 +176,7 @@ struct BackButton: ViewModifier { } } -func testServerConnection(server: Binding) async -> ProtocolTestFailure? { +func testServerConnection(server: Binding) async -> ProtocolTestFailure? { do { let r = try await testProtoServer(server: server.wrappedValue.server) switch r { @@ -170,16 +196,14 @@ func testServerConnection(server: Binding) async -> ProtocolTestFailu } } -func serverHostname(_ srv: String) -> String { - parseServerAddress(srv)?.hostnames.first ?? srv -} - struct ProtocolServerView_Previews: PreviewProvider { static var previews: some View { ProtocolServerView( - serverProtocol: .smp, - server: Binding.constant(ServerCfg.sampleData.custom), - serverToEdit: ServerCfg.sampleData.custom + userServers: Binding.constant([UserOperatorServers.sampleDataNilOperator]), + serverErrors: Binding.constant([]), + server: Binding.constant(UserServer.sampleData.custom), + serverToEdit: UserServer.sampleData.custom, + backLabel: "Your SMP servers" ) } } diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServersView.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServersView.swift new file mode 100644 index 0000000000..b9737914ec --- /dev/null +++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ProtocolServersView.swift @@ -0,0 +1,359 @@ +// +// ProtocolServersView.swift +// SimpleX (iOS) +// +// Created by Evgeny on 15/11/2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +private let howToUrl = URL(string: "https://simplex.chat/docs/server.html")! + +struct YourServersView: View { + @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject private var m: ChatModel + @EnvironmentObject var theme: AppTheme + @Environment(\.editMode) private var editMode + @Binding var userServers: [UserOperatorServers] + @Binding var serverErrors: [UserServersError] + var operatorIndex: Int + @State private var selectedServer: String? = nil + @State private var showAddServer = false + @State private var newServerNavLinkActive = false + @State private var showScanProtoServer = false + @State private var testing = false + + var body: some View { + yourServersView() + .opacity(testing ? 0.4 : 1) + .overlay { + if testing { + ProgressView() + .scaleEffect(2) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + .allowsHitTesting(!testing) + } + + private func yourServersView() -> some View { + let duplicateHosts = findDuplicateHosts(serverErrors) + return List { + if !userServers[operatorIndex].smpServers.filter({ !$0.deleted }).isEmpty { + Section { + ForEach($userServers[operatorIndex].smpServers) { srv in + if !srv.wrappedValue.deleted { + ProtocolServerViewLink( + userServers: $userServers, + serverErrors: $serverErrors, + duplicateHosts: duplicateHosts, + server: srv, + serverProtocol: .smp, + backLabel: "Your servers", + selectedServer: $selectedServer + ) + } else { + EmptyView() + } + } + .onDelete { indexSet in + deleteSMPServer($userServers, operatorIndex, indexSet) + validateServers_($userServers, $serverErrors) + } + } header: { + Text("Message servers") + .foregroundColor(theme.colors.secondary) + } footer: { + if let errStr = globalSMPServersError(serverErrors) { + ServersErrorView(errStr: errStr) + } else { + Text("The servers for new connections of your current chat profile **\(m.currentUser?.displayName ?? "")**.") + .foregroundColor(theme.colors.secondary) + .lineLimit(10) + } + } + } + + if !userServers[operatorIndex].xftpServers.filter({ !$0.deleted }).isEmpty { + Section { + ForEach($userServers[operatorIndex].xftpServers) { srv in + if !srv.wrappedValue.deleted { + ProtocolServerViewLink( + userServers: $userServers, + serverErrors: $serverErrors, + duplicateHosts: duplicateHosts, + server: srv, + serverProtocol: .xftp, + backLabel: "Your servers", + selectedServer: $selectedServer + ) + } else { + EmptyView() + } + } + .onDelete { indexSet in + deleteXFTPServer($userServers, operatorIndex, indexSet) + validateServers_($userServers, $serverErrors) + } + } header: { + Text("Media & file servers") + .foregroundColor(theme.colors.secondary) + } footer: { + if let errStr = globalXFTPServersError(serverErrors) { + ServersErrorView(errStr: errStr) + } else { + Text("The servers for new files of your current chat profile **\(m.currentUser?.displayName ?? "")**.") + .foregroundColor(theme.colors.secondary) + .lineLimit(10) + } + } + } + + Section { + ZStack { + Button("Add server") { + showAddServer = true + } + + NavigationLink(isActive: $newServerNavLinkActive) { + newServerDestinationView() + } label: { + EmptyView() + } + .frame(width: 1, height: 1) + .hidden() + } + } footer: { + if let errStr = globalServersError(serverErrors) { + ServersErrorView(errStr: errStr) + } + } + + Section { + TestServersButton( + smpServers: $userServers[operatorIndex].smpServers, + xftpServers: $userServers[operatorIndex].xftpServers, + testing: $testing + ) + howToButton() + } + } + .toolbar { + if ( + !userServers[operatorIndex].smpServers.filter({ !$0.deleted }).isEmpty || + !userServers[operatorIndex].xftpServers.filter({ !$0.deleted }).isEmpty + ) { + EditButton() + } + } + .confirmationDialog("Add server", isPresented: $showAddServer, titleVisibility: .hidden) { + Button("Enter server manually") { newServerNavLinkActive = true } + Button("Scan server QR code") { showScanProtoServer = true } + } + .sheet(isPresented: $showScanProtoServer) { + ScanProtocolServer( + userServers: $userServers, + serverErrors: $serverErrors + ) + .modifier(ThemedBackground(grouped: true)) + } + } + + private func newServerDestinationView() -> some View { + NewServerView( + userServers: $userServers, + serverErrors: $serverErrors + ) + .navigationTitle("New server") + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } + + func howToButton() -> some View { + Button { + DispatchQueue.main.async { + UIApplication.shared.open(howToUrl) + } + } label: { + HStack { + Text("How to use your servers") + Image(systemName: "arrow.up.right.circle") + } + } + } +} + +struct ProtocolServerViewLink: View { + @EnvironmentObject var theme: AppTheme + @Binding var userServers: [UserOperatorServers] + @Binding var serverErrors: [UserServersError] + var duplicateHosts: Set + @Binding var server: UserServer + var serverProtocol: ServerProtocol + var backLabel: LocalizedStringKey + @Binding var selectedServer: String? + + var body: some View { + let proto = serverProtocol.rawValue.uppercased() + + NavigationLink(tag: server.id, selection: $selectedServer) { + ProtocolServerView( + userServers: $userServers, + serverErrors: $serverErrors, + server: $server, + serverToEdit: server, + backLabel: backLabel + ) + .navigationBarTitle("\(proto) server") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.large) + } label: { + let address = parseServerAddress(server.server) + HStack { + Group { + if let address = address { + if !address.valid || address.serverProtocol != serverProtocol { + invalidServer() + } else if address.hostnames.contains(where: duplicateHosts.contains) { + Image(systemName: "exclamationmark.circle").foregroundColor(.red) + } else if !server.enabled { + Image(systemName: "slash.circle").foregroundColor(theme.colors.secondary) + } else { + showTestStatus(server: server) + } + } else { + invalidServer() + } + } + .frame(width: 16, alignment: .center) + .padding(.trailing, 4) + + let v = Text(address?.hostnames.first ?? server.server).lineLimit(1) + if server.enabled { + v + } else { + v.foregroundColor(theme.colors.secondary) + } + } + } + } + + private func invalidServer() -> some View { + Image(systemName: "exclamationmark.circle").foregroundColor(.red) + } +} + +func deleteSMPServer( + _ userServers: Binding<[UserOperatorServers]>, + _ operatorServersIndex: Int, + _ serverIndexSet: IndexSet +) { + if let idx = serverIndexSet.first { + let server = userServers[operatorServersIndex].wrappedValue.smpServers[idx] + if server.serverId == nil { + userServers[operatorServersIndex].wrappedValue.smpServers.remove(at: idx) + } else { + var updatedServer = server + updatedServer.deleted = true + userServers[operatorServersIndex].wrappedValue.smpServers[idx] = updatedServer + } + } +} + +func deleteXFTPServer( + _ userServers: Binding<[UserOperatorServers]>, + _ operatorServersIndex: Int, + _ serverIndexSet: IndexSet +) { + if let idx = serverIndexSet.first { + let server = userServers[operatorServersIndex].wrappedValue.xftpServers[idx] + if server.serverId == nil { + userServers[operatorServersIndex].wrappedValue.xftpServers.remove(at: idx) + } else { + var updatedServer = server + updatedServer.deleted = true + userServers[operatorServersIndex].wrappedValue.xftpServers[idx] = updatedServer + } + } +} + +struct TestServersButton: View { + @Binding var smpServers: [UserServer] + @Binding var xftpServers: [UserServer] + @Binding var testing: Bool + + var body: some View { + Button("Test servers", action: testServers) + .disabled(testing || allServersDisabled) + } + + private var allServersDisabled: Bool { + smpServers.allSatisfy { !$0.enabled } && xftpServers.allSatisfy { !$0.enabled } + } + + private func testServers() { + resetTestStatus() + testing = true + Task { + let fs = await runServersTest() + await MainActor.run { + testing = false + if !fs.isEmpty { + let msg = fs.map { (srv, f) in + "\(srv): \(f.localizedDescription)" + }.joined(separator: "\n") + showAlert( + NSLocalizedString("Tests failed!", comment: "alert title"), + message: String.localizedStringWithFormat(NSLocalizedString("Some servers failed the test:\n%@", comment: "alert message"), msg) + ) + } + } + } + } + + private func resetTestStatus() { + for i in 0.. [String: ProtocolTestFailure] { + var fs: [String: ProtocolTestFailure] = [:] + for i in 0..) { switch resp { case let .success(r): - if parseServerAddress(r.string) != nil { - servers.append(ServerCfg(server: r.string, preset: false, tested: nil, enabled: true)) - dismiss() - } else { - showAddressError = true - } + var server: UserServer = .empty + server.server = r.string + addServer(server, $userServers, $serverErrors, dismiss) case let .failure(e): logger.error("ScanProtocolServer.processQRCode QR code error: \(e.localizedDescription)") dismiss() @@ -54,6 +45,9 @@ struct ScanProtocolServer: View { struct ScanProtocolServer_Previews: PreviewProvider { static var previews: some View { - ScanProtocolServer(servers: Binding.constant([])) + ScanProtocolServer( + userServers: Binding.constant([UserOperatorServers.sampleDataNilOperator]), + serverErrors: Binding.constant([]) + ) } } diff --git a/apps/ios/Shared/Views/UserSettings/NotificationsView.swift b/apps/ios/Shared/Views/UserSettings/NotificationsView.swift index 4876d60eca..c4d0588987 100644 --- a/apps/ios/Shared/Views/UserSettings/NotificationsView.swift +++ b/apps/ios/Shared/Views/UserSettings/NotificationsView.swift @@ -11,36 +11,50 @@ import SimpleXChat struct NotificationsView: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @State private var notificationMode: NotificationsMode = ChatModel.shared.notificationMode - @State private var showAlert: NotificationAlert? + @State private var ntfAlert: NotificationAlert? @State private var legacyDatabase = dbContainerGroupDefault.get() == .documents + @State private var testing = false + @State private var testedSuccess: Bool? = nil var body: some View { + ZStack { + viewBody() + if testing { + ProgressView().scaleEffect(2) + } + } + .alert(item: $ntfAlert) { alert in + if let token = m.deviceToken { + return notificationAlert(alert, token) + } else { + return Alert(title: Text("No device token!")) + } + } + } + + private func viewBody() -> some View { List { Section { NavigationLink { List { Section { SelectionListView(list: NotificationsMode.values, selection: $notificationMode) { mode in - showAlert = .setMode(mode: mode) + ntfAlert = .setMode(mode: mode) } } footer: { VStack(alignment: .leading) { Text(ntfModeDescription(notificationMode)) + .foregroundColor(theme.colors.secondary) } .font(.callout) .padding(.top, 1) } } .navigationTitle("Send notifications") + .modifier(ThemedBackground(grouped: true)) .navigationBarTitleDisplayMode(.inline) - .alert(item: $showAlert) { alert in - if let token = m.deviceToken { - return notificationAlert(alert, token) - } else { - return Alert(title: Text("No device token!")) - } - } } label: { HStack { Text("Send notifications") @@ -59,6 +73,7 @@ struct NotificationsView: View { } footer: { VStack(alignment: .leading, spacing: 1) { Text("You can set lock screen notification preview via settings.") + .foregroundColor(theme.colors.secondary) Button("Open Settings") { DispatchQueue.main.async { UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil) @@ -68,6 +83,7 @@ struct NotificationsView: View { } } .navigationTitle("Show preview") + .modifier(ThemedBackground(grouped: true)) .navigationBarTitleDisplayMode(.inline) } label: { HStack { @@ -78,13 +94,16 @@ struct NotificationsView: View { } if let server = m.notificationServer { - smpServers("Push server", [server]) + smpServers("Push server", [server], theme.colors.secondary) + testTokenButton(server) } } header: { Text("Push notifications") + .foregroundColor(theme.colors.secondary) } footer: { if legacyDatabase { Text("Please restart the app and migrate the database to enable push notifications.") + .foregroundColor(theme.colors.secondary) .font(.callout) .padding(.top, 1) } @@ -109,6 +128,11 @@ struct NotificationsView: View { notificationMode = m.notificationMode } ) + case let .testFailure(testFailure): + return Alert( + title: Text("Server test failed!"), + message: Text(testFailure.localizedDescription) + ) case let .error(title, error): return Alert(title: Text(title), message: Text(error)) } @@ -133,12 +157,13 @@ struct NotificationsView: View { notificationMode = .off m.notificationMode = .off m.notificationServer = nil + testedSuccess = nil } } catch let error { await MainActor.run { let err = responseError(error) logger.error("apiDeleteToken error: \(err)") - showAlert = .error(title: "Error deleting token", error: err) + ntfAlert = .error(title: "Error deleting token", error: err) } } default: @@ -150,28 +175,123 @@ struct NotificationsView: View { notificationMode = ntfMode m.notificationMode = ntfMode m.notificationServer = ntfServer + testedSuccess = nil } } catch let error { await MainActor.run { let err = responseError(error) logger.error("apiRegisterToken error: \(err)") - showAlert = .error(title: "Error enabling notifications", error: err) + ntfAlert = .error(title: "Error enabling notifications", error: err) } } } } } + + private func testTokenButton(_ server: String) -> some View { + HStack { + Button("Test notifications") { + testing = true + Task { + await testServerAndToken(server) + await MainActor.run { testing = false } + } + } + .disabled(testing) + if !testing { + Spacer() + showTestStatus() + } + } + } + + @ViewBuilder func showTestStatus() -> some View { + if testedSuccess == true { + Image(systemName: "checkmark") + .foregroundColor(.green) + } else if testedSuccess == false { + Image(systemName: "multiply") + .foregroundColor(.red) + } + } + + private func testServerAndToken(_ server: String) async { + do { + let r = try await testProtoServer(server: server) + switch r { + case .success: + if let token = m.deviceToken { + do { + let status = try await apiCheckToken(token: token) + await MainActor.run { + m.tokenStatus = status + testedSuccess = status.workingToken + if status.workingToken { + showAlert( + NSLocalizedString("Notifications status", comment: "alert title"), + message: tokenStatusInfo(status, register: false) + ) + } else { + showAlert( + title: NSLocalizedString("Notifications error", comment: "alert title"), + message: tokenStatusInfo(status, register: true), + buttonTitle: "Register", + buttonAction: { + reRegisterToken(token: token) + testedSuccess = nil + }, + cancelButton: true + ) + } + } + } catch let error { + await MainActor.run { + let err = responseError(error) + logger.error("apiCheckToken \(err)") + ntfAlert = .error(title: "Error checking token status", error: err) + } + } + } else { + await MainActor.run { + showAlert( + NSLocalizedString("No token!", comment: "alert title") + ) + } + } + case let .failure(f): + await MainActor.run { + ntfAlert = .testFailure(testFailure: f) + testedSuccess = false + } + } + } catch let error { + await MainActor.run { + let err = responseError(error) + logger.error("testServerConnection \(err)") + ntfAlert = .error(title: "Error testing server connection", error: err) + } + } + } } func ntfModeDescription(_ mode: NotificationsMode) -> LocalizedStringKey { switch mode { - case .off: return "**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." - case .periodic: return "**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." - case .instant: return "**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." + case .off: return "**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." + case .periodic: return "**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." + case .instant: return "**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." + } +} + +func ntfModeShortDescription(_ mode: NotificationsMode) -> LocalizedStringKey { + switch mode { + case .off: return "Check messages when allowed." + case .periodic: return "Check messages every 20 min." + case .instant: return "E2E encrypted notifications." } } struct SelectionListView: View { + @EnvironmentObject var theme: AppTheme var list: [Item] @Binding var selection: Item var onSelection: ((Item) -> Void)? @@ -179,32 +299,24 @@ struct SelectionListView: View { var body: some View { ForEach(list) { item in - HStack { - Text(item.label) - Spacer() - if selection == item { - Image(systemName: "checkmark") - .resizable().scaledToFit().frame(width: 16) - .foregroundColor(.accentColor) - } - } - .contentShape(Rectangle()) - .listRowBackground(Color(uiColor: tapped == item ? .secondarySystemFill : .systemBackground)) - .onTapGesture { + Button { if selection == item { return } if let f = onSelection { f(item) } else { selection = item } - } - ._onButtonGesture { down in - if down { - tapped = item - } else { - tapped = nil + } label: { + HStack { + Text(item.label).foregroundColor(theme.colors.onBackground) + Spacer() + if selection == item { + Image(systemName: "checkmark") + .resizable().scaledToFit().frame(width: 16) + .foregroundColor(theme.colors.primary) + } } - } perform: {} + } } .environment(\.editMode, .constant(.active)) } @@ -212,11 +324,13 @@ struct SelectionListView: View { enum NotificationAlert: Identifiable { case setMode(mode: NotificationsMode) + case testFailure(testFailure: ProtocolTestFailure) case error(title: LocalizedStringKey, error: String) var id: String { switch self { case let .setMode(mode): return "enable \(mode.rawValue)" + case let .testFailure(testFailure): return "testFailure \(testFailure.testStep) \(testFailure.testError)" case let .error(title, error): return "error \(title): \(error)" } } diff --git a/apps/ios/Shared/Views/UserSettings/PreferencesView.swift b/apps/ios/Shared/Views/UserSettings/PreferencesView.swift index 2e560f8578..bd8171623a 100644 --- a/apps/ios/Shared/Views/UserSettings/PreferencesView.swift +++ b/apps/ios/Shared/Views/UserSettings/PreferencesView.swift @@ -11,6 +11,7 @@ import SimpleXChat struct PreferencesView: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme @State var profile: LocalProfile @State var preferences: FullPreferences @State var currentPreferences: FullPreferences @@ -31,11 +32,22 @@ struct PreferencesView: View { .disabled(currentPreferences == preferences) } } + .onDisappear { + if currentPreferences != preferences { + showAlert( + title: NSLocalizedString("Your chat preferences", comment: "alert title"), + message: NSLocalizedString("Chat preferences were changed.", comment: "alert message"), + buttonTitle: NSLocalizedString("Save", comment: "alert button"), + buttonAction: savePreferences, + cancelButton: true + ) + } + } } private func featureSection(_ feature: ChatFeature, _ allowFeature: Binding) -> some View { Section { - settingsRow(feature.icon) { + settingsRow(feature.icon, color: theme.colors.secondary) { Picker(feature.text, selection: allowFeature) { ForEach(FeatureAllowed.values) { allow in Text(allow.text) @@ -44,7 +56,7 @@ struct PreferencesView: View { .frame(height: 36) } } - footer: { featureFooter(feature, allowFeature) } + footer: { featureFooter(feature, allowFeature).foregroundColor(theme.colors.secondary) } } @@ -54,11 +66,11 @@ struct PreferencesView: View { get: { allowFeature.wrappedValue == .always || allowFeature.wrappedValue == .yes }, set: { yes, _ in allowFeature.wrappedValue = yes ? .yes : .no } ) - settingsRow(ChatFeature.timedMessages.icon) { + settingsRow(ChatFeature.timedMessages.icon, color: theme.colors.secondary) { Toggle(ChatFeature.timedMessages.text, isOn: allow) } } - footer: { featureFooter(.timedMessages, allowFeature) } + footer: { featureFooter(.timedMessages, allowFeature).foregroundColor(theme.colors.secondary) } } private func featureFooter(_ feature: ChatFeature, _ allowFeature: Binding) -> some View { diff --git a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift index 8d13c6fb39..eba7f8066a 100644 --- a/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift +++ b/apps/ios/Shared/Views/UserSettings/PrivacySettings.swift @@ -11,15 +11,20 @@ import SimpleXChat struct PrivacySettings: View { @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @AppStorage(DEFAULT_PRIVACY_ACCEPT_IMAGES) private var autoAcceptImages = true @AppStorage(DEFAULT_PRIVACY_LINK_PREVIEWS) private var useLinkPreviews = true @AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true @AppStorage(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true @AppStorage(GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES, store: groupDefaults) private var encryptLocalFiles = true + @AppStorage(GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS, store: groupDefaults) private var askToApproveRelays = true @State private var simplexLinkMode = privacySimplexLinkModeDefault.get() + @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false + @AppStorage(DEFAULT_PRIVACY_SHORT_LINKS) private var shortSimplexLinks = false @AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false @AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false @State private var currentLAMode = privacyLocalAuthModeDefault.get() + @AppStorage(DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) private var privacyMediaBlurRadius: Int = 0 @State private var contactReceipts = false @State private var contactReceiptsReset = false @State private var contactReceiptsOverrides = 0 @@ -43,46 +48,38 @@ struct PrivacySettings: View { var body: some View { VStack { List { - Section("Device") { + Section(header: Text("Device").foregroundColor(theme.colors.secondary)) { NavigationLink { SimplexLockView(prefPerformLA: $prefPerformLA, currentLAMode: $currentLAMode) .navigationTitle("SimpleX Lock") + .modifier(ThemedBackground(grouped: true)) } label: { if prefPerformLA { settingsRow("lock.fill", color: .green) { simplexLockRow(currentLAMode.text) } } else { - settingsRow("lock") { + settingsRow("lock", color: theme.colors.secondary) { simplexLockRow("Off") } } } - settingsRow("eye.slash") { + settingsRow("eye.slash", color: theme.colors.secondary) { Toggle("Protect app screen", isOn: $protectScreen) } } Section { - settingsRow("lock.doc") { - Toggle("Encrypt local files", isOn: $encryptLocalFiles) - .onChange(of: encryptLocalFiles) { - setEncryptLocalFiles($0) - } - } - settingsRow("photo") { - Toggle("Auto-accept images", isOn: $autoAcceptImages) - .onChange(of: autoAcceptImages) { - privacyAcceptImagesGroupDefault.set($0) - } - } - settingsRow("network") { + settingsRow("network", color: theme.colors.secondary) { Toggle("Send link previews", isOn: $useLinkPreviews) + .onChange(of: useLinkPreviews) { linkPreviews in + privacyLinkPreviewsGroupDefault.set(linkPreviews) + } } - settingsRow("message") { + settingsRow("message", color: theme.colors.secondary) { Toggle("Show last messages", isOn: $showChatPreviews) } - settingsRow("rectangle.and.pencil.and.ellipsis") { + settingsRow("rectangle.and.pencil.and.ellipsis", color: theme.colors.secondary) { Toggle("Message draft", isOn: $saveLastDraft) } .onChange(of: saveLastDraft) { saveDraft in @@ -91,7 +88,7 @@ struct PrivacySettings: View { m.draftChatId = nil } } - settingsRow("link") { + settingsRow("link", color: theme.colors.secondary) { Picker("SimpleX links", selection: $simplexLinkMode) { ForEach( SimpleXLinkMode.values + (SimpleXLinkMode.values.contains(simplexLinkMode) ? [] : [simplexLinkMode]) @@ -104,24 +101,77 @@ struct PrivacySettings: View { .onChange(of: simplexLinkMode) { mode in privacySimplexLinkModeDefault.set(mode) } + if developerTools { + settingsRow("link.badge.plus", color: theme.colors.secondary) { + Toggle("Use short links (BETA)", isOn: $shortSimplexLinks) + } + } } header: { Text("Chats") + .foregroundColor(theme.colors.secondary) } Section { - settingsRow("person") { + settingsRow("lock.doc", color: theme.colors.secondary) { + Toggle("Encrypt local files", isOn: $encryptLocalFiles) + .onChange(of: encryptLocalFiles) { + setEncryptLocalFiles($0) + } + } + settingsRow("photo", color: theme.colors.secondary) { + Toggle("Auto-accept images", isOn: $autoAcceptImages) + .onChange(of: autoAcceptImages) { + privacyAcceptImagesGroupDefault.set($0) + } + } + settingsRow("circle.filled.pattern.diagonalline.rectangle", color: theme.colors.secondary) { + Picker("Blur media", selection: $privacyMediaBlurRadius) { + let values = [0, 12, 24, 48] + ([0, 12, 24, 48].contains(privacyMediaBlurRadius) ? [] : [privacyMediaBlurRadius]) + ForEach(values, id: \.self) { radius in + let text: String = switch radius { + case 0: NSLocalizedString("Off", comment: "blur media") + case 12: NSLocalizedString("Soft", comment: "blur media") + case 24: NSLocalizedString("Medium", comment: "blur media") + case 48: NSLocalizedString("Strong", comment: "blur media") + default: "\(radius)" + } + Text(text) + } + } + } + .frame(height: 36) + settingsRow("network.badge.shield.half.filled", color: theme.colors.secondary) { + Toggle("Protect IP address", isOn: $askToApproveRelays) + } + } header: { + Text("Files") + .foregroundColor(theme.colors.secondary) + } footer: { + if askToApproveRelays { + Text("The app will ask to confirm downloads from unknown file servers (except .onion).") + .foregroundColor(theme.colors.secondary) + } else { + Text("Without Tor or VPN, your IP address will be visible to file servers.") + .foregroundColor(theme.colors.secondary) + } + } + + Section { + settingsRow("person", color: theme.colors.secondary) { Toggle("Contacts", isOn: $contactReceipts) } - settingsRow("person.2") { + settingsRow("person.2", color: theme.colors.secondary) { Toggle("Small groups (max 20)", isOn: $groupReceipts) } } header: { Text("Send delivery receipts to") + .foregroundColor(theme.colors.secondary) } footer: { VStack(alignment: .leading) { Text("These settings are for your current profile **\(m.currentUser?.displayName ?? "")**.") Text("They can be overridden in contact and group settings.") } + .foregroundColor(theme.colors.secondary) .frame(maxWidth: .infinity, alignment: .leading) } .confirmationDialog(contactReceiptsDialogTitle, isPresented: $contactReceiptsDialogue, titleVisibility: .visible) { @@ -317,6 +367,7 @@ struct SimplexLockView: View { @Binding var prefPerformLA: Bool @Binding var currentLAMode: LAMode @EnvironmentObject var m: ChatModel + @EnvironmentObject var theme: AppTheme @AppStorage(DEFAULT_LA_NOTICE_SHOWN) private var prefLANoticeShown = false @State private var laMode: LAMode = privacyLocalAuthModeDefault.get() @AppStorage(DEFAULT_LA_LOCK_DELAY) private var laLockDelay = 30 @@ -324,6 +375,7 @@ struct SimplexLockView: View { @State private var selfDestruct: Bool = UserDefaults.standard.bool(forKey: DEFAULT_LA_SELF_DESTRUCT) @State private var currentSelfDestruct: Bool = UserDefaults.standard.bool(forKey: DEFAULT_LA_SELF_DESTRUCT) @AppStorage(DEFAULT_LA_SELF_DESTRUCT_DISPLAY_NAME) private var selfDestructDisplayName = "" + @AppStorage(GROUP_DEFAULT_ALLOW_SHARE_EXTENSION, store: groupDefaults) private var allowShareExtension = false @State private var performLAToggleReset = false @State private var performLAModeReset = false @State private var performLASelfDestructReset = false @@ -395,13 +447,19 @@ struct SimplexLockView: View { } } + if performLA { + Section("Share to SimpleX") { + Toggle("Allow sharing", isOn: $allowShareExtension) + } + } + if performLA && laMode == .passcode { - Section("Self-destruct passcode") { + Section(header: Text("Self-destruct passcode").foregroundColor(theme.colors.secondary)) { Toggle(isOn: $selfDestruct) { HStack(spacing: 6) { Text("Enable self-destruct") Image(systemName: "info.circle") - .foregroundColor(.accentColor) + .foregroundColor(theme.colors.primary) .font(.system(size: 14)) } .onTapGesture { @@ -419,6 +477,7 @@ struct SimplexLockView: View { } } .onChange(of: performLA) { performLAToggle in + appLocalAuthEnabledGroupDefault.set(performLAToggle) prefLANoticeShown = true if performLAToggleReset { performLAToggleReset = false diff --git a/apps/ios/Shared/Views/UserSettings/ProtocolServersView.swift b/apps/ios/Shared/Views/UserSettings/ProtocolServersView.swift deleted file mode 100644 index b9163d4bad..0000000000 --- a/apps/ios/Shared/Views/UserSettings/ProtocolServersView.swift +++ /dev/null @@ -1,307 +0,0 @@ -// -// ProtocolServersView.swift -// SimpleX (iOS) -// -// Created by Evgeny on 15/11/2022. -// Copyright © 2022 SimpleX Chat. All rights reserved. -// - -import SwiftUI -import SimpleXChat - -private let howToUrl = URL(string: "https://simplex.chat/docs/server.html")! - -struct ProtocolServersView: View { - @Environment(\.dismiss) var dismiss: DismissAction - @EnvironmentObject private var m: ChatModel - @Environment(\.editMode) private var editMode - let serverProtocol: ServerProtocol - @State private var currServers: [ServerCfg] = [] - @State private var presetServers: [String] = [] - @State private var servers: [ServerCfg] = [] - @State private var selectedServer: String? = nil - @State private var showAddServer = false - @State private var showScanProtoServer = false - @State private var justOpened = true - @State private var testing = false - @State private var alert: ServerAlert? = nil - @State private var showSaveDialog = false - - var proto: String { serverProtocol.rawValue.uppercased() } - - var body: some View { - ZStack { - protocolServersView() - if testing { - ProgressView().scaleEffect(2) - } - } - } - - enum ServerAlert: Identifiable { - case testsFailed(failures: [String: ProtocolTestFailure]) - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") - - var id: String { - switch self { - case .testsFailed: return "testsFailed" - case let .error(title, _): return "error \(title)" - } - } - } - - private func protocolServersView() -> some View { - List { - Section { - ForEach($servers) { srv in - protocolServerView(srv) - } - .onMove { indexSet, offset in - servers.move(fromOffsets: indexSet, toOffset: offset) - } - .onDelete { indexSet in - servers.remove(atOffsets: indexSet) - } - Button("Add server…") { - showAddServer = true - } - } header: { - Text("\(proto) servers") - } footer: { - Text("The servers for new connections of your current chat profile **\(m.currentUser?.displayName ?? "")**.") - .lineLimit(10) - } - - Section { - Button("Reset") { servers = currServers } - .disabled(servers == currServers || testing) - Button("Test servers", action: testServers) - .disabled(testing || allServersDisabled) - Button("Save servers", action: saveServers) - .disabled(saveDisabled) - howToButton() - } - } - .toolbar { EditButton() } - .confirmationDialog("Add server…", isPresented: $showAddServer, titleVisibility: .hidden) { - Button("Enter server manually") { - servers.append(ServerCfg.empty) - selectedServer = servers.last?.id - } - Button("Scan server QR code") { showScanProtoServer = true } - Button("Add preset servers", action: addAllPresets) - .disabled(hasAllPresets()) - } - .sheet(isPresented: $showScanProtoServer) { - ScanProtocolServer(servers: $servers) - } - .modifier(BackButton(disabled: Binding.constant(false)) { - if saveDisabled { - dismiss() - justOpened = false - } else { - showSaveDialog = true - } - }) - .confirmationDialog("Save servers?", isPresented: $showSaveDialog) { - Button("Save") { - saveServers() - dismiss() - justOpened = false - } - Button("Exit without saving") { dismiss() } - } - .alert(item: $alert) { a in - switch a { - case let .testsFailed(fs): - let msg = fs.map { (srv, f) in - "\(srv): \(f.localizedDescription)" - }.joined(separator: "\n") - return Alert( - title: Text("Tests failed!"), - message: Text("Some servers failed the test:\n" + msg) - ) - case .error: - return Alert( - title: Text("Error") - ) - } - } - .onAppear { - // this condition is needed to prevent re-setting the servers when exiting single server view - if !justOpened { return } - do { - let r = try getUserProtoServers(serverProtocol) - currServers = r.protoServers - presetServers = r.presetServers - servers = currServers - } catch let error { - alert = .error( - title: "Error loading \(proto) servers", - error: "Error: \(responseError(error))" - ) - } - justOpened = false - } - } - - private var saveDisabled: Bool { - servers.isEmpty || - servers == currServers || - testing || - !servers.allSatisfy { srv in - if let address = parseServerAddress(srv.server) { - return uniqueAddress(srv, address) - } - return false - } || - allServersDisabled - } - - private var allServersDisabled: Bool { - servers.allSatisfy { !$0.enabled } - } - - private func protocolServerView(_ server: Binding) -> some View { - let srv = server.wrappedValue - return NavigationLink(tag: srv.id, selection: $selectedServer) { - ProtocolServerView( - serverProtocol: serverProtocol, - server: server, - serverToEdit: srv - ) - .navigationBarTitle(srv.preset ? "Preset server" : "Your server") - .navigationBarTitleDisplayMode(.large) - } label: { - let address = parseServerAddress(srv.server) - HStack { - Group { - if let address = address { - if !address.valid || address.serverProtocol != serverProtocol { - invalidServer() - } else if !uniqueAddress(srv, address) { - Image(systemName: "exclamationmark.circle").foregroundColor(.red) - } else if !srv.enabled { - Image(systemName: "slash.circle").foregroundColor(.secondary) - } else { - showTestStatus(server: srv) - } - } else { - invalidServer() - } - } - .frame(width: 16, alignment: .center) - .padding(.trailing, 4) - - let v = Text(address?.hostnames.first ?? srv.server).lineLimit(1) - if srv.enabled { - v - } else { - v.foregroundColor(.secondary) - } - } - } - } - - func howToButton() -> some View { - Button { - DispatchQueue.main.async { - UIApplication.shared.open(howToUrl) - } - } label: { - HStack { - Text("How to use your servers") - Image(systemName: "arrow.up.right.circle") - } - } - } - - private func invalidServer() -> some View { - Image(systemName: "exclamationmark.circle").foregroundColor(.red) - } - - private func uniqueAddress(_ s: ServerCfg, _ address: ServerAddress) -> Bool { - servers.allSatisfy { srv in - address.hostnames.allSatisfy { host in - srv.id == s.id || !srv.server.contains(host) - } - } - } - - private func hasAllPresets() -> Bool { - presetServers.allSatisfy { hasPreset($0) } - } - - private func addAllPresets() { - for srv in presetServers { - if !hasPreset(srv) { - servers.append(ServerCfg(server: srv, preset: true, tested: nil, enabled: true)) - } - } - } - - private func hasPreset(_ srv: String) -> Bool { - servers.contains(where: { $0.server == srv }) - } - - private func testServers() { - resetTestStatus() - testing = true - Task { - let fs = await runServersTest() - await MainActor.run { - testing = false - if !fs.isEmpty { - alert = .testsFailed(failures: fs) - } - } - } - } - - private func resetTestStatus() { - for i in 0.. [String: ProtocolTestFailure] { - var fs: [String: ProtocolTestFailure] = [:] - for i in 0..(defaults: UserDefaults let customDisappearingMessageTimeDefault = IntDefault(defaults: UserDefaults.standard, forKey: DEFAULT_CUSTOM_DISAPPEARING_MESSAGE_TIME) +let showDeleteConversationNoticeDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOW_DELETE_CONVERSATION_NOTICE) +let showDeleteContactNoticeDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOW_DELETE_CONTACT_NOTICE) + +/// after importing new database, this flag will be set and unset only after importing app settings in `initializeChat` */ +let shouldImportAppSettingsDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOULD_IMPORT_APP_SETTINGS) +let currentThemeDefault = StringDefault(defaults: UserDefaults.standard, forKey: DEFAULT_CURRENT_THEME, withDefault: DefaultTheme.SYSTEM_THEME_NAME) +let systemDarkThemeDefault = StringDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SYSTEM_DARK_THEME, withDefault: DefaultTheme.DARK.themeName) +let currentThemeIdsDefault = CodableDefault<[String: String]>(defaults: UserDefaults.standard, forKey: DEFAULT_CURRENT_THEME_IDS, withDefault: [:] ) +let themeOverridesDefault: CodableDefault<[ThemeOverrides]> = CodableDefault(defaults: UserDefaults.standard, forKey: DEFAULT_THEME_OVERRIDES, withDefault: []) + func setGroupDefaults() { privacyAcceptImagesGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_ACCEPT_IMAGES)) + appLocalAuthEnabledGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA)) + privacyLinkPreviewsGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS)) + profileImageCornerRadiusGroupDefault.set(UserDefaults.standard.double(forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS)) } +public class StringDefault { + var defaults: UserDefaults + var key: String + var defaultValue: String + + public init(defaults: UserDefaults = UserDefaults.standard, forKey: String, withDefault: String) { + self.defaults = defaults + self.key = forKey + self.defaultValue = withDefault + } + + public func get() -> String { + defaults.string(forKey: key) ?? defaultValue + } + + public func set(_ value: String) { + defaults.set(value, forKey: key) + defaults.synchronize() + } +} + +public class CodableDefault { + var defaults: UserDefaults + var key: String + var defaultValue: T + + public init(defaults: UserDefaults = UserDefaults.standard, forKey: String, withDefault: T) { + self.defaults = defaults + self.key = forKey + self.defaultValue = withDefault + } + + var cache: T? = nil + + public func get() -> T { + if let cache { + return cache + } else if let value = defaults.string(forKey: key) { + let res = decodeJSON(value) ?? defaultValue + cache = res + return res + } + return defaultValue + } + + public func set(_ value: T) { + defaults.set(encodeJSON(value), forKey: key) + cache = value + //defaults.synchronize() + } +} + +let networkProxyDefault: CodableDefault = CodableDefault(defaults: UserDefaults.standard, forKey: DEFAULT_NETWORK_PROXY, withDefault: NetworkProxy.def) + struct SettingsView: View { @Environment(\.colorScheme) var colorScheme + @Environment(\.dismiss) var dismiss @EnvironmentObject var chatModel: ChatModel @EnvironmentObject var sceneDelegate: SceneDelegate - @Binding var showSettings: Bool + @EnvironmentObject var theme: AppTheme @State private var showProgress: Bool = false var body: some View { @@ -161,187 +277,158 @@ struct SettingsView: View { if showProgress { progressView() } - if let la = chatModel.laRequest { - LocalAuthView(authRequest: la) - } } } - @ViewBuilder func settingsView() -> some View { - let user = chatModel.currentUser - NavigationView { - List { - Section("You") { - if let user = user { - NavigationLink { - UserProfile() - .navigationTitle("Your current profile") - } label: { - ProfilePreview(profileOf: user) - .padding(.leading, -8) - } - } - - NavigationLink { - UserProfilesView(showSettings: $showSettings) - } label: { - settingsRow("person.crop.rectangle.stack") { Text("Your chat profiles") } - } - - - if let user = user { - NavigationLink { - UserAddressView(shareViaProfile: user.addressShared) - .navigationTitle("SimpleX address") - .navigationBarTitleDisplayMode(.large) - } label: { - settingsRow("qrcode") { Text("Your SimpleX address") } - } - - NavigationLink { - PreferencesView(profile: user.profile, preferences: user.fullPreferences, currentPreferences: user.fullPreferences) - .navigationTitle("Your preferences") - } label: { - settingsRow("switch.2") { Text("Chat preferences") } - } - } - - NavigationLink { - ConnectDesktopView(viaSettings: true) - } label: { - settingsRow("desktopcomputer") { Text("Use from desktop") } - } - - NavigationLink { - MigrateFromDevice(showSettings: $showSettings, showProgressOnSettings: $showProgress) - .navigationTitle("Migrate device") - .navigationBarTitleDisplayMode(.large) - } label: { - settingsRow("tray.and.arrow.up") { Text("Migrate to another device") } + func settingsView() -> some View { + List { + let user = chatModel.currentUser + Section(header: Text("Settings").foregroundColor(theme.colors.secondary)) { + NavigationLink { + NotificationsView() + .navigationTitle("Notifications") + .modifier(ThemedBackground(grouped: true)) + } label: { + HStack { + notificationsIcon() + Text("Notifications") } } .disabled(chatModel.chatRunning != true) - Section("Settings") { - NavigationLink { - NotificationsView() - .navigationTitle("Notifications") - } label: { - HStack { - notificationsIcon() - Text("Notifications") - } - } - .disabled(chatModel.chatRunning != true) - - NavigationLink { - NetworkAndServers() - .navigationTitle("Network & servers") - } label: { - settingsRow("externaldrive.connected.to.line.below") { Text("Network & servers") } - } - .disabled(chatModel.chatRunning != true) - - NavigationLink { - CallSettings() - .navigationTitle("Your calls") - } label: { - settingsRow("video") { Text("Audio & video calls") } - } - .disabled(chatModel.chatRunning != true) - - NavigationLink { - PrivacySettings() - .navigationTitle("Your privacy") - } label: { - settingsRow("lock") { Text("Privacy & security") } - } - .disabled(chatModel.chatRunning != true) - - if UIApplication.shared.supportsAlternateIcons { - NavigationLink { - AppearanceSettings() - .navigationTitle("Appearance") - } label: { - settingsRow("sun.max") { Text("Appearance") } - } - .disabled(chatModel.chatRunning != true) - } - - chatDatabaseRow() + NavigationLink { + NetworkAndServers() + .navigationTitle("Network & servers") + .modifier(ThemedBackground(grouped: true)) + } label: { + settingsRow("externaldrive.connected.to.line.below", color: theme.colors.secondary) { Text("Network & servers") } } + .disabled(chatModel.chatRunning != true) - Section("Help") { - if let user = user { - NavigationLink { - ChatHelp(showSettings: $showSettings) - .navigationTitle("Welcome \(user.displayName)!") - .frame(maxHeight: .infinity, alignment: .top) - } label: { - settingsRow("questionmark") { Text("How to use it") } - } - } + NavigationLink { + CallSettings() + .navigationTitle("Your calls") + .modifier(ThemedBackground(grouped: true)) + } label: { + settingsRow("video", color: theme.colors.secondary) { Text("Audio & video calls") } + } + .disabled(chatModel.chatRunning != true) + + NavigationLink { + PrivacySettings() + .navigationTitle("Your privacy") + .modifier(ThemedBackground(grouped: true)) + } label: { + settingsRow("lock", color: theme.colors.secondary) { Text("Privacy & security") } + } + .disabled(chatModel.chatRunning != true) + + if UIApplication.shared.supportsAlternateIcons { NavigationLink { - WhatsNewView(viaSettings: true) - .navigationBarTitleDisplayMode(.inline) + AppearanceSettings() + .navigationTitle("Appearance") + .modifier(ThemedBackground(grouped: true)) } label: { - settingsRow("plus") { Text("What's new") } - } - NavigationLink { - SimpleXInfo(onboarding: false) - .navigationBarTitle("", displayMode: .inline) - .frame(maxHeight: .infinity, alignment: .top) - } label: { - settingsRow("info") { Text("About SimpleX Chat") } - } - settingsRow("number") { - Button("Send questions and ideas") { - showSettings = false - DispatchQueue.main.async { - UIApplication.shared.open(simplexTeamURL) - } - } + settingsRow("sun.max", color: theme.colors.secondary) { Text("Appearance") } } .disabled(chatModel.chatRunning != true) - settingsRow("envelope") { Text("[Send us email](mailto:chat@simplex.chat)") } - } - - Section("Support SimpleX Chat") { - settingsRow("keyboard") { Text("[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)") } - settingsRow("star") { - Button("Rate the app") { - if let scene = sceneDelegate.windowScene { - SKStoreReviewController.requestReview(in: scene) - } - } - } - ZStack(alignment: .leading) { - Image(colorScheme == .dark ? "github_light" : "github") - .resizable() - .frame(width: 24, height: 24) - .opacity(0.5) - Text("[Star on GitHub](https://github.com/simplex-chat/simplex-chat)") - .padding(.leading, indent) - } - } - - Section("Develop") { - NavigationLink { - DeveloperView() - .navigationTitle("Developer tools") - } label: { - settingsRow("chevron.left.forwardslash.chevron.right") { Text("Developer tools") } - } - NavigationLink { - VersionView() - .navigationBarTitle("App version") - } label: { - Text("v\(appVersion ?? "?") (\(appBuild ?? "?"))") - } } } - .navigationTitle("Your settings") + + Section(header: Text("Chat database").foregroundColor(theme.colors.secondary)) { + chatDatabaseRow() + NavigationLink { + MigrateFromDevice(showProgressOnSettings: $showProgress) + .toolbar { + // Redaction broken for `.navigationTitle` - using a toolbar item instead. + ToolbarItem(placement: .principal) { + Text("Migrate device").font(.headline) + } + } + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.large) + } label: { + settingsRow("tray.and.arrow.up", color: theme.colors.secondary) { Text("Migrate to another device") } + } + } + + Section(header: Text("Help").foregroundColor(theme.colors.secondary)) { + if let user = user { + NavigationLink { + ChatHelp(dismissSettingsSheet: dismiss) + .navigationTitle("Welcome \(user.displayName)!") + .modifier(ThemedBackground()) + .frame(maxHeight: .infinity, alignment: .top) + } label: { + settingsRow("questionmark", color: theme.colors.secondary) { Text("How to use it") } + } + } + NavigationLink { + WhatsNewView(viaSettings: true, updatedConditions: false) + .modifier(ThemedBackground()) + .navigationBarTitleDisplayMode(.inline) + } label: { + settingsRow("plus", color: theme.colors.secondary) { Text("What's new") } + } + NavigationLink { + SimpleXInfo(onboarding: false) + .navigationBarTitle("", displayMode: .inline) + .modifier(ThemedBackground()) + .frame(maxHeight: .infinity, alignment: .top) + } label: { + settingsRow("info", color: theme.colors.secondary) { Text("About SimpleX Chat") } + } + settingsRow("number", color: theme.colors.secondary) { + Button("Send questions and ideas") { + dismiss() + DispatchQueue.main.async { + UIApplication.shared.open(simplexTeamURL) + } + } + } + .disabled(chatModel.chatRunning != true) + settingsRow("envelope", color: theme.colors.secondary) { Text("[Send us email](mailto:chat@simplex.chat)") } + } + + Section(header: Text("Support SimpleX Chat").foregroundColor(theme.colors.secondary)) { + settingsRow("keyboard", color: theme.colors.secondary) { Text("[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)") } + settingsRow("star", color: theme.colors.secondary) { + Button("Rate the app") { + if let scene = sceneDelegate.windowScene { + SKStoreReviewController.requestReview(in: scene) + } + } + } + ZStack(alignment: .leading) { + Image(colorScheme == .dark ? "github_light" : "github") + .resizable() + .frame(width: 24, height: 24) + .opacity(0.5) + .colorMultiply(theme.colors.secondary) + Text("[Star on GitHub](https://github.com/simplex-chat/simplex-chat)") + .padding(.leading, indent) + } + } + + Section(header: Text("Develop").foregroundColor(theme.colors.secondary)) { + NavigationLink { + DeveloperView() + .navigationTitle("Developer tools") + .modifier(ThemedBackground(grouped: true)) + } label: { + settingsRow("chevron.left.forwardslash.chevron.right", color: theme.colors.secondary) { Text("Developer tools") } + } + NavigationLink { + VersionView() + .navigationBarTitle("App version") + .modifier(ThemedBackground()) + } label: { + Text("v\(appVersion ?? "?") (\(appBuild ?? "?"))") + } + } } + .navigationTitle("Your settings") + .modifier(ThemedBackground(grouped: true)) .onDisappear { chatModel.showingTerminal = false chatModel.terminalItems = [] @@ -350,10 +437,11 @@ struct SettingsView: View { private func chatDatabaseRow() -> some View { NavigationLink { - DatabaseView(showSettings: $showSettings, chatItemTTL: chatModel.chatItemTTL) + DatabaseView(dismissSettingsSheet: dismiss, chatItemTTL: chatModel.chatItemTTL) .navigationTitle("Your chat database") + .modifier(ThemedBackground(grouped: true)) } label: { - let color: Color = chatModel.chatDbEncrypted == false ? .orange : .secondary + let color: Color = chatModel.chatDbEncrypted == false ? .orange : theme.colors.secondary settingsRow("internaldrive", color: color) { HStack { Text("Database passphrase & export") @@ -384,13 +472,17 @@ struct SettingsView: View { switch (chatModel.tokenStatus) { case .new: icon = "bolt" - color = .secondary + color = theme.colors.secondary case .registered: icon = "bolt.fill" - color = .secondary - case .invalid: + color = theme.colors.secondary + case .invalid: fallthrough + case .invalidBad: fallthrough + case .invalidTopic: fallthrough + case .invalidExpired: fallthrough + case .invalidUnregistered: icon = "bolt.slash" - color = .secondary + color = theme.colors.secondary case .confirmed: icon = "bolt.fill" color = .yellow @@ -399,10 +491,10 @@ struct SettingsView: View { color = .green case .expired: icon = "bolt.slash.fill" - color = .secondary + color = theme.colors.secondary case .none: icon = "bolt" - color = .secondary + color = theme.colors.secondary } return Image(systemName: icon) .padding(.trailing, 9) @@ -410,7 +502,7 @@ struct SettingsView: View { } } -func settingsRow(_ icon: String, color: Color = .secondary, content: @escaping () -> Content) -> some View { +func settingsRow(_ icon: String, color: Color/* = .secondary*/, content: @escaping () -> Content) -> some View { ZStack(alignment: .leading) { Image(systemName: icon).frame(maxWidth: 24, maxHeight: 24, alignment: .center) .symbolRenderingMode(.monochrome) @@ -421,33 +513,31 @@ func settingsRow(_ icon: String, color: Color = .secondary, cont struct ProfilePreview: View { var profileOf: NamedChat - var color = Color(uiColor: .tertiarySystemFill) + var color = Color(uiColor: .tertiarySystemGroupedBackground) var body: some View { HStack { - ProfileImage(imageStr: profileOf.image, color: color) - .frame(width: 44, height: 44) + ProfileImage(imageStr: profileOf.image, size: 44, color: color) .padding(.trailing, 6) - .padding(.vertical, 6) - VStack(alignment: .leading) { - Text(profileOf.displayName) - .fontWeight(.bold) - .font(.title2) - if profileOf.fullName != "" && profileOf.fullName != profileOf.displayName { - Text(profileOf.fullName) - } - } + profileName(profileOf).lineLimit(1) } } } +func profileName(_ profileOf: NamedChat) -> Text { + var t = Text(profileOf.displayName).fontWeight(.semibold).font(.title2) + if profileOf.fullName != "" && profileOf.fullName != profileOf.displayName { + t = t + Text(verbatim: " (" + profileOf.fullName + ")") +// .font(.callout) + } + return t +} + struct SettingsView_Previews: PreviewProvider { static var previews: some View { let chatModel = ChatModel() chatModel.currentUser = User.sampleData - @State var showSettings = false - - return SettingsView(showSettings: $showSettings) + return SettingsView() .environmentObject(chatModel) } } diff --git a/apps/ios/Shared/Views/UserSettings/StorageView.swift b/apps/ios/Shared/Views/UserSettings/StorageView.swift new file mode 100644 index 0000000000..094c1cb3d6 --- /dev/null +++ b/apps/ios/Shared/Views/UserSettings/StorageView.swift @@ -0,0 +1,56 @@ +// +// StorageView.swift +// SimpleX (iOS) +// +// Created by Stanislav Dmitrenko on 13.01.2025. +// Copyright © 2025 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct StorageView: View { + @State var appGroupFiles: [String: Int64] = [:] + @State var documentsFiles: [String: Int64] = [:] + + var body: some View { + ScrollView { + VStack(alignment: .leading) { + directoryView("App group:", appGroupFiles) + if !documentsFiles.isEmpty { + directoryView("Documents:", documentsFiles) + } + } + } + .padding() + .onAppear { + appGroupFiles = traverseFiles(in: getGroupContainerDirectory()) + documentsFiles = traverseFiles(in: getDocumentsDirectory()) + } + } + + @ViewBuilder + private func directoryView(_ name: LocalizedStringKey, _ contents: [String: Int64]) -> some View { + Text(name).font(.headline) + ForEach(Array(contents), id: \.key) { (key, value) in + Text(key).bold() + Text(verbatim: " ") + Text((ByteCountFormatter.string(fromByteCount: value, countStyle: .binary))) + } + } + + private func traverseFiles(in dir: URL) -> [String: Int64] { + var res: [String: Int64] = [:] + let fm = FileManager.default + do { + if let enumerator = fm.enumerator(at: dir, includingPropertiesForKeys: [.isDirectoryKey, .fileSizeKey, .fileAllocatedSizeKey]) { + for case let url as URL in enumerator { + let attrs = try url.resourceValues(forKeys: [/*.isDirectoryKey, .fileSizeKey,*/ .fileAllocatedSizeKey]) + let root = String(url.absoluteString.replacingOccurrences(of: dir.absoluteString, with: "").split(separator: "/")[0]) + res[root] = (res[root] ?? 0) + Int64(attrs.fileAllocatedSize ?? 0) + } + } + } catch { + logger.error("Error traversing files: \(error)") + } + return res + } +} diff --git a/apps/ios/Shared/Views/UserSettings/UserAddressLearnMore.swift b/apps/ios/Shared/Views/UserSettings/UserAddressLearnMore.swift index 15f6a1c7d7..6c1ea8deb2 100644 --- a/apps/ios/Shared/Views/UserSettings/UserAddressLearnMore.swift +++ b/apps/ios/Shared/Views/UserSettings/UserAddressLearnMore.swift @@ -9,15 +9,94 @@ import SwiftUI struct UserAddressLearnMore: View { + @State var showCreateAddressButton = false + @State private var createAddressLinkActive = false + @State private var createOneTimeLinkActive = false + var body: some View { - List { - VStack(alignment: .leading, spacing: 18) { - Text("You can share your address as a link or QR code - anybody can connect to you.") - Text("You won't lose your contacts if you later delete your address.") - Text("When people request to connect, you can accept or reject it.") - Text("Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).") + VStack { + List { + VStack(alignment: .leading, spacing: 12) { + (Text(Image(systemName: "envelope")).foregroundColor(.secondary) + textSpace + Text("Share address publicly").bold().font(.title2)) + Text("Share SimpleX address on social media.") + Text("You won't lose your contacts if you later delete your address.") + + (Text(Image(systemName: "link.badge.plus")).foregroundColor(.secondary) + textSpace + Text("Share 1-time link with a friend").font(.title2).bold()) + .padding(.top) + Text("1-time link can be used *with one contact only* - share in person or via any messenger.") + Text("You can set connection name, to remember who the link was shared with.") + + if !showCreateAddressButton { + (Text(Image(systemName: "shield")).foregroundColor(.secondary) + textSpace + Text("Connection security").font(.title2).bold()) + .padding(.top) + Text("SimpleX address and 1-time links are safe to share via any messenger.") + Text("To protect against your link being replaced, you can compare contact security codes.") + Text("Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses).") + .padding(.top) + } + + } + .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) } - .listRowBackground(Color.clear) + .frame(maxHeight: .infinity, alignment: .top) + + Spacer() + + if showCreateAddressButton { + VStack { + addressCreationButton() + .padding(.bottom) + + createOneTimeLinkButton() + } + .padding() + } + } + .frame(maxHeight: .infinity, alignment: .top) + } + + private func addressCreationButton() -> some View { + ZStack { + Button { + createAddressLinkActive = true + } label: { + Text("Create SimpleX address") + } + .buttonStyle(OnboardingButtonStyle()) + + NavigationLink(isActive: $createAddressLinkActive) { + UserAddressView(autoCreate: true) + .navigationTitle("SimpleX address") + .navigationBarTitleDisplayMode(.large) + } label: { + EmptyView() + } + .frame(width: 1, height: 1) + .hidden() + } + } + + private func createOneTimeLinkButton() -> some View { + ZStack { + Button { + createOneTimeLinkActive = true + } label: { + Text("Create 1-time link") + .font(.callout) + } + + NavigationLink(isActive: $createOneTimeLinkActive) { + NewChatView(selection: .invite) + .navigationTitle("New chat") + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } label: { + EmptyView() + } + .frame(width: 1, height: 1) + .hidden() } } } diff --git a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift index 96eeffd16d..4813edf96c 100644 --- a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift @@ -8,61 +8,40 @@ import SwiftUI import MessageUI -import SimpleXChat +@preconcurrency import SimpleXChat struct UserAddressView: View { @Environment(\.dismiss) var dismiss: DismissAction @EnvironmentObject private var chatModel: ChatModel - @State var viaCreateLinkView = false + @EnvironmentObject var theme: AppTheme @State var shareViaProfile = false + @State var autoCreate = false + @State private var showShortLink = true @State private var aas = AutoAcceptState() @State private var savedAAS = AutoAcceptState() - @State private var ignoreShareViaProfileChange = false @State private var showMailView = false @State private var mailViewResult: Result? = nil @State private var alert: UserAddressAlert? - @State private var showSaveDialogue = false @State private var progressIndicator = false - @FocusState private var keyboardVisible: Bool private enum UserAddressAlert: Identifiable { case deleteAddress - case profileAddress(on: Bool) case shareOnCreate - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { case .deleteAddress: return "deleteAddress" - case let .profileAddress(on): return "profileAddress \(on)" case .shareOnCreate: return "shareOnCreate" case let .error(title, _): return "error \(title)" } } } - + var body: some View { ZStack { - if viaCreateLinkView { - userAddressScrollView() - } else { - userAddressScrollView() - .modifier(BackButton(disabled: Binding.constant(false)) { - if savedAAS == aas { - dismiss() - } else { - keyboardVisible = false - showSaveDialogue = true - } - }) - .confirmationDialog("Save settings?", isPresented: $showSaveDialogue) { - Button("Save auto-accept settings") { - saveAAS() - dismiss() - } - Button("Exit without saving") { dismiss() } - } - } + userAddressView() + if progressIndicator { ZStack { if chatModel.userAddress != nil { @@ -75,22 +54,10 @@ struct UserAddressView: View { } } } - } - - @Namespace private var bottomID - - private func userAddressScrollView() -> some View { - ScrollViewReader { proxy in - userAddressView() - .onChange(of: keyboardVisible) { _ in - if keyboardVisible { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - withAnimation { - proxy.scrollTo(bottomID, anchor: .top) - } - } - } - } + .onAppear { + if chatModel.userAddress == nil, autoCreate { + createAddress() + } } } @@ -102,14 +69,19 @@ struct UserAddressView: View { aas = AutoAcceptState(userAddress: userAddress) savedAAS = aas } - .onChange(of: aas.enable) { _ in - if !aas.enable { aas = AutoAcceptState() } - } } else { Section { createAddressButton() - } footer: { - Text("Create an address to let people connect with you.") + } header: { + Text("For social media") + .foregroundColor(theme.colors.secondary) + } + + Section { + createOneTimeLinkButton() + } header: { + Text("Or to share privately") + .foregroundColor(theme.colors.secondary) } Section { @@ -124,8 +96,8 @@ struct UserAddressView: View { title: Text("Delete address?"), message: shareViaProfile - ? Text("All your contacts will remain connected. Profile update will be sent to your contacts.") - : Text("All your contacts will remain connected."), + ? Text("All your contacts will remain connected. Profile update will be sent to your contacts.") + : Text("All your contacts will remain connected."), primaryButton: .destructive(Text("Delete")) { progressIndicator = true Task { @@ -135,7 +107,6 @@ struct UserAddressView: View { chatModel.userAddress = nil chatModel.updateUser(u) if shareViaProfile { - ignoreShareViaProfileChange = true shareViaProfile = false } } @@ -148,96 +119,108 @@ struct UserAddressView: View { } }, secondaryButton: .cancel() ) - case let .profileAddress(on): - if on { - return Alert( - title: Text("Share address with contacts?"), - message: Text("Profile update will be sent to your contacts."), - primaryButton: .default(Text("Share")) { - setProfileAddress(on) - }, secondaryButton: .cancel() { - ignoreShareViaProfileChange = true - shareViaProfile = !on - } - ) - } else { - return Alert( - title: Text("Stop sharing address?"), - message: Text("Profile update will be sent to your contacts."), - primaryButton: .default(Text("Stop sharing")) { - setProfileAddress(on) - }, secondaryButton: .cancel() { - ignoreShareViaProfileChange = true - shareViaProfile = !on - } - ) - } case .shareOnCreate: return Alert( title: Text("Share address with contacts?"), message: Text("Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts."), primaryButton: .default(Text("Share")) { - setProfileAddress(true) - ignoreShareViaProfileChange = true + setProfileAddress($progressIndicator, true) shareViaProfile = true }, secondaryButton: .cancel() ) case let .error(title, error): - return Alert(title: Text(title), message: Text(error)) + return mkAlert(title: title, message: error) } } } @ViewBuilder private func existingAddressView(_ userAddress: UserContactLink) -> some View { Section { - SimpleXLinkQRCode(uri: userAddress.connReqContact) - .id("simplex-contact-address-qrcode-\(userAddress.connReqContact)") + SimpleXCreatedLinkQRCode(link: userAddress.connLinkContact, short: $showShortLink) + .id("simplex-contact-address-qrcode-\(userAddress.connLinkContact.simplexChatUri(short: showShortLink))") shareQRCodeButton(userAddress) - if MFMailComposeViewController.canSendMail() { - shareViaEmailButton(userAddress) + // if MFMailComposeViewController.canSendMail() { + // shareViaEmailButton(userAddress) + // } + settingsRow("briefcase", color: theme.colors.secondary) { + Toggle("Business address", isOn: $aas.business) + .onChange(of: aas.business) { ba in + if ba { + aas.enable = true + aas.incognito = false + } + saveAAS($aas, $savedAAS) + } } - shareWithContactsButton() - autoAcceptToggle() - learnMoreButton() + addressSettingsButton(userAddress) } header: { - Text("Address") + ToggleShortLinkHeader(text: Text("For social media"), link: userAddress.connLinkContact, short: $showShortLink) + } footer: { + if aas.business { + Text("Add your team members to the conversations.") + .foregroundColor(theme.colors.secondary) + } } - if aas.enable { - autoAcceptSection() + Section { + createOneTimeLinkButton() + } header: { + Text("Or to share privately") + .foregroundColor(theme.colors.secondary) + } + + Section { + learnMoreButton() } Section { deleteAddressButton() } footer: { Text("Your contacts will remain connected.") + .foregroundColor(theme.colors.secondary) } - .id(bottomID) } private func createAddressButton() -> some View { Button { - progressIndicator = true - Task { - do { - let connReqContact = try await apiCreateUserAddress() - DispatchQueue.main.async { - chatModel.userAddress = UserContactLink(connReqContact: connReqContact) - alert = .shareOnCreate - progressIndicator = false - } - } catch let error { - logger.error("UserAddressView apiCreateUserAddress: \(responseError(error))") - let a = getErrorAlert(error, "Error creating address") - alert = .error(title: a.title, error: a.message) - await MainActor.run { progressIndicator = false } - } - } + createAddress() } label: { Label("Create SimpleX address", systemImage: "qrcode") } } + private func createAddress() { + progressIndicator = true + Task { + do { + let short = UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS) + let connLinkContact = try await apiCreateUserAddress(short: short) + DispatchQueue.main.async { + chatModel.userAddress = UserContactLink(connLinkContact: connLinkContact) + alert = .shareOnCreate + progressIndicator = false + } + } catch let error { + logger.error("UserAddressView apiCreateUserAddress: \(responseError(error))") + let a = getErrorAlert(error, "Error creating address") + alert = .error(title: a.title, error: a.message) + await MainActor.run { progressIndicator = false } + } + } + } + + private func createOneTimeLinkButton() -> some View { + NavigationLink { + NewChatView(selection: .invite) + .navigationTitle("New chat") + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } label: { + Label("Create 1-time link", systemImage: "link.badge.plus") + .foregroundColor(theme.colors.primary) + } + } + private func deleteAddressButton() -> some View { Button(role: .destructive) { alert = .deleteAddress @@ -249,9 +232,9 @@ struct UserAddressView: View { private func shareQRCodeButton(_ userAddress: UserContactLink) -> some View { Button { - showShareSheet(items: [simplexChatLink(userAddress.connReqContact)]) + showShareSheet(items: [simplexChatLink(userAddress.connLinkContact.simplexChatUri(short: showShortLink))]) } label: { - settingsRow("square.and.arrow.up") { + settingsRow("square.and.arrow.up", color: theme.colors.secondary) { Text("Share address") } } @@ -261,7 +244,7 @@ struct UserAddressView: View { Button { showMailView = true } label: { - settingsRow("envelope") { + settingsRow("envelope", color: theme.colors.secondary) { Text("Invite friends") } } @@ -287,110 +270,250 @@ struct UserAddressView: View { } } - private func autoAcceptToggle() -> some View { - settingsRow("checkmark") { - Toggle("Auto-accept", isOn: $aas.enable) - .onChange(of: aas.enable) { _ in - saveAAS() - } + private func addressSettingsButton(_ userAddress: UserContactLink) -> some View { + NavigationLink { + UserAddressSettingsView(shareViaProfile: $shareViaProfile) + .navigationTitle("Address settings") + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } label: { + Text("Address settings") } } private func learnMoreButton() -> some View { NavigationLink { UserAddressLearnMore() - .navigationTitle("SimpleX address") - .navigationBarTitleDisplayMode(.large) + .navigationTitle("Address or 1-time link?") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.inline) } label: { - settingsRow("info.circle") { - Text("About SimpleX address") + settingsRow("info.circle", color: theme.colors.secondary) { + Text("SimpleX address or 1-time link?") + } + } + } +} + +struct ToggleShortLinkHeader: View { + @EnvironmentObject var theme: AppTheme + let text: Text + var link: CreatedConnLink + @Binding var short: Bool + + var body: some View { + if link.connShortLink == nil { + text.foregroundColor(theme.colors.secondary) + } else { + HStack { + text.foregroundColor(theme.colors.secondary) + Spacer() + Text(short ? "Full link" : "Short link") + .textCase(.none) + .foregroundColor(theme.colors.primary) + .onTapGesture { short.toggle() } + } + } + } +} + +private struct AutoAcceptState: Equatable { + var enable = false + var incognito = false + var business = false + var welcomeText = "" + + init(enable: Bool = false, incognito: Bool = false, business: Bool = false, welcomeText: String = "") { + self.enable = enable + self.incognito = incognito + self.business = business + self.welcomeText = welcomeText + } + + init(userAddress: UserContactLink) { + if let aa = userAddress.autoAccept { + enable = true + incognito = aa.acceptIncognito + business = aa.businessAddress + if let msg = aa.autoReply { + welcomeText = msg.text + } else { + welcomeText = "" + } + } else { + enable = false + incognito = false + business = false + welcomeText = "" + } + } + + var autoAccept: AutoAccept? { + if enable { + var autoReply: MsgContent? = nil + let s = welcomeText.trimmingCharacters(in: .whitespacesAndNewlines) + if s != "" { autoReply = .text(s) } + return AutoAccept(businessAddress: business, acceptIncognito: incognito, autoReply: autoReply) + } + return nil + } +} + +private func setProfileAddress(_ progressIndicator: Binding, _ on: Bool) { + progressIndicator.wrappedValue = true + Task { + do { + if let u = try await apiSetProfileAddress(on: on) { + DispatchQueue.main.async { + ChatModel.shared.updateUser(u) + } + } + await MainActor.run { progressIndicator.wrappedValue = false } + } catch let error { + logger.error("apiSetProfileAddress: \(responseError(error))") + await MainActor.run { progressIndicator.wrappedValue = false } + } + } +} + +struct UserAddressSettingsView: View { + @Environment(\.dismiss) var dismiss: DismissAction + @EnvironmentObject var theme: AppTheme + @Binding var shareViaProfile: Bool + @State private var aas = AutoAcceptState() + @State private var savedAAS = AutoAcceptState() + @State private var ignoreShareViaProfileChange = false + @State private var progressIndicator = false + @FocusState private var keyboardVisible: Bool + + var body: some View { + ZStack { + if let userAddress = ChatModel.shared.userAddress { + userAddressSettingsView() + .onAppear { + aas = AutoAcceptState(userAddress: userAddress) + savedAAS = aas + } + .onChange(of: aas.enable) { aasEnabled in + if !aasEnabled { aas = AutoAcceptState() } + } + .onDisappear { + if savedAAS != aas { + showAlert( + title: NSLocalizedString("Auto-accept settings", comment: "alert title"), + message: NSLocalizedString("Settings were changed.", comment: "alert message"), + buttonTitle: NSLocalizedString("Save", comment: "alert button"), + buttonAction: { saveAAS($aas, $savedAAS) }, + cancelButton: true + ) + } + } + } else { + Text(String("Error opening address settings")) + } + if progressIndicator { + ProgressView().scaleEffect(2) + } + } + } + + private func userAddressSettingsView() -> some View { + List { + Section { + shareWithContactsButton() + autoAcceptToggle().disabled(aas.business) + } + + if aas.enable { + autoAcceptSection() } } } private func shareWithContactsButton() -> some View { - settingsRow("person") { + settingsRow("person", color: theme.colors.secondary) { Toggle("Share with contacts", isOn: $shareViaProfile) .onChange(of: shareViaProfile) { on in if ignoreShareViaProfileChange { ignoreShareViaProfileChange = false } else { - alert = .profileAddress(on: on) + if on { + showAlert( + NSLocalizedString("Share address with contacts?", comment: "alert title"), + message: NSLocalizedString("Profile update will be sent to your contacts.", comment: "alert message"), + actions: {[ + UIAlertAction( + title: NSLocalizedString("Cancel", comment: "alert action"), + style: .default, + handler: { _ in + ignoreShareViaProfileChange = true + shareViaProfile = !on + } + ), + UIAlertAction( + title: NSLocalizedString("Share", comment: "alert action"), + style: .default, + handler: { _ in + setProfileAddress($progressIndicator, on) + } + ) + ]} + ) + } else { + showAlert( + NSLocalizedString("Stop sharing address?", comment: "alert title"), + message: NSLocalizedString("Profile update will be sent to your contacts.", comment: "alert message"), + actions: {[ + UIAlertAction( + title: NSLocalizedString("Cancel", comment: "alert action"), + style: .default, + handler: { _ in + ignoreShareViaProfileChange = true + shareViaProfile = !on + } + ), + UIAlertAction( + title: NSLocalizedString("Stop sharing", comment: "alert action"), + style: .default, + handler: { _ in + setProfileAddress($progressIndicator, on) + } + ) + ]} + ) + } } } } } - private func setProfileAddress(_ on: Bool) { - progressIndicator = true - Task { - do { - if let u = try await apiSetProfileAddress(on: on) { - DispatchQueue.main.async { - chatModel.updateUser(u) - } + private func autoAcceptToggle() -> some View { + settingsRow("checkmark", color: theme.colors.secondary) { + Toggle("Auto-accept", isOn: $aas.enable) + .onChange(of: aas.enable) { _ in + saveAAS($aas, $savedAAS) } - await MainActor.run { progressIndicator = false } - } catch let error { - logger.error("UserAddressView apiSetProfileAddress: \(responseError(error))") - await MainActor.run { progressIndicator = false } - } } } - private struct AutoAcceptState: Equatable { - var enable = false - var incognito = false - var welcomeText = "" - - init(enable: Bool = false, incognito: Bool = false, welcomeText: String = "") { - self.enable = enable - self.incognito = incognito - self.welcomeText = welcomeText - } - - init(userAddress: UserContactLink) { - if let aa = userAddress.autoAccept { - enable = true - incognito = aa.acceptIncognito - if let msg = aa.autoReply { - welcomeText = msg.text - } else { - welcomeText = "" - } - } else { - enable = false - incognito = false - welcomeText = "" - } - } - - var autoAccept: AutoAccept? { - if enable { - var autoReply: MsgContent? = nil - let s = welcomeText.trimmingCharacters(in: .whitespacesAndNewlines) - if s != "" { autoReply = .text(s) } - return AutoAccept(acceptIncognito: incognito, autoReply: autoReply) - } - return nil - } - } - - @ViewBuilder private func autoAcceptSection() -> some View { + private func autoAcceptSection() -> some View { Section { - acceptIncognitoToggle() + if !aas.business { + acceptIncognitoToggle() + } welcomeMessageEditor() saveAASButton() .disabled(aas == savedAAS) } header: { Text("Auto-accept") + .foregroundColor(theme.colors.secondary) } } private func acceptIncognitoToggle() -> some View { settingsRow( aas.incognito ? "theatermasks.fill" : "theatermasks", - color: aas.incognito ? .indigo : .secondary + color: aas.incognito ? .indigo : theme.colors.secondary ) { Toggle("Accept incognito", isOn: $aas.incognito) } @@ -401,7 +524,7 @@ struct UserAddressView: View { Group { if aas.welcomeText.isEmpty { TextEditor(text: Binding.constant(NSLocalizedString("Enter welcome message… (optional)", comment: "placeholder"))) - .foregroundColor(.secondary) + .foregroundColor(theme.colors.secondary) .disabled(true) } TextEditor(text: $aas.welcomeText) @@ -417,22 +540,24 @@ struct UserAddressView: View { private func saveAASButton() -> some View { Button { keyboardVisible = false - saveAAS() + saveAAS($aas, $savedAAS) } label: { Text("Save") } } +} - private func saveAAS() { - Task { - do { - if let address = try await userAddressAutoAccept(aas.autoAccept) { - chatModel.userAddress = address - savedAAS = aas +private func saveAAS(_ aas: Binding, _ savedAAS: Binding) { + Task { + do { + if let address = try await userAddressAutoAccept(aas.wrappedValue.autoAccept) { + await MainActor.run { + ChatModel.shared.userAddress = address + savedAAS.wrappedValue = aas.wrappedValue } - } catch let error { - logger.error("userAddressAutoAccept error: \(responseError(error))") } + } catch let error { + logger.error("userAddressAutoAccept error: \(responseError(error))") } } } @@ -440,7 +565,9 @@ struct UserAddressView: View { struct UserAddressView_Previews: PreviewProvider { static var previews: some View { let chatModel = ChatModel() - chatModel.userAddress = UserContactLink(connReqContact: "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D") + chatModel.userAddress = UserContactLink(connLinkContact: CreatedConnLink(connFullLink: "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D", connShortLink: nil)) + + return Group { UserAddressView() .environmentObject(chatModel) diff --git a/apps/ios/Shared/Views/UserSettings/UserProfile.swift b/apps/ios/Shared/Views/UserSettings/UserProfile.swift index e5ec23178d..9aa42930bf 100644 --- a/apps/ios/Shared/Views/UserSettings/UserProfile.swift +++ b/apps/ios/Shared/Views/UserSettings/UserProfile.swift @@ -11,8 +11,11 @@ import SimpleXChat struct UserProfile: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme + @AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner @State private var profile = Profile(displayName: "", fullName: "") - @State private var editProfile = false + @State private var currentProfileHash: Int? + // Modals @State private var showChooseSource = false @State private var showImagePicker = false @State private var showTakePhoto = false @@ -21,85 +24,86 @@ struct UserProfile: View { @FocusState private var focusDisplayName var body: some View { - let user: User = chatModel.currentUser! - - return VStack(alignment: .leading) { - Text("Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile.") - .padding(.bottom) - - if editProfile { - ZStack(alignment: .center) { - ZStack(alignment: .topTrailing) { + List { + Group { + if profile.image != nil { + ZStack(alignment: .bottomTrailing) { + ZStack(alignment: .topTrailing) { + profileImageView(profile.image) + .onTapGesture { showChooseSource = true } + overlayButton("multiply", edge: .top) { profile.image = nil } + } + overlayButton("camera", edge: .bottom) { showChooseSource = true } + } + } else { + ZStack(alignment: .center) { profileImageView(profile.image) - if user.image != nil { - Button { - profile.image = nil - } label: { - Image(systemName: "multiply") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 12) - } + editImageButton { showChooseSource = true } + } + } + } + .frame(maxWidth: .infinity, alignment: .center) + .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + .padding(.top) + .contentShape(Rectangle()) + + Section { + HStack { + TextField("Enter your name…", text: $profile.displayName) + .focused($focusDisplayName) + if !validDisplayName(profile.displayName) { + Button { + alert = .invalidNameError(validName: mkValidName(profile.displayName)) + } label: { + Image(systemName: "exclamationmark.circle").foregroundColor(.red) } } - - editImageButton { showChooseSource = true } } - .frame(maxWidth: .infinity, alignment: .center) - - VStack(alignment: .leading) { - ZStack(alignment: .leading) { - if !validNewProfileName(user) { - Button { - alert = .invalidNameError(validName: mkValidName(profile.displayName)) - } label: { - Image(systemName: "exclamationmark.circle").foregroundColor(.red) - } - } else { - Image(systemName: "exclamationmark.circle").foregroundColor(.clear) - } - profileNameTextEdit("Profile name", $profile.displayName) - .focused($focusDisplayName) - } - .padding(.bottom) - if showFullName(user) { - profileNameTextEdit("Full name (optional)", $profile.fullName) - .padding(.bottom) - } - HStack(spacing: 20) { - Button("Cancel") { editProfile = false } - Button("Save (and notify contacts)") { saveProfile() } - .disabled(!canSaveProfile(user)) - } + if let user = chatModel.currentUser, showFullName(user) { + TextField("Full name (optional)", text: $profile.fullName) } - .frame(maxWidth: .infinity, minHeight: 120, alignment: .leading) - } else { - ZStack(alignment: .center) { - profileImageView(user.image) - .onTapGesture { startEditingImage(user) } + } footer: { + Text("Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile.") + } - if user.image == nil { - editImageButton { startEditingImage(user) } - } + Section { + Button(action: getCurrentProfile) { + Text("Reset") } - .frame(maxWidth: .infinity, alignment: .center) - - VStack(alignment: .leading) { - profileNameView("Profile name:", user.profile.displayName) - if showFullName(user) { - profileNameView("Full name:", user.profile.fullName) - } - Button("Edit") { - profile = fromLocalProfile(user.profile) - editProfile = true - focusDisplayName = true - } + .disabled(currentProfileHash == profile.hashValue) + Button(action: saveProfile) { + Text("Save (and notify contacts)") } - .frame(maxWidth: .infinity, minHeight: 120, alignment: .leading) + .disabled(!canSaveProfile) } } - .padding() - .frame(maxHeight: .infinity, alignment: .top) + // Lifecycle + .onAppear { + getCurrentProfile() + } + .onDisappear { + if canSaveProfile { + showAlert( + title: NSLocalizedString("Save your profile?", comment: "alert title"), + message: NSLocalizedString("Your profile was changed. If you save it, the updated profile will be sent to all your contacts.", comment: "alert message"), + buttonTitle: NSLocalizedString("Save (and notify contacts)", comment: "alert button"), + buttonAction: saveProfile, + cancelButton: true + ) + } + } + .onChange(of: chosenImage) { image in + Task { + let resized: String? = if let image { + await resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) + } else { + nil + } + await MainActor.run { profile.image = resized } + } + } + // Modals .confirmationDialog("Profile image", isPresented: $showChooseSource, titleVisibility: .visible) { Button("Take picture") { showTakePhoto = true @@ -126,57 +130,48 @@ struct UserProfile: View { } } } - .onChange(of: chosenImage) { image in - if let image = image { - profile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) - } else { - profile.image = nil - } - } .alert(item: $alert) { a in userProfileAlert(a, $profile.displayName) } } - func profileNameTextEdit(_ label: LocalizedStringKey, _ name: Binding) -> some View { - TextField(label, text: name) - .padding(.leading, 32) - } - - func profileNameView(_ label: LocalizedStringKey, _ name: String) -> some View { - HStack { - Text(label) - Text(name).fontWeight(.bold) - } - .padding(.bottom) - } - - func startEditingImage(_ user: User) { - profile = fromLocalProfile(user.profile) - editProfile = true - showChooseSource = true - } - - private func validNewProfileName(_ user: User) -> Bool { - profile.displayName == user.profile.displayName || validDisplayName(profile.displayName.trimmingCharacters(in: .whitespaces)) + private func overlayButton( + _ systemName: String, + edge: Edge.Set, + action: @escaping () -> Void + ) -> some View { + Image(systemName: systemName) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 12) + .foregroundColor(theme.colors.primary) + .padding(6) + .frame(width: 36, height: 36, alignment: .center) + .background(radius >= 20 ? Color.clear : theme.colors.background.opacity(0.5)) + .clipShape(Circle()) + .contentShape(Circle()) + .padding([.trailing, edge], -12) + .onTapGesture(perform: action) } private func showFullName(_ user: User) -> Bool { user.profile.fullName != "" && user.profile.fullName != user.profile.displayName } - - private func canSaveProfile(_ user: User) -> Bool { - profile.displayName.trimmingCharacters(in: .whitespaces) != "" && validNewProfileName(user) + + private var canSaveProfile: Bool { + currentProfileHash != profile.hashValue && + profile.displayName.trimmingCharacters(in: .whitespaces) != "" && + validDisplayName(profile.displayName) } - func saveProfile() { + private func saveProfile() { + focusDisplayName = false Task { do { profile.displayName = profile.displayName.trimmingCharacters(in: .whitespaces) if let (newProfile, _) = try await apiUpdateProfile(profile: profile) { - DispatchQueue.main.async { + await MainActor.run { chatModel.updateCurrentUser(newProfile) - profile = newProfile + getCurrentProfile() } - editProfile = false } else { alert = .duplicateUserError } @@ -185,12 +180,17 @@ struct UserProfile: View { } } } + + private func getCurrentProfile() { + if let user = chatModel.currentUser { + profile = fromLocalProfile(user.profile) + currentProfileHash = profile.hashValue + } + } } func profileImageView(_ imageStr: String?) -> some View { - ProfileImage(imageStr: imageStr) - .aspectRatio(1, contentMode: .fit) - .frame(maxWidth: 192, maxHeight: 192) + ProfileImage(imageStr: imageStr, size: 192) } func editImageButton(action: @escaping () -> Void) -> some View { @@ -203,19 +203,3 @@ func editImageButton(action: @escaping () -> Void) -> some View { .frame(width: 48) } } - -struct UserProfile_Previews: PreviewProvider { - static var previews: some View { - let chatModel1 = ChatModel() - chatModel1.currentUser = User.sampleData - let chatModel2 = ChatModel() - chatModel2.currentUser = User.sampleData - chatModel2.currentUser?.profile.image = "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBMRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAAqACAAQAAAABAAAAgKADAAQAAAABAAAAgAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/+ICNElDQ19QUk9GSUxFAAEBAAACJGFwcGwEAAAAbW50clJHQiBYWVogB+EABwAHAA0AFgAgYWNzcEFQUEwAAAAAQVBQTAAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA0y1hcHBsyhqVgiV/EE04mRPV0eoVggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKZGVzYwAAAPwAAABlY3BydAAAAWQAAAAjd3RwdAAAAYgAAAAUclhZWgAAAZwAAAAUZ1hZWgAAAbAAAAAUYlhZWgAAAcQAAAAUclRSQwAAAdgAAAAgY2hhZAAAAfgAAAAsYlRSQwAAAdgAAAAgZ1RSQwAAAdgAAAAgZGVzYwAAAAAAAAALRGlzcGxheSBQMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0ZXh0AAAAAENvcHlyaWdodCBBcHBsZSBJbmMuLCAyMDE3AABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAACD3wAAPb////+7WFlaIAAAAAAAAEq/AACxNwAACrlYWVogAAAAAAAAKDgAABELAADIuXBhcmEAAAAAAAMAAAACZmYAAPKnAAANWQAAE9AAAApbc2YzMgAAAAAAAQxCAAAF3v//8yYAAAeTAAD9kP//+6L///2jAAAD3AAAwG7/wAARCACAAIADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9sAQwABAQEBAQECAQECAwICAgMEAwMDAwQGBAQEBAQGBwYGBgYGBgcHBwcHBwcHCAgICAgICQkJCQkLCwsLCwsLCwsL/9sAQwECAgIDAwMFAwMFCwgGCAsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL/90ABAAI/9oADAMBAAIRAxEAPwD4N1TV59SxpunRtBb/APPP/lo+eMsf4R+uKyxNa6Y32a3UTzjoi8Ip9/8AOfYV0tx4d1a8VlsojaWo6uThj+Pb6Cs2CCGyP2LQ4xPIMBpGIVVz7ngV+Ap31P2C1iSDQbnWXRtVYyMT8kSDkZ9B29zXXReD7ZVOkX0QlLgg2ycjBH8ZHXPoOK9O8L6LpljZidWMjyqMzAdc/wB3PJ+p4qjrPiuxs1a38LwLJIn35ScoP94jlm9hxW8ZKJm1fY/Gv4yeA/E37L3xf07xz4GuH0260+7i1bRLpDkwzQOHVfQ+WwAI7r1zmv7fv2Nv2nfCv7YH7PHh346+FwkD6nEYtRs1OTZ6jBhbiA98K/zJnrGynvX8u3x3+G6fFvwXcadcOZNTQebZyN1EgH3QB91W6H657VD/AMEYP2qdQ/Zb/aRuPgN8RpjZeFviJcJabJztWy1tPkgkOeFE3+ok9zGTwtfY5Nj1Vjyt6nzuZ4XlfMj+zamH5TupVYnhhgjsaRyMYNe8eEMC7jxxU+1SMYFQFyaevPWgRqaeuSVFb0SDgAZI/SsLS9w4kxux1HTNdTEAMDvQJst20UitvA4rotMh8ycbuAv6k1Rs3UgcHjrXc6Xb2iTKVIJPQEcZ96qKMW7nWabpNmzRyEE9wOlegtplzFCLiMbEcfKw5/XP51l6ZPK6b2SJsdd64A/Kr0t5fyRsqsPLU5baNo49P0q2I//Q8iuPD17eeTpVy32u2ufls5lAC5P8MmOA2O/Q/XIrHl+GWn+CGN7qyC9ugxkSID92nvz1+pwK/TKb9j34t3Pw/PjXXrpdR165L3F7pkiDz5RISzHzFIUzliXKBQCTgMGwD8P6zompRzR2V2xuLWV9sE7ggo4yPLlBxhgRgE8k8cHivyPPMl9g3iMMrw6r+X/gH6PlmZ+1tRrP3uj7/wDBPnjXdR1rXWDao5jtm4S3h43gf3jwSPyH1rW0Xw9f6uyw2MYSNAAT/Ag/qa9ii+GTWEv2nV8nfztH3m/+t/nirMsVtMPscGIYYuCqjj8fWvmo+9qz227aI5O38NeH/DeJIGE079ZW9fQf/W/Ovyx/ba+C1x/aR+K/h6FoLa5dUvDH8rRzj7kgI+7ux253DPev1yuINKtF3XriOMDlm+83+6O1eNePZoPH2h3ngWC032N7E0UhI7HuPcdQfWvQweJdKakjkxFFTjZn6+f8Eu/2yE/a+/Zss9R8TXCyeMvCpTSfECZ+eSZF/dXWPS5jG4n/AJ6Bx2r9JGbd0r+GX9jD476z/wAE5v20IL3xPM7eGdUZdK8QBeUewmYGO6A7tbviT127171/cfaXdve28d1aSJNFKqukiHcjqwyGUjggggg9xX6Dhq6q01JM+NxVF05tdCyRQCOvakY4GRTFYd66DmN2xk2sK6eE5+YVxlo5EwB4rrLZiTyePWgmSOmsAThCcZPFdxZ5KruJyprgrWQ5G3tXS21+FABzVrYyZ6ZZTTSqCR8vQ4rUudWgW1e3QMrBScj1/D+tcpp1+UXaOn09fWtKP7OAzNjK+tNiP//R/oYjkSW9NgqsWVA7HHyrk4AJ9Tzx6CvjL9qz4M+FrbRrn4q2s0Fjcs6R3ttKdsd+ZCFBUf8APx0xj/WAYOCA1fVF58Y/hbb/AAwPxlXWIH8OCHzhdKc57bAv3vM3fLsxu3cYzX58eGdH8f8A7b/xIHi/xOs2k+DNGkK28AOCgPVQejXMg++/IiU7RyefmI+Z79+qPl++0JpR/wATG7Z9M4WOQfeVv7srdT/snp+NeWa9bfZXez8KxCZQcGVhiJT/AOzH6fnX7K/Fn9mfwzf6N9r+GmnwWV3DF5UlmBiC8iAxtbPAkx0c/e6N/eH5s+IvDcuj2jWcUTJYwsYXDrtktHXgxuvBxngE9Oh9/is6yVUr4nDL3Oq7enl+R9Plmac9qNZ+90ff/gnybLoheT7XrM3nMo5JH8h2HtXJa9/aGoMYbAC0gTqwH7x1H8hXsHiWGDRUboqr/Eeck+nrXj9/d3twWmlzbQHnn77e/tXzaqXXuntuNtz4z/ay+Eul+NPAf9u+H4TLq2kqzEAfNLAeXU/T7w/Ed6/XL/giD+2n/wALr+Ck37Nnjq78zxV8PYkW0Z2+a60VjthbJ5LWzfuW/wBjyz3NfCGuJLLm30tSsT8OT/U1+b1v4w8VfsE/tXeHf2kfhqjz2Vvcl5rdDiO4tZflu7Q+zoSUz0baeq19RkWMUZexk/Q8LNMLzx51uf3yIxPXvTQuTkVw3wz+IfhH4seBNG+JngS7W+0XX7OG/sp1P34ZlDLn0Izhh2YEGu+LAHFfXo+XJ4P9cp6YNdbCWHFcerFSCK6OGcMBk0wOmtZMVswurDNcnHKB7VqxXbDGKaZEoncRXpt4iy8fWlN44XdM5+bGPauWbUAI9p5NeH/E39oTwF8OAdO1W6+06kfuWVuQ0vtvOcIPdiPalOrGC5pOyHToym7RV2f/0nXmiaPrF/ceJvC1hrUnhC11EyFGZsIN2Mtg+QLjy+A5GQcZI6V/QP8ABrWvhd4i+GmnXXwZeI6DAnkxRxgq0LL95JFb5hJnO7dyTz3qt4f8EeCPC3g5Pht4csYItKt4fKNngMpjfOd4PJLckk8k18FeKvBXj79kHxu/xW+ECte+F711XUtNdiVC54VvQj/lnL2+63FfNNqWh7rVtT9JdItdaitpV8QSxyy+a5VowVURE/KDnuB1PQ9a/OD4yfEbwv8AEP4rx6F8JNIfXb4QyQXMlqAwvmQgEBThSkQBUysQpyFBOBjE+NH7WWu/HtrH4QfACxvYpNZHl3bSr5M7kjLQqc/JGo5ml/u8DrX2X+z38A9C+B3hzyQUvNbvVX7dehcA7ekUQ/hiT+Fe/U81m1bVj1Px/wDiX4FXQ4b7WNItJXitXZLq3nU+fpzjqpQ87PQ88eowa+JdanuvP+03JzG3Kk87voP8a/pi+NPwStfiAo8V+GDHaeI7aPYsjj91dxj/AJYzjuOyv1X6V+Mfxk+By6eL7xPodhLE9kzDUNJYfvbSXqWUd4z147cjivjc3ybkviMMtOq7eaPo8tzXmtRrvXo/8z4aaC/1a3drrbDbr6nCgepPc+36V4T8Z/A/h7xz4KvPB8uGmcb4LhhxHKv3WUeh6HPY17TrMuo3dysUA3p0VUGEArCudFt7aH7bqjguOQP6V89SquLUk9T26lNNWZ7L/wAEJv2vNQ8L6xq/7BPxZma3ureafUPDHnHvy93Zg/X9/EO+XA7Cv6fFwRnNfwWftIWHi/wL4u0T9pX4Vu2ma74buobpJY+GEkDBo5CO4B+Vx3U4PFf2VfshftPeFf2tv2e/Dvx18LbYhq0G29tQcm0vovluID/uPkr6oVPev0TLsWq9FT69T43MMN7KpdbM+q1kA+WtuF8qCa5H7SD0qvrnjbw34L0KTxD4qvobCyhBLzTuFUY7DPU+wya7nNJXZwxu3ZHoqyqq5JxXnPxL+Nvw3+EemjUPHmqxWIbPlxcvNIR2WNcsfrjFflz8cf8AgpDJMZ/DvwKgwOVOq3S/rFGf0LV8MaZp/jf4j603ibxTdT3U053PdXRLu+eflB7fkK8PFZ5TheNHV/h/wT2cLlFSfvVNF+J+hnxI/ba8cfEa5fQfhnG+h6e5KCY/NeTD6jIjH0yfcV514W8HX2plrjUiWLEtIWbcSSOS7dST/k1x2g2PhrwdZhpyFbHzEnLk+5/oK6eDxRq2soYdPH2S0xjjh2H9K+erY+pVlzTdz3aWEhSjaCsf/9P+gafwFajxovjGKeVJSqrJEPuOVUoD7ZBGR32ivgn9pz9pHUfGOvP+zb8BIDrGr6kZLO/nhwUXH34UY/LwP9bJ91BxndxXyp41/ab/AGivht4c1D9mf+0La7vrOY6f/asUpe4WP7vlRzEhRnIHmMNyAkcEcfpB+zB+zBo37O/hQ3moBL3xLfxA312gyFA5EEOeRGp79Xb5j2x8wfQHyHZ/CP41fsg6lZ/GHT3tvEVvDC0WqxwIU8uGUqXXnnaCoIlHQj5vlOR+lPwv+Lngv4v+Gk8UeC7oTRBvLnib5ZYJcZKSL1B9D0YcgkU/QfEkXitbuzuLR7S5tGCTwS4bAfO3kcEEA5B/lg1+Yn7Qdtbfsd/E/TPiT8IdShs21jzDc6HIf3TRIQWyB0hYnCE8xt9044Ckr7k7H7AiUEf4V438U/hZa+O0TXNGkWy120XbDcEfJKn/ADxmA+8h7Hqp5HpWN8Efjv4N+OvhFfFHhOTy5otqXlnIR51tKRnaw7g9VccMOnOQPXZ71Yo2mdgiqMsWOAAOufasXoyrXPw++NX7P9zHdX174Q0wWOqW/wC81DSjjMe7J86HHDxtgnC5zzjkEV+Z3iOS20u7PlZupiT+9YYQH/ZWv6hvjRp3grXPAJ8c3t6lldabGZLC/j5be3KxY/jSUgAp+IwRkfzs/tYan4Vi+LM8nhzyo5bq2gnu4Iukd04PmDI6ZGGIHc18hnmW06K+s09LvVefkfRZTjZ1H7Cetlo/8z5d1bQk1m1ng1OMTRXCGOVX+7tbg5+tQf8ABPL9o/xV/wAE9vi/r3gDxhYahrPw18WSrMJbGMzvZXcYwkyxjn5k/dyr1OFI6VqBpJ8LdPiM9gOv0FWFTzJBFbJtzgADliT0H515uAzKphpNxV0z0sVhIVo8sj9rviP/AMFJPhxpuhJ/wqm2n1rUbhcqbmJreKLP95T8zEeg/GvzP8Y/Eb4vftA+Ije+Kb2XUWU/JCDstoAewH3Rj8TXmOi+HrJYTd63MII1OPLB+d8diev4DtXtWjeIrPTNNENtD9mjx8kY+V2H0/hH60YzNK2IdpPTsthYXL6VHWK17s2/C3gHQvDCLqPiKRZ7hei/wKfYdz7mu9/4TGa5lEGjREA8Z7/5+lec2Ntf65KLm+IjhXkZ4UCunt9X0zTONN56gu39K4k2dtlueh6Xpdxcz/a9UfMi84J4X+grv7fxNaaehi0oCWUDDSH7o+leNW99f30fls3l2+eT0z61oDVFgiEOngtgY3Y/kP61pEln/9T74+Ff/BPn4e6R8MnsPieWvfFF+haS+gkbbZM3RIQeHA/jLjMhznAwBufCz42+Mf2bPEsHwM/aNlMmiONmj6+cmIRg4Cuxz+7GQMn5oicNlcGvWf2ffiB418d/Dfwn4tvR9st9StTb3IVVUxSw8NK7E5O4qRgeo46msH9tXx78JfAfwS1CL4oQx30l8ki6XZ5Ama7VTtkQ9UWPIMjdNvynO4A/NHvnqP7Rn7Q/gX9nLwY3iXVGiudR1BS2n2aOA102PvkjpEowWfpjgcmviz9nH9njxT8afFEn7SX7TkJvJL8+bp+mXSfIUP3JJIyPljUf6qI9vmPOK+DfgboFl4V+LfhHxt+1DpWoW/he7iL6bJfRt9mLpgwOwbOYIyd23sSrFdvX+iZ7n7bY+fpkqHzU3RSj50IYZVuDhh34PIqG7bBufnr8Zv2fvF3wa8Vf8L8/ZgQ20sAJ1DR4lLRPF1fbGPvRHGWjHKn5kxjFe8fDD9qX4Q/FL4cXni/V7uHS2sIv+JpYXLgyQE/3RwZEc8Rso+bpwcive/E/irQPBOgXfizxTeJYafp8ZmnnkOFRR+pJPAA5J4GTX8uP7Uf7R3hHWPilqfjDwNpo02HVZ8wWqL84jAAaVlHAeUguVHAY/Unnq1oU6bnVdkuv6GtOlKclCmtWfQn7X37bl7qEqaB4HRbaCyXytOssgiBTgedL281hzg9Onrn8xl1eNpJNQ1C4M00zGSSV23M7HqST1Oa5K7Np44uf7Psmkubp3M0hCjcG9ZGzjn1r3fwR8LrDRokvNaIlmABw3IU/l1/yBXwWZY+eJnzS0itl/XU+tweEjh4WW73ZmaHpev8AiNhJCjW9vjh2+8w9hXqVnpukeGoFe4cqVIJdjyT2/X86W+8U2ljG1rpCiRxxu6jNeO+IrbX9amEzuwERy3rz9eB/M15jdztSPQhr7ahrEt/b/Ky8bXHIz0bn1HPP4CvW/CsEUKNqOqybQ3zZb77n2z/OvnvS2khv4r5wZLiLAUADbx6jvjtmvWNGinvbn7TqjlyRnGcjNNR0DmPTZtYuNSxb2KlY+w7fX3rd063toHDTAzSj+H/H0+lYulwz3Moislx2yOD+n9KzvF3xX8C/DCIwXbi+1NvuWsJzhj/fPRRxVRRV7ntNlp91eRm61F1hgUZOTtVawtT+JGiaQDYeF4hf3J+Uyn/VqT6dya+GNb+M3j74i339n3rx2ttG2PItwwT2yxALH6ce9e3eGLXyLFcofN24wf6nsPYU9gP/1fof9kb9uf4LeBf2QYLjxVctDrujNcIdJAImuJHkYoIiRjaejFsbMHI6Zf8As+/BTxt+1l4/X9qT9pSPdpW4NoukOCIpI0OYyUPS3Q8qDzK3zNkdfkv/AIJ4/s0ah+0xZWv7Q3xmjik8PCZvstqgwuoSQnYC3cwJtwSeZmBz8uc/vtp3iPQrm+k0LT50M9oMNCo27QuFIXgAheAdudp4ODXzeyPfbIviJ4C8I/FLwnceCPHFmLvTrkdOjxOPuyRt/A69iPocgkV+dehfEbxr+wf4ot/hz8W5ZtZ+Hd+7DS9VRCz2h67CvoM/PFnK/eTK5FfpHrviHR/DejXXiDxBdRWNhYxNPcXEzBI4o0GWZieAAK/mw/bP/bF1n9pvxTH4a8DxvD4X0mZjYRSAo88pBQ3Uw6jKkiOP+FSc/MxxhUqQpwc6jtFFU6cqk1GCu2W/26f269Y+Nutnwv4KElv4cs5M2ds/ytcOOPtE2O/9xP4R7kmvz00L4e614kvTqniKR087qf429h/dH616Zofg/S/D+dW16Xz7k/MXbr9AO3+ea2W1q8v/AN1pqeTE3AYj5iPb/P4V8DmWZzxU9NILZfq/M+uwWCjh495dWa2jWPh7wZaC10+FFfsqD5ifUnrn3/WpbibUtVI+0Psj/uA449z/AErPjtrTTI/tepybc8kE5Ymse78UXV0fL0hPIjHG89fw9K8u3c7W7Grd38WjOEt0Blx95v4c+i/41iW5ur+VmvHIG7IHTmqscK2ymaY5dhnLck/Qf41sWlqyqZp3EWevrRZCu2bdgoUiCIYOeT3zXp2hrp+nRfb9VmWCFerP1PsB3NeNz+K9O0eApYr58q/xN0B9f/1VzZ1q/wBQv/td07Mw6lvT2HRR+pockhpHp3jv4q6pdwnR/CObKBxgyf8ALZx7dxXz5p+i6tPqryW8WXYHLSgso7/Oe59s16Np9rNdXTG0Uh24Z++Pr2H5n6V6LZ22k+HoFudVcBs/LHjv7L1J9z+lRzGyiM8IeCI7fZfXKguFUGRjkcDnaD/WvQrrxNYaQo0rSYzLMR25wfUn/P0rift2ueJG2RB7S3PRV/1jD3PRRj/9ddh4b0C1iJKAY/MZPv8AxH9KhS1Lt3P/1v0M/YPkRP2ZNBhiARY3uVCqMAAStwAOwr6budO8L6Fe3PjW/dbUQRySzTSSlII12jzJGBIRTtQbnwOBya+Lf+CevizRdf8A2VNH1vS7lJbQT3hMmcBQshJ3Z+7t75xivy7/AG6/27G+OWpy/CP4WXTL4OgfE9wmQ2qyIeG7H7MrfcU48w4Y8bRXy9ScYRc5uyW59BGEpT5YrUs/tq/tm6r+0x4gPw3+G9xJa+CdPmDM/KNqMiHiVxwfKB5ijPX77c4C/GVlc2eip9h0SLz5z94noD/tH/J9hXJaTZXUkGxT5MA5YZxnPdm9/QV1j3WmeHoFkuPk4+Vf4mHsP4R7n8q+DzTMpYufLHSC2/zZ9XgcFHDxu/iZaj0i6uZDqGtThtvJzwoqrdeJY7RzbaYuSRw7Dt7f5xXE6h4kvNamG/5YgcqmcLj1Pc/X8qtLAwQGPDyPzk9B/n0ryuXsdzkW5LyS4k8+/kLsx4X/AB/wFdFYxXVwyxW6gMe55Ix6Cm6Z4et7JTqevzCJj1Zu/wBBUepeNba3t2svDcflL/FPJyT9BSsuormlcPYaJGHuGM0zcjJrk7vUbvUZwJD8vO1Rwo/Dv+Ncvda3AP3s7FpHOSzHLE+w7Utm+q6uTFZDyo8/Mx6/WomWkb+baDDTPlj0ReSPqRnFdBpukXeptv2iK3Xl3Y4RQPU1mWkFhpOQF+0XAwCO+TnAJ6L9OvtViJNV8RShdTcC2j5ESfLEvufU/Xn0rNstRPQI9QtwgsfCyiYr/wAvLjEQP+yv8X1P610mj+H0WcXWpO1xeMOWbl8fyQU3RbbMSiyG1EH+sbjgf3R2+tdbamytrc3KnbErANM3OWPOAP4iR0qGzdGotg2xbNBktjKJk/p1P48fSuziOn6DBtuj5twekYP3Sf7xH8q8/ttbvriUw6eGgSTv/wAtZB65/hH0P49qll1PS9FJF0RLP2jU5xn1qLiP/9k=" - return Group { - UserProfile() - .environmentObject(chatModel1) - UserProfile() - .environmentObject(chatModel2) - } - } -} diff --git a/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift b/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift index f2cac59dae..887023b670 100644 --- a/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift @@ -8,7 +8,7 @@ import SimpleXChat struct UserProfilesView: View { @EnvironmentObject private var m: ChatModel - @Binding var showSettings: Bool + @EnvironmentObject private var theme: AppTheme @Environment(\.editMode) private var editMode @AppStorage(DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE) private var showHiddenProfilesNotice = true @AppStorage(DEFAULT_SHOW_MUTE_PROFILE_ALERT) private var showMuteProfileAlert = true @@ -21,6 +21,7 @@ struct UserProfilesView: View { @State private var profileHidden = false @State private var profileAction: UserProfileAction? @State private var actionPassword = "" + @State private var navigateToProfileCreate = false var trimmedSearchTextOrPassword: String { searchTextOrPassword.trimmingCharacters(in: .whitespaces)} @@ -29,7 +30,7 @@ struct UserProfilesView: View { case hiddenProfilesNotice case muteProfileAlert case activateUserError(error: String) - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -55,17 +56,6 @@ struct UserProfilesView: View { } var body: some View { - if authorized { - userProfilesView() - } else { - Button(action: runAuth) { Label("Unlock", systemImage: "lock") } - .onAppear(perform: runAuth) - } - } - - private func runAuth() { authorize(NSLocalizedString("Open user profiles", comment: "authentication reason"), $authorized) } - - private func userProfilesView() -> some View { List { if profileHidden { Button { @@ -77,12 +67,14 @@ struct UserProfilesView: View { Section { let users = filteredUsers() let v = ForEach(users) { u in - userView(u.user) + userView(u) } if #available(iOS 16, *) { v.onDelete { indexSet in if let i = indexSet.first { - confirmDeleteUser(users[i].user) + withAuth { + confirmDeleteUser(users[i].user) + } } } } else { @@ -90,16 +82,26 @@ struct UserProfilesView: View { } if trimmedSearchTextOrPassword == "" { - NavigationLink { - CreateProfile() - } label: { + NavigationLink( + destination: CreateProfile(), + isActive: $navigateToProfileCreate + ) { Label("Add profile", systemImage: "plus") + .frame(maxWidth: .infinity, alignment: .leading) + .frame(height: 38) + .padding(.leading, 16).padding(.vertical, 8).padding(.trailing, 32) + .contentShape(Rectangle()) + .onTapGesture { + withAuth { + self.navigateToProfileCreate = true + } + } + .padding(.leading, -16).padding(.vertical, -8).padding(.trailing, -32) } - .frame(height: 44) - .padding(.vertical, 4) } } footer: { Text("Tap to activate profile.") + .foregroundColor(theme.colors.secondary) .font(.body) .padding(.top, 8) @@ -111,6 +113,7 @@ struct UserProfilesView: View { } } .navigationTitle("Your chat profiles") + .modifier(ThemedBackground(grouped: true)) .searchable(text: $searchTextOrPassword, placement: .navigationBarDrawer(displayMode: .always)) .autocorrectionDisabled(true) .textInputAutocapitalization(.never) @@ -123,7 +126,7 @@ struct UserProfilesView: View { deleteModeButton("Profile and server connections", true) deleteModeButton("Local profile data only", false) } - .sheet(item: $selectedUser) { user in + .appSheet(item: $selectedUser) { user in HiddenProfileView(user: user, profileHidden: $profileHidden) } .onChange(of: profileHidden) { _ in @@ -131,7 +134,7 @@ struct UserProfilesView: View { withAnimation { profileHidden = false } } } - .sheet(item: $profileAction) { action in + .appSheet(item: $profileAction) { action in profileActionView(action) } .alert(item: $alert) { alert in @@ -169,7 +172,7 @@ struct UserProfilesView: View { message: Text(err) ) case let .error(title, error): - return Alert(title: Text(title), message: Text(error)) + return mkAlert(title: title, message: error) } } } @@ -188,7 +191,25 @@ struct UserProfilesView: View { private var visibleUsersCount: Int { m.users.filter({ u in !u.user.hidden }).count } - + + private func withAuth(_ action: @escaping () -> Void) { + if authorized { + action() + } else { + authenticate( + reason: NSLocalizedString("Change chat profiles", comment: "authentication reason") + ) { laResult in + switch laResult { + case .success, .unavailable: + authorized = true + AppSheetState.shared.scenePhaseActive = true + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: action) + case .failed: authorized = false + } + } + } + } + private func correctPassword(_ user: User, _ pwd: String) -> Bool { if let ph = user.viewPwdHash { return pwd != "" && chatPasswordHash(pwd, ph.salt) == ph.hash @@ -200,26 +221,29 @@ struct UserProfilesView: View { !user.hidden ? nil : trimmedSearchTextOrPassword } - @ViewBuilder private func profileActionView(_ action: UserProfileAction) -> some View { + private func profileActionView(_ action: UserProfileAction) -> some View { let passwordValid = actionPassword == actionPassword.trimmingCharacters(in: .whitespaces) let passwordField = PassphraseField(key: $actionPassword, placeholder: "Profile password", valid: passwordValid) let actionEnabled: (User) -> Bool = { user in actionPassword != "" && passwordValid && correctPassword(user, actionPassword) } - List { + return List { switch action { case let .deleteUser(user, delSMPQueues): actionHeader("Delete profile", user) Section { passwordField - settingsRow("trash") { + settingsRow("trash", color: theme.colors.secondary) { Button("Delete chat profile", role: .destructive) { - profileAction = nil - Task { await removeUser(user, delSMPQueues, viewPwd: actionPassword) } + withAuth { + profileAction = nil + Task { await removeUser(user, delSMPQueues, viewPwd: actionPassword) } + } } .disabled(!actionEnabled(user)) } } footer: { if actionEnabled(user) { Text("All chats and messages will be deleted - this cannot be undone!") + .foregroundColor(theme.colors.secondary) .font(.callout) } } @@ -227,16 +251,19 @@ struct UserProfilesView: View { actionHeader("Unhide profile", user) Section { passwordField - settingsRow("lock.open") { + settingsRow("lock.open", color: theme.colors.secondary) { Button("Unhide chat profile") { - profileAction = nil - setUserPrivacy(user) { try await apiUnhideUser(user.userId, viewPwd: actionPassword) } + withAuth{ + profileAction = nil + setUserPrivacy(user) { try await apiUnhideUser(user.userId, viewPwd: actionPassword) } + } } .disabled(!actionEnabled(user)) } } } } + .modifier(ThemedBackground()) } @ViewBuilder func actionHeader(_ title: LocalizedStringKey, _ user: User) -> some View { @@ -252,11 +279,13 @@ struct UserProfilesView: View { private func deleteModeButton(_ title: LocalizedStringKey, _ delSMPQueues: Bool) -> some View { Button(title, role: .destructive) { - if let user = userToDelete { - if passwordEntryRequired(user) { - profileAction = .deleteUser(user: user, delSMPQueues: delSMPQueues) - } else { - alert = .deleteUser(user: user, delSMPQueues: delSMPQueues) + withAuth { + if let user = userToDelete { + if passwordEntryRequired(user) { + profileAction = .deleteUser(user: user, delSMPQueues: delSMPQueues) + } else { + alert = .deleteUser(user: user, delSMPQueues: delSMPQueues) + } } } } @@ -269,6 +298,7 @@ struct UserProfilesView: View { private func removeUser(_ user: User, _ delSMPQueues: Bool, viewPwd: String?) async { do { if user.activeUser { + ChatModel.shared.removeWallpaperFilesFromAllChats(user) if let newActive = m.users.first(where: { u in !u.user.activeUser && !u.user.hidden }) { try await changeActiveUserAsync_(newActive.user.userId, viewPwd: nil) try await deleteUser() @@ -276,10 +306,11 @@ struct UserProfilesView: View { // Deleting the last visible user while having hidden one(s) try await deleteUser() try await changeActiveUserAsync_(nil, viewPwd: nil) + try? await stopChatAsync() await MainActor.run { onboardingStageDefault.set(.step1_SimpleXInfo) m.onboardingStage = .step1_SimpleXInfo - showSettings = false + dismissAllSheets() } } } else { @@ -293,70 +324,86 @@ struct UserProfilesView: View { func deleteUser() async throws { try await apiDeleteUser(user.userId, delSMPQueues, viewPwd: viewPwd) + removeWallpaperFilesFromTheme(user.uiThemes) await MainActor.run { withAnimation { m.removeUser(user) } } } } - @ViewBuilder private func userView(_ user: User) -> some View { + @ViewBuilder private func userView(_ userInfo: UserInfo) -> some View { + let user = userInfo.user let v = Button { Task { do { try await changeActiveUserAsync_(user.userId, viewPwd: userViewPassword(user)) + dismissAllSheets() } catch { await MainActor.run { alert = .activateUserError(error: responseError(error)) } } } } label: { HStack { - ProfileImage(imageStr: user.image, color: Color(uiColor: .tertiarySystemFill)) - .frame(width: 44, height: 44) - .padding(.vertical, 4) + ProfileImage(imageStr: user.image, size: 38) .padding(.trailing, 12) Text(user.chatViewName) Spacer() if user.activeUser { - Image(systemName: "checkmark").foregroundColor(.primary) - } else if user.hidden { - Image(systemName: "lock").foregroundColor(.secondary) - } else if !user.showNtfs { - Image(systemName: "speaker.slash").foregroundColor(.secondary) + Image(systemName: "checkmark").foregroundColor(theme.colors.onBackground) } else { - Image(systemName: "checkmark").foregroundColor(.clear) + if userInfo.unreadCount > 0 { + UnreadBadge(userInfo: userInfo) + } + if user.hidden { + Image(systemName: "lock").foregroundColor(theme.colors.secondary) + } else if userInfo.unreadCount == 0 { + if !user.showNtfs { + Image(systemName: "speaker.slash").foregroundColor(theme.colors.secondary) + } else { + Image(systemName: "checkmark").foregroundColor(.clear) + } + } } } } - .foregroundColor(.primary) + .foregroundColor(theme.colors.onBackground) .swipeActions(edge: .leading, allowsFullSwipe: true) { if user.hidden { Button("Unhide") { - if passwordEntryRequired(user) { - profileAction = .unhideUser(user: user) - } else { - setUserPrivacy(user) { try await apiUnhideUser(user.userId, viewPwd: trimmedSearchTextOrPassword) } + withAuth { + if passwordEntryRequired(user) { + profileAction = .unhideUser(user: user) + } else { + setUserPrivacy(user) { try await apiUnhideUser(user.userId, viewPwd: trimmedSearchTextOrPassword) } + } } } .tint(.green) } else { if visibleUsersCount > 1 { Button("Hide") { - selectedUser = user + withAuth { + selectedUser = user + } } .tint(.gray) } Group { if user.showNtfs { Button("Mute") { - setUserPrivacy(user, successAlert: showMuteProfileAlert ? .muteProfileAlert : nil) { - try await apiMuteUser(user.userId) + withAuth { + setUserPrivacy(user, successAlert: showMuteProfileAlert ? .muteProfileAlert : nil) { + try await apiMuteUser(user.userId) + } } } } else { Button("Unmute") { - setUserPrivacy(user) { try await apiUnmuteUser(user.userId) } + withAuth { + setUserPrivacy(user) { try await apiUnmuteUser(user.userId) } + } } } } - .tint(.accentColor) + .tint(theme.colors.primary) } } if #available(iOS 16, *) { @@ -364,7 +411,9 @@ struct UserProfilesView: View { } else { v.swipeActions(edge: .trailing, allowsFullSwipe: true) { Button("Delete", role: .destructive) { - confirmDeleteUser(user) + withAuth { + confirmDeleteUser(user) + } } } } @@ -401,8 +450,15 @@ public func chatPasswordHash(_ pwd: String, _ salt: String) -> String { return hash } +public func correctPassword(_ user: User, _ pwd: String) -> Bool { + if let ph = user.viewPwdHash { + return pwd != "" && chatPasswordHash(pwd, ph.salt) == ph.hash + } + return false +} + struct UserProfilesView_Previews: PreviewProvider { static var previews: some View { - UserProfilesView(showSettings: Binding.constant(true)) + UserProfilesView() } } diff --git a/apps/ios/SimpleX (iOS).entitlements b/apps/ios/SimpleX (iOS).entitlements index c78a7cb941..2ec32def0a 100644 --- a/apps/ios/SimpleX (iOS).entitlements +++ b/apps/ios/SimpleX (iOS).entitlements @@ -9,6 +9,10 @@ applinks:simplex.chat applinks:www.simplex.chat applinks:simplex.chat?mode=developer + applinks:*.simplex.im + applinks:*.simplex.im?mode=developer + applinks:*.simplexonflux.com + applinks:*.simplexonflux.com?mode=developer com.apple.security.application-groups diff --git a/apps/ios/SimpleX Localizations/ar.xcloc/Localized Contents/ar.xliff b/apps/ios/SimpleX Localizations/ar.xcloc/Localized Contents/ar.xliff index e0477899be..e965e5a1a5 100644 --- a/apps/ios/SimpleX Localizations/ar.xcloc/Localized Contents/ar.xliff +++ b/apps/ios/SimpleX Localizations/ar.xcloc/Localized Contents/ar.xliff @@ -39,7 +39,7 @@ !1 colored! - ! 1 ملون! + ! 1 مُلوَّن! No comment provided by engineer. @@ -49,7 +49,7 @@ %@ - %@ + %@ No comment provided by engineer. @@ -69,7 +69,7 @@ %@ is not verified - %@ لم يتم التحقق منها + %@ لم يتم التحقق منه No comment provided by engineer. @@ -107,8 +107,9 @@ %d ثانية message ttl - + %d skipped message(s) + %d الرسائل المتخطية integrity error chat item @@ -121,12 +122,14 @@ %lld %@ No comment provided by engineer. - + %lld contact(s) selected + %lld تم اختيار جهات الاتصال No comment provided by engineer. - + %lld file(s) with total size of %@ + %lld الملفات ذات الحجم الإجمالي %@ No comment provided by engineer. @@ -134,8 +137,9 @@ %lld أعضاء No comment provided by engineer. - + %lld second(s) + %lld ثوانى No comment provided by engineer. @@ -183,24 +187,19 @@ ) No comment provided by engineer. - - **Add new contact**: to create your one-time QR Code or link for your contact. - ** إضافة جهة اتصال جديدة **: لإنشاء رمز QR لمرة واحدة أو رابط جهة الاتصال الخاصة بك. - No comment provided by engineer. - **Create link / QR code** for your contact to use. ** أنشئ رابطًا / رمز QR ** لتستخدمه جهة الاتصال الخاصة بك. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. - ** المزيد من الخصوصية **: تحقق من الرسائل الجديدة كل 20 دقيقة. تتم مشاركة رمز الجهاز مع خادم SimpleX Chat ، ولكن ليس عدد جهات الاتصال أو الرسائل لديك. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. + ** المزيد من الخصوصية **: تحققوا من الرسائل الجديدة كل 20 دقيقة. تتم مشاركة رمز الجهاز مع خادم SimpleX Chat ، ولكن ليس عدد جهات الاتصال أو الرسائل لديكم. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). - ** الأكثر خصوصية **: لا تستخدم خادم إشعارات SimpleX Chat ، وتحقق من الرسائل بشكل دوري في الخلفية (يعتمد على عدد مرات استخدامك للتطبيق). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. + ** الأكثر خصوصية **: لا تستخدم خادم إشعارات SimpleX Chat ، وتحقق من الرسائل بشكل دوري في الخلفية (يعتمد على عدد مرات استخدامكم للتطبيق). No comment provided by engineer. @@ -210,11 +209,11 @@ **Please note**: you will NOT be able to recover or change passphrase if you lose it. - ** يرجى ملاحظة **: لن تتمكن من استعادة أو تغيير عبارة المرور إذا فقدتها. + ** يرجى ملاحظة **: لن تتمكنوا من استعادة أو تغيير عبارة المرور إذا فقدتموها. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. ** موصى به **: يتم إرسال رمز الجهاز والإشعارات إلى خادم إشعارات SimpleX Chat ، ولكن ليس محتوى الرسالة أو حجمها أو مصدرها. No comment provided by engineer. @@ -305,7 +304,7 @@ A separate TCP connection will be used **for each chat profile you have in the app**. - سيتم استخدام اتصال TCP منفصل ** لكل ملف تعريف دردشة لديك في التطبيق **. + سيتم استخدام اتصال TCP منفصل ** لكل ملف تعريف دردشة لديكم في التطبيق **. No comment provided by engineer. @@ -355,312 +354,381 @@ Accept requests No comment provided by engineer. - + Add preset servers + إضافة خوادم محددة مسبقا No comment provided by engineer. - + Add profile + إضافة الملف الشخصي No comment provided by engineer. - + Add servers by scanning QR codes. + إضافة خوادم عن طريق مسح رموز QR. No comment provided by engineer. - - Add server… + + Add server + أضف الخادم No comment provided by engineer. - + Add to another device + أضف إلى جهاز آخر No comment provided by engineer. - + Admins can create the links to join groups. + يمكن للمُدراء إنشاء روابط للانضمام إلى المجموعات. No comment provided by engineer. - + Advanced network settings + إعدادات الشبكة المتقدمة No comment provided by engineer. - + All chats and messages will be deleted - this cannot be undone! + سيتم حذف جميع الدردشات والرسائل - لا يمكن التراجع عن هذا! No comment provided by engineer. - + All group members will remain connected. + سيبقى جميع أعضاء المجموعة على اتصال. No comment provided by engineer. - + All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. + سيتم حذف جميع الرسائل - لا يمكن التراجع عن هذا! سيتم حذف الرسائل فقط من أجلك. No comment provided by engineer. All your contacts will remain connected No comment provided by engineer. - + Allow + سماح No comment provided by engineer. - + Allow disappearing messages only if your contact allows it to you. + السماح بالرسائل المختفية فقط إذا سمحت لك جهة الاتصال بذلك. No comment provided by engineer. Allow irreversible message deletion only if your contact allows it to you. No comment provided by engineer. - + Allow sending direct messages to members. + السماح بإرسال رسائل مباشرة إلى الأعضاء. No comment provided by engineer. - + Allow sending disappearing messages. + السماح بإرسال الرسائل التي تختفي. No comment provided by engineer. Allow to irreversibly delete sent messages. No comment provided by engineer. - + Allow to send voice messages. + السماح بإرسال رسائل صوتية. No comment provided by engineer. - + Allow voice messages only if your contact allows them. + اسمح بالرسائل الصوتية فقط إذا سمحت جهة اتصالك بذلك. No comment provided by engineer. - + Allow voice messages? + السماح بالرسائل الصوتية؟ No comment provided by engineer. Allow your contacts to irreversibly delete sent messages. No comment provided by engineer. - + Allow your contacts to send disappearing messages. + السماح لجهات اتصالك بإرسال رسائل تختفي. No comment provided by engineer. - + Allow your contacts to send voice messages. + اسمح لجهات اتصالك بإرسال رسائل صوتية. No comment provided by engineer. - + Already connected? + متصل بالفعل؟ No comment provided by engineer. - + Answer call + أجب الاتصال No comment provided by engineer. - + App build: %@ + إصدار التطبيق: %@ No comment provided by engineer. - + App icon + رمز التطبيق No comment provided by engineer. - + App version + نسخة التطبيق No comment provided by engineer. - + App version: v%@ + نسخة التطبيق: v%@ No comment provided by engineer. - + Appearance + المظهر No comment provided by engineer. - + Attach + إرفاق No comment provided by engineer. - + Audio & video calls + مكالمات الصوت والفيديو No comment provided by engineer. - + Authentication failed + فشلت المصادقة No comment provided by engineer. - + Authentication unavailable + المصادقة غير متاحة No comment provided by engineer. - + Auto-accept contact requests + قبول طلبات الاتصال تلقائيًا No comment provided by engineer. - + Auto-accept images + قبول تلقائي للصور No comment provided by engineer. Automatically No comment provided by engineer. - + Back + رجوع No comment provided by engineer. Both you and your contact can irreversibly delete sent messages. No comment provided by engineer. - + Both you and your contact can send disappearing messages. + يمكنك أنت وجهة اتصالك إرسال رسائل تختفي. No comment provided by engineer. - + Both you and your contact can send voice messages. + يمكنك أنت وجهة اتصالك إرسال رسائل صوتية. No comment provided by engineer. - + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). + حسب ملف تعريف الدردشة (افتراضي) أو [حسب الاتصال] (https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. - + Call already ended! + انتهت المكالمة بالفعل! No comment provided by engineer. - + Calls + المكالمات No comment provided by engineer. - + Can't invite contact! + لا يمكن دعوة جهة اتصال! No comment provided by engineer. - + Can't invite contacts! + لا يمكن دعوة جهات الاتصال! No comment provided by engineer. - + Cancel + إلغاء No comment provided by engineer. - + Cannot access keychain to save database password + لا يمكن الوصول إلى سلسلة المفاتيح لحفظ كلمة مرور قاعدة البيانات No comment provided by engineer. - + Cannot receive file + لا يمكن استلام الملف No comment provided by engineer. - + Change + تغير No comment provided by engineer. - + Change database passphrase? + تغيير عبارة مرور قاعدة البيانات؟ No comment provided by engineer. - + Change member role? + تغيير دور العضو؟ No comment provided by engineer. - + Change receiving address + تغيير عنوان الاستلام No comment provided by engineer. - + Change receiving address? + تغيير عنوان الاستلام؟ No comment provided by engineer. - + Change role + تغيير الدور No comment provided by engineer. Chat archive No comment provided by engineer. - + Chat console + وحدة تحكم الدردشة No comment provided by engineer. - + Chat database + قاعدة بيانات الدردشة No comment provided by engineer. - + Chat database deleted + حُذفت قاعدة بيانات الدردشة No comment provided by engineer. - + Chat database imported + استُوردت قاعدة بيانات الدردشة No comment provided by engineer. - + Chat is running + الدردشة قيد التشغيل No comment provided by engineer. - + Chat is stopped + توقفت الدردشة No comment provided by engineer. - + Chat preferences + تفضيلات الدردشة No comment provided by engineer. - + Chats + الدردشات No comment provided by engineer. - + Check server address and try again. + تحقق من عنوان الخادم وحاول مرة أخرى. No comment provided by engineer. - + Choose file + اختر الملف No comment provided by engineer. - + Choose from library + اختر من المكتبة No comment provided by engineer. - + Clear + مسح No comment provided by engineer. - + Clear conversation + مسح الدردشة No comment provided by engineer. - + Clear conversation? + مسح الدردشة؟ No comment provided by engineer. - + Clear verification + امسح التحقُّق No comment provided by engineer. Colors No comment provided by engineer. - + Compare security codes with your contacts. + قارن رموز الأمان مع جهات اتصالك. No comment provided by engineer. - + Configure ICE servers + ضبط خوادم ICE No comment provided by engineer. - + Confirm + تأكيد No comment provided by engineer. - + Confirm new passphrase… + تأكيد عبارة المرور الجديدة… No comment provided by engineer. - + Connect + اتصل server test step @@ -671,8 +739,9 @@ Connect via group link? No comment provided by engineer. - + Connect via link + تواصل عبر الرابط No comment provided by engineer. @@ -687,224 +756,273 @@ Connect via relay No comment provided by engineer. - + Connecting to server… + جارِ الاتصال بالخادم… No comment provided by engineer. - + Connecting to server… (error: %@) + الاتصال بالخادم... (الخطأ: %@) No comment provided by engineer. - + Connection + الاتصال No comment provided by engineer. - + Connection error + خطأ في الإتصال No comment provided by engineer. - + Connection error (AUTH) + خطأ في الإتصال (المصادقة) No comment provided by engineer. Connection request No comment provided by engineer. - + Connection request sent! + أرسلت طلب الاتصال! No comment provided by engineer. - + Connection timeout + انتهت مهلة الاتصال No comment provided by engineer. - + Contact allows + تسمح جهة الاتصال No comment provided by engineer. - + Contact already exists + جهة الاتصال موجودة بالفعل No comment provided by engineer. Contact and all messages will be deleted - this cannot be undone! No comment provided by engineer. - + Contact hidden: + جهة الاتصال مخفية: notification - + Contact is connected + تم الاتصال notification Contact is not connected yet! No comment provided by engineer. - + Contact name + اسم جهة الاتصال No comment provided by engineer. - + Contact preferences + تفضيلات جهة الاتصال No comment provided by engineer. Contact requests No comment provided by engineer. - + Contacts can mark messages for deletion; you will be able to view them. + يمكن لجهات الاتصال تحديد الرسائل لحذفها؛ ستتمكن من مشاهدتها. No comment provided by engineer. - + Copy + نسخ chat item action Core built at: %@ No comment provided by engineer. - + Core version: v%@ + الإصدار الأساسي: v%@ No comment provided by engineer. - + Create + إنشاء No comment provided by engineer. Create address No comment provided by engineer. - + Create group link + إنشاء رابط المجموعة No comment provided by engineer. - + Create link + إنشاء رابط No comment provided by engineer. Create one-time invitation link No comment provided by engineer. - + Create queue + إنشاء قائمة انتظار server test step - + Create secret group + إنشاء مجموعة سرية No comment provided by engineer. - + Create your profile + أنشئ ملف تعريفك No comment provided by engineer. Created on %@ No comment provided by engineer. - + Current passphrase… + عبارة المرور الحالية… No comment provided by engineer. - + Currently maximum supported file size is %@. + الحد الأقصى لحجم الملف المدعوم حاليًا هو %@. No comment provided by engineer. - + Dark + داكن No comment provided by engineer. - + Database ID + معرّف قاعدة البيانات No comment provided by engineer. - + Database encrypted! + قاعدة البيانات مُعمّاة! No comment provided by engineer. - + Database encryption passphrase will be updated and stored in the keychain. + سيتم تحديث عبارة المرور الخاصة بتشفير قاعدة البيانات وتخزينها في سلسلة المفاتيح. + No comment provided by engineer. - + Database encryption passphrase will be updated. + سيتم تحديث عبارة مرور تعمية قاعدة البيانات. + No comment provided by engineer. - + Database error + خطأ في قاعدة البيانات No comment provided by engineer. - + Database is encrypted using a random passphrase, you can change it. + قاعدة البيانات مُعمّاة باستخدام عبارة مرور عشوائية، يمكنك تغييرها. No comment provided by engineer. - + Database is encrypted using a random passphrase. Please change it before exporting. + قاعدة البيانات مُعمّاة باستخدام عبارة مرور عشوائية. يُرجى تغييره قبل التصدير. No comment provided by engineer. - + Database passphrase + عبارة مرور قاعدة البيانات No comment provided by engineer. - + Database passphrase & export + عبارة مرور قاعدة البيانات وتصديرها No comment provided by engineer. - + Database passphrase is different from saved in the keychain. + عبارة المرور الخاصة بقاعدة البيانات مختلفة عن تلك المحفوظة في سلسلة المفاتيح. No comment provided by engineer. - + Database passphrase is required to open chat. + عبارة مرور قاعدة البيانات مطلوبة لفتح الدردشة. No comment provided by engineer. - + Database will be encrypted and the passphrase stored in the keychain. + سيتم تشفير قاعدة البيانات وتخزين عبارة المرور في سلسلة المفاتيح. + No comment provided by engineer. - + Database will be encrypted. + سيتم تعمية قاعدة البيانات. + No comment provided by engineer. - + Database will be migrated when the app restarts + سيتم نقل قاعدة البيانات عند إعادة تشغيل التطبيق No comment provided by engineer. - + Decentralized + لامركزي No comment provided by engineer. - + Delete + حذف chat item action Delete Contact No comment provided by engineer. - + Delete address + حذف العنوان No comment provided by engineer. - + Delete address? + حذف العنوان؟ No comment provided by engineer. - + Delete after + حذف بعد No comment provided by engineer. - + Delete all files + حذف جميع الملفات No comment provided by engineer. @@ -915,152 +1033,188 @@ Delete chat archive? No comment provided by engineer. - + Delete chat profile? + حذف ملف تعريف الدردشة؟ No comment provided by engineer. - + Delete connection + حذف الاتصال No comment provided by engineer. - + Delete contact + حذف جهة الاتصال No comment provided by engineer. - + Delete contact? + حذف جهة الاتصال؟ No comment provided by engineer. - + Delete database + حذف قاعدة البيانات No comment provided by engineer. - + Delete files and media? + حذف الملفات والوسائط؟ No comment provided by engineer. - + Delete files for all chat profiles + حذف الملفات لجميع ملفات تعريف الدردشة No comment provided by engineer. - + Delete for everyone + حذف للجميع chat feature - + Delete for me + حذف بالنسبة لي No comment provided by engineer. - + Delete group + حذف المجموعة No comment provided by engineer. - + Delete group? + حذف المجموعة؟ No comment provided by engineer. - + Delete invitation + حذف الدعوة No comment provided by engineer. - + Delete link + حذف الرابط No comment provided by engineer. - + Delete link? + حذف الرابط؟ No comment provided by engineer. - + Delete message? + حذف الرسالة؟ No comment provided by engineer. - + Delete messages + حذف الرسائل No comment provided by engineer. - + Delete messages after + حذف الرسائل بعد No comment provided by engineer. - + Delete old database + حذف قاعدة البيانات القديمة No comment provided by engineer. - + Delete old database? + حذف قاعدة البيانات القديمة؟ No comment provided by engineer. Delete pending connection No comment provided by engineer. - + Delete pending connection? + حذف الاتصال قيد الانتظار؟ No comment provided by engineer. - + Delete queue + حذف قائمة الانتظار server test step - + Delete user profile? + حذف ملف تعريف المستخدم؟ No comment provided by engineer. - + Description + الوصف No comment provided by engineer. - + Develop + يطور No comment provided by engineer. - + Developer tools + أدوات المطور No comment provided by engineer. - + Device + الجهاز No comment provided by engineer. - + Device authentication is disabled. Turning off SimpleX Lock. + استيثاق الجهاز مُعطَّل. جارِ إيقاف تشغيل قفل SimpleX. No comment provided by engineer. - + Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication. + مصادقة الجهاز غير مفعّلة. يمكنك تشغيل قفل SimpleX عبر الإعدادات، بمجرد تفعيل مصادقة الجهاز. No comment provided by engineer. - + Different names, avatars and transport isolation. + أسماء مختلفة، صور الأفاتار وعزل النقل. No comment provided by engineer. - + Direct messages + رسائل مباشرة chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited. + الرسائل المباشرة بين الأعضاء ممنوعة. No comment provided by engineer. - + Disable SimpleX Lock + تعطيل قفل SimpleX authentication reason - + Disappearing messages + الرسائل المختفية chat feature - + Disappearing messages are prohibited in this chat. + يُحظر اختفاء الرسائل في هذه الدردشة. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. + الرسائل المختفية ممنوعة. No comment provided by engineer. - + Disconnect + قطع الاتصال server test step @@ -1071,124 +1225,153 @@ Display name: No comment provided by engineer. - + Do NOT use SimpleX for emergency calls. + لا تستخدم SimpleX لإجراء مكالمات الطوارئ. No comment provided by engineer. - + Do it later + افعل ذلك لاحقا No comment provided by engineer. - + Duplicate display name! + اسم العرض مكرر! No comment provided by engineer. - + Edit + تحرير chat item action - + Edit group profile + حرّر ملف تعريف المجموعة No comment provided by engineer. - + Enable + تفعيل No comment provided by engineer. - + Enable SimpleX Lock + تفعيل قفل SimpleX authentication reason - + Enable TCP keep-alive + تفعيل أبقِ TCP على قيد الحياة No comment provided by engineer. - + Enable automatic message deletion? + تفعيل الحذف التلقائي للرسائل؟ No comment provided by engineer. - + Enable instant notifications? + تفعيل الإشعارات فورية؟ No comment provided by engineer. - + Enable notifications + تفعيل الإشعارات No comment provided by engineer. - + Enable periodic notifications? + تفعيل الإشعارات دورية؟ No comment provided by engineer. - + Encrypt + التشفير No comment provided by engineer. - + Encrypt database? + تشفير قاعدة البيانات؟ No comment provided by engineer. - + Encrypted database + قاعدة بيانات مشفرة No comment provided by engineer. - + Encrypted message or another event + رسالة مشفرة أو حدث آخر notification - + Encrypted message: database error + رسالة مشفرة: خطأ في قاعدة البيانات notification - + Encrypted message: keychain error + رسالة مشفرة: خطأ في سلسلة المفاتيح notification - + Encrypted message: no passphrase + الرسالة المشفرة: لا توجد عبارة مرور notification - + Encrypted message: unexpected error + رسالة مشفرة: خطأ غير متوقع notification - + Enter correct passphrase. + أدخل عبارة المرور الصحيحة. No comment provided by engineer. - + Enter passphrase… + أدخل عبارة المرور… No comment provided by engineer. - + Enter server manually + أدخل الخادم يدوياً No comment provided by engineer. - + Error + خطأ No comment provided by engineer. - + Error accepting contact request + خطأ في قبول طلب الاتصال No comment provided by engineer. Error accessing database file No comment provided by engineer. - + Error adding member(s) + خطأ في إضافة عضو (أعضاء) No comment provided by engineer. - + Error changing address + خطأ في تغيير العنوان No comment provided by engineer. - + Error changing role + خطأ في تغيير الدور المتغير No comment provided by engineer. - + Error changing setting + خطأ في تغيير الإعدادات No comment provided by engineer. @@ -1419,16 +1602,16 @@ Group members can irreversibly delete sent messages. No comment provided by engineer. - - Group members can send direct messages. + + Members can send direct messages. No comment provided by engineer. - - Group members can send disappearing messages. + + Members can send disappearing messages. No comment provided by engineer. - - Group members can send voice messages. + + Members can send voice messages. No comment provided by engineer. @@ -1519,8 +1702,8 @@ Image will be received when your contact is online, please wait or check later! No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam No comment provided by engineer. @@ -1616,8 +1799,8 @@ Irreversible message deletion is prohibited in this chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. No comment provided by engineer. @@ -1917,8 +2100,8 @@ We will be adding server redundancy to prevent lost messages. Onion hosts will not be used. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only client devices store user profiles, contacts, groups, and messages. No comment provided by engineer. @@ -1969,8 +2152,9 @@ We will be adding server redundancy to prevent lost messages. Open user profiles authentication reason - - Open-source protocol and code – anybody can run the servers. + + Anybody can host servers. + يمكن لأي شخص استضافة الخوادم. No comment provided by engineer. @@ -2001,8 +2185,8 @@ We will be adding server redundancy to prevent lost messages. Paste the link you received into the box below to connect with your contact. No comment provided by engineer. - - People can connect to you only via the links you share. + + You decide who can connect. No comment provided by engineer. @@ -2373,96 +2557,117 @@ We will be adding server redundancy to prevent lost messages. Sent messages will be deleted after set time. No comment provided by engineer. - + Server requires authorization to create queues, check password + يتطلب الخادم إذنًا لإنشاء قوائم انتظار، تحقق من كلمة المرور server test error - + Server test failed! + فشلت تجربة الخادم! No comment provided by engineer. - + Servers + الخوادم No comment provided by engineer. - + Set 1 day + تعيين يوم واحد No comment provided by engineer. - + Set contact name… + تعيين اسم جهة الاتصال… No comment provided by engineer. - + Set group preferences + عيّن تفضيلات المجموعة No comment provided by engineer. - + Set passphrase to export + عيّن عبارة المرور للتصدير No comment provided by engineer. - + Set timeouts for proxy/VPN + حدد مهلات للوسيط او شبكات افتراضية خاصة (Proxy/VPN timeouts) No comment provided by engineer. - + Settings + الإعدادات No comment provided by engineer. - + Share + مشاركة chat item action Share invitation link No comment provided by engineer. - + Share link + مشاركة الرابط No comment provided by engineer. Share one-time invitation link No comment provided by engineer. - + Show QR code + عرض رمز QR No comment provided by engineer. - + Show preview + عرض المعاينة No comment provided by engineer. - + SimpleX Chat security was audited by Trail of Bits. + تم تدقيق أمان SimpleX Chat بواسطة Trail of Bits. No comment provided by engineer. - + SimpleX Lock + قفل SimpleX No comment provided by engineer. - + SimpleX Lock turned on + تم تشغيل القفل SimpleX No comment provided by engineer. - + SimpleX contact address + عنوان جهة أتصال SimpleX simplex link type - + SimpleX encrypted message or connection event + حَدَثْ SimpleX لرسالة أو اتصال مشفر notification - + SimpleX group link + رابط مجموعة SimpleX simplex link type - + SimpleX links + روابط SimpleX No comment provided by engineer. - + SimpleX one-time invitation + دعوة SimpleX لمرة واحدة simplex link type @@ -2581,8 +2786,8 @@ We will be adding server redundancy to prevent lost messages. Thanks to the users – contribute via Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. + + No user identifiers. No comment provided by engineer. @@ -2613,16 +2818,16 @@ We will be adding server redundancy to prevent lost messages. The microphone does not work when the app is in the background. No comment provided by engineer. - - The next generation of private messaging + + The future of messaging No comment provided by engineer. The old database was not removed during the migration, it can be deleted. No comment provided by engineer. - - The profile is only shared with your contacts. + + Your profile is stored on your device and only shared with your contacts. No comment provided by engineer. @@ -2677,8 +2882,8 @@ We will be adding server redundancy to prevent lost messages. To prevent the call interruption, enable Do Not Disturb mode. No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. No comment provided by engineer. @@ -2851,8 +3056,8 @@ To connect, please ask your contact to create another connection link and check Voice messages are prohibited in this chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. No comment provided by engineer. @@ -2963,10 +3168,6 @@ To connect, please ask your contact to create another connection link and check You can use markdown to format messages: No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - You could not be verified; please try again. No comment provided by engineer. @@ -3526,72 +3727,87 @@ SimpleX servers cannot see your profile. secret No comment provided by engineer. - + starting… + يبدأ… No comment provided by engineer. - + strike + شطب No comment provided by engineer. this contact notification title - + unknown + غير معروف connection info - + updated group profile + حدثت ملف تعريف المجموعة rcv group event chat item v%@ (%@) No comment provided by engineer. - + via contact address link + عبر رابط عنوان الاتصال chat list item description - + via group link + عبر رابط المجموعة chat list item description - + via one-time link + عبر رابط لمرة واحدة chat list item description - + via relay + عبر المُرحل No comment provided by engineer. - + video call (not e2e encrypted) + مكالمة الفيديو ليست مُعمّاة بين الطريفين No comment provided by engineer. - + waiting for answer… + بانتظار الرد… No comment provided by engineer. - + waiting for confirmation… + في انتظار التأكيد… No comment provided by engineer. - + wants to connect to you! + يريد الاتصال بك! No comment provided by engineer. - + yes + نعم pref value - + you are invited to group + أنت مدعو إلى المجموعة No comment provided by engineer. - + you changed address + غيّرتَ العنوان chat item text @@ -3606,16 +3822,18 @@ SimpleX servers cannot see your profile. you changed role of %1$@ to %2$@ snd group event chat item - + you left + غادرت snd group event chat item you removed %@ snd group event chat item - + you shared one-time link + لقد شاركت رابط لمرة واحدة chat list item description @@ -3657,7 +3875,7 @@ SimpleX servers cannot see your profile. # %@ - # %@ + # %@ copied message info title, # <title> @@ -3667,7 +3885,7 @@ SimpleX servers cannot see your profile. ## In reply to - ## ردًا على + ## ردًّا على copied message info @@ -3675,6 +3893,1870 @@ SimpleX servers cannot see your profile. %@ و %@ متصل No comment provided by engineer. + + %@ downloaded + %@ تم التنزيل + + + %@ and %@ + %@ و %@ + + + %@ connected + %@ متصل + + + %lld minutes + %lld دقائق + + + %@, %@ and %lld members + %@, %@ و %lld أعضاء + + + %d weeks + %d أسابيع + + + %@ uploaded + %@ تم الرفع + + + %@, %@ and %lld other members connected + %@, %@ و %lld أعضاء آخرين متصلين + + + %lld seconds + %lld ثواني + + + %u messages failed to decrypt. + %u فشلت عملية فك تشفير الرسائل. + + + %lld messages marked deleted + %lld الرسائل معلمه بالحذف + + + %lld messages moderated by %@ + %lld رسائل تمت إدارتها بواسطة %@ + + + %lld new interface languages + %lld لغات واجهة جديدة + + + %lld group events + %lld أحداث المجموعة + + + %lld messages blocked by admin + %lld رسائل محظورة بواسطه المسؤول + + + %lld messages blocked + %lld رسائل تم حظرها + + + %u messages skipped. + %u تم تخطي الرسائل. + + + **Create 1-time link**: to create and share a new invitation link. + **إضافة جهة اتصال**: لإنشاء رابط دعوة جديد، أو الاتصال عبر الرابط الذي تلقيتوهم. + + + **Create group**: to create a new group. + **إنشاء مجموعة**: لإنشاء مجموعة جديدة. + + + (this device v%@) + (هذا الجهاز v%@) + + + (new) + (جديد) + + + **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **يرجى الملاحظة**: سيؤدي استخدام نفس قاعدة البيانات على جهازين إلى كسر فك تشفير الرسائل من اتصالاتكم كحماية أمنية. + + + A new random profile will be shared. + سيتم مشاركة ملف تعريفي عشوائي جديد. + + + 30 seconds + 30 ثانيه + + + - more stable message delivery. +- a bit better groups. +- and more! + - تسليم رسائل أكثر استقرارًا. +- مجموعات أفضل قليلاً. +- والمزيد! + + + 0 sec + 0 ثانيه + + + 1 minute + 1 دقيقة + + + 5 minutes + 5 دقائق + + + <p>Hi!</p> +<p><a href="%@">Connect to me via SimpleX Chat</a></p> + <p>مرحبا!</p> +<p><a href="%@">أتصل بى من خلال SimpleX Chat</a></p> + + + 0s + 0 ث + + + A few more things + بعض الأشياء الأخرى + + + - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! +- delivery receipts (up to 20 members). +- faster and more stable. + - أتصل بـ [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! +- delivery receipts (up to 20 members). +- أسرع و أكثر اسْتِقْرارًا. + + + **Warning**: the archive will be removed. + **تحذير**: سيتم إزالة الأرشيف. + + + - optionally notify deleted contacts. +- profile names with spaces. +- and more! + - إخطار جهات الاتصال المحذوفة بشكل اختياري. +- أسماء الملفات الشخصية مع المسافات. +- والمزيد! + + + - voice messages up to 5 minutes. +- custom time to disappear. +- editing history. + - رسائل صوتية تصل مدتها إلى 5 دقائق. +- وقت مخصص للاختفاء. +- تعديل السجل. + + + Add welcome message + إضافة رسالة ترحيب + + + Abort changing address? + هل تريد إلغاء تغيير العنوان؟ + + + Add contact + إضافة جهة اتصال + + + Abort + إحباط + + + About SimpleX address + حول عنوان SimpleX + + + Accept connection request? + قبول طلب الاتصال؟ + + + Acknowledged + معترف به + + + Acknowledgement errors + أخطاء الإقرار + + + Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. + أضف عنوانًا إلى ملفكم الشخصي، حتى تتمكن جهات الاتصال الخاصة بكم من مشاركته مع أشخاص اخرين. سيتم إرسال تحديث الملف الشخصي إلى جهات الاتصال الخاصة بكم. + + + Abort changing address + إحباط تغيير العنوان + + + Active connections + اتصالات نشطة + + + Apply + طبّق + + + %@ server + %@ خادم + + + Accept conditions + اقبل الشروط + + + Share address + مشاركة العنوان + + + Already connecting! + جارٍ الاتصال بالفعل! + + + %d file(s) are still being downloaded. + %d الملف(ات) لا تزال قيد التنزيل. + + + %d file(s) failed to download. + %d الملف(ات) فشلت في التنزيل. + + + All app data is deleted. + حُذفت جميع بيانات التطبيق. + + + Allow irreversible message deletion only if your contact allows it to you. (24 hours) + السماح بحذف الرسائل بشكل لا رجوع فيه فقط إذا سمحت لك جهة الاتصال بذلك. (24 ساعة) + + + Share profile + شارك ملف التعريف + + + Always use relay + استخدم الموجه دائمًا + + + Address + عنوان + + + All data is erased when it is entered. + يتم مسح جميع البيانات عند إدخالها. + + + %d file(s) were deleted. + %d تم حذف الملف(ات). + + + %d file(s) were not downloaded. + %d لم يتم تنزيل الملف(ات). + + + %d messages not forwarded + %d الرسائل لم يتم تحويلها + + + %d seconds(s) + %d ثواني + + + **Scan / Paste link**: to connect via a link you received. + **امسح / ألصِق الرابط**: للاتصال عبر الرابط الذي تلقيته. + + + 1 year + سنة واحدة + + + 1-time link + رابط لمرة واحدة + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + يمكن استعمال الرابط لمرة واحدة *مع جهة اتصال واحدة فقط* - شاركه شخصياً أو عبر أي تطبيق مراسلة. + + + Accent + لون تمييزي + + + Accepted conditions + الشروط المتفق عليها + + + All chats will be removed from the list (text), and the list deleted. + سيتم إزالة جميع الدردشات من القائمة (النص)، وحذف القائمة. + + + Allow message reactions. + السماح بردود الفعل على الرسائل. + + + Allow to irreversibly delete sent messages. (24 hours) + السماح بحذف الرسائل المرسلة بشكل لا رجعة فيه. (24 ساعة) + + + Allow to send SimpleX links. + السماح بإرسال روابط SimpleX. + + + Already joining the group! + جارٍ انضمام بالفعل إلى المجموعة! + + + An empty chat profile with the provided name is created, and the app opens as usual. + يتم إنشاء ملف تعريف دردشة فارغ بالاسم المقدم، ويفتح التطبيق كالمعتاد. + + + Authentication cancelled + ألغيت المصادقة + + + Audio/video calls are prohibited. + مكالمات الصوت/الفيديو محظورة. + + + Better groups + مجموعات أفضل + + + Background + الخلفية + + + Better calls + مكالمات أفضل + + + Both you and your contact can irreversibly delete sent messages. (24 hours) + يمكنك أنت وجهة اتصالك حذف الرسائل المرسلة بشكل لا رجعة فيه. (24 ساعة) + + + Block member for all? + حظر العضو للجميع؟ + + + Blur media + تمويه الوسائط + + + Server type + نوع الخادم + + + Server requires authorization to upload, check password + يتطلب الخادم إذنًا للرفع، تحقق من كلمة المرور + + + Server version is incompatible with network settings. + إصدار الخادم غير متوافق مع إعدادات الشبكة. + + + Share with contacts + مشاركة مع جهات الاتصال + + + Show: + عرض: + + + SimpleX Address + عنوان SimpleX + + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + توصلت SimpleX Chat وFlux إلى اتفاق لتضمين الخوادم التي تديرها Flux في التطبيق. + + + Allow calls? + السماح بالمكالمات؟ + + + App passcode is replaced with self-destruct passcode. + يتم استبدال رمز مرور التطبيق برمز مرور التدمير الذاتي. + + + SimpleX Lock mode + SimpleX وضع القفل + + + Audio and video calls + مكالمات الصوت والفيديو + + + App passcode + رمز مرور التطبيق + + + Bad message ID + معرّف رسالة سيئ + + + Server address is incompatible with network settings. + عنوان الخادم غير متوافق مع إعدادات الشبكة. + + + Servers statistics will be reset - this cannot be undone! + سيتم تصفير إحصائيات الخوادم - لا يمكن التراجع عن هذا! + + + Allow to send files and media. + السماح بإرسال الملفات والوسائط. + + + App encrypts new local files (except videos). + يُعمِّي الملفات المحلية الجديدة (باستثناء مقاطع الفيديو). + + + Better messages + رسائل أفضل + + + Set passcode + عيّن رمز المرور + + + Additional accent 2 + لون إضافي ثانوي 2 + + + Allow your contacts adding message reactions. + السماح لجهات اتصالك بإضافة ردود الفعل للرسالة. + + + Allow your contacts to call you. + السماح لجهات اتصالك بالاتصال بك. + + + Audio/video calls + مكالمات الصوت/الفيديو + + + Better notifications + إشعارات أفضل + + + Better user experience + تجربة مستخدم أفضل + + + Block + حظر + + + Black + أسود + + + Block member? + حظر العضو؟ + + + Blocked by admin + محظور من قبل المُدير + + + Blur for better privacy. + تمويه من أجل خصوصية أفضل. + + + Show → on messages sent via private routing. + عرض ← على الرسائل المرسلة عبر التوجيه الخاص. + + + Share from other apps. + المشاركة من التطبيقات الأخرى. + + + Share this 1-time invite link + شارك رابط الدعوة هذا لمرة واحدة + + + Set passphrase + عيّن عبارة المرور + + + Share address with contacts? + مشاركة العنوان مع جهات الاتصال؟ + + + Allow downgrade + السماح بالرجوع إلى إصدار سابق + + + Bad desktop address + عنوان سطح المكتب غير صالح + + + %1$@, %2$@ + %1$@, %2$@ + + + All profiles + جميع ملفات التعريف + + + Authentication is required before the call is connected, but you may miss calls. + يتطلب التوثيق قبل الاتصال بالمكالمة، ولكن قد تفوتك المكالمات. + + + Archiving database + جارِ أرشفة قاعدة البيانات + + + Settings were changed. + تم تغيير الإعدادات. + + + Better groups performance + أداء مجموعات أفضل + + + Better privacy and security + خصوصية وأمان أفضل + + + Better security ✅ + أمان أفضل ✅ + + + Block for all + حظر للجميع + + + Block group members + حظر أعضاء المجموعة + + + Block member + حظر العضو + + + Both you and your contact can add message reactions. + يمكنك أنت وجهة اتصالك إضافة ردود فعل الرسائل. + + + Both you and your contact can make calls. + يمكنك أنت وجهة الاتصال إجراء مكالمات. + + + Server + الخادم + + + Server operators + مُشغلي الخادم + + + Server version is incompatible with your app: %@. + إصدار الخادم غير متوافق مع التطبيق لديك: %@. + + + Servers info + معلومات الخوادم + + + Set chat name… + عيّن اسم الدردشة… + + + Shape profile images + شكّل الصور التعريفية + + + Share address publicly + شارك العنوان علناً + + + Show developer options + عرض خيارات المطور + + + SimpleX address + عنوان SimpleX + + + SimpleX address or 1-time link? + عنوان SimpleX أو رابط لمرة واحدة؟ + + + @'%@' + @'%@' + + + @%@ + @%@ + + + Active + نشط + + + Add friends + أضف أصدقاء + + + Add list + أضف القائمة + + + Address change will be aborted. Old receiving address will be used. + سيتم إحباط تغيير العنوان. سيتم استخدام عنوان الاستلام القديم. + + + All messages will be deleted - this cannot be undone! + سيتم حذف كافة الرسائل - لا يمكن التراجع عن هذا! + + + All reports will be archived for you. + سيتم أرشفة كافة البلاغات لك. + + + All your contacts will remain connected. + ستبقى جميع جهات اتصالك متصلة. + + + All your contacts will remain connected. Profile update will be sent to your contacts. + ستبقى جميع جهات اتصالك متصلة. سيتم إرسال تحديث ملف التعريف إلى جهات اتصالك. + + + All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + جميع جهات الاتصال، المحادثات والملفات الخاصة بك سيتم تشفيرها بأمان ورفعها على شكل أجزاء إلى موجهات XFTP المُعدة. + + + Allow calls only if your contact allows them. + السماح بالمكالمات فقط إذا سمحت جهة اتصالك بذلك. + + + Allow message reactions only if your contact allows them. + اسمح بردود الفعل على الرسائل فقط إذا سمحت جهة اتصالك بذلك. + + + Allow to report messsages to moderators. + السماح بالإبلاغ عن الرسائل إلى المشرفين. + + + Allow your contacts to irreversibly delete sent messages. (24 hours) + اسمح لجهات اتصالك بحذف الرسائل المرسلة بشكل لا رجعة فيه. (24 ساعة) + + + Another reason + سبب آخر + + + App group: + مجموعة التطبيق: + + + Apply to + طبّق لِ + + + Archive + أرشف + + + Archive %lld reports? + أرشف تقارير %lld؟ + + + Archive all reports? + أرشفة كافة البلاغات؟ + + + Archive and upload + أرشفة و رفع + + + Archive report + أرشف البلاغ + + + Archive report? + أرشف البلاغ؟ + + + Archive reports + أرشف البلاغات + + + Ask + اسأل + + + Auto-accept settings + إعدادات القبول التلقائي + + + Better message dates. + تواريخ أفضل للرسائل. + + + Server added to operator %@. + تمت إضافة الخادم إلى المشغل %@. + + + Server address is incompatible with network settings: %@. + عنوان الخادم غير متوافق مع إعدادات الشبكة: %@. + + + Server protocol changed. + تغيّر بروتوكول الخادم. + + + SimpleX links are prohibited. + روابط SimpleX محظورة. + + + Additional accent + لون إضافي ثانوي + + + Always use private routing. + استخدم دائمًا التوجيه الخاص. + + + About operators + عن المُشغلين + + + Add team members + أضف أعضاء الفريق + + + Added media & file servers + أُضيفت خوادم الوسائط والملفات + + + Added message servers + أُضيفت خوادم الرسائل + + + Address or 1-time link? + عنوان أو رابط لمرة واحدة؟ + + + Address settings + إعدادات العنوان + + + Allow sharing + السماح بالمشاركة + + + App data migration + ترحيل بيانات التطبيق + + + Archive contacts to chat later. + أرشفة جهات الاتصال للدردشة لاحقًا. + + + Better networking + اتصال أفضل + + + Session code + رمز الجلسة + + + Set default theme + تعيين السمة الافتراضية + + + Set it instead of system authentication. + عيّنها بدلاً من استيثاق النظام. + + + Set the message shown to new members! + تعيين رسالة تظهر للأعضاء الجدد! + + + Share 1-time link + مشاركة رابط ذو استخدام واحد + + + Share 1-time link with a friend + شارك رابطًا لمرة واحدة مع صديق + + + Share SimpleX address on social media. + شارك عنوان SimpleX على وسائل التواصل الاجتماعي. + + + Share to SimpleX + المشاركة لSimpleX + + + Show calls in phone history + عرض المكالمات في سجل الهاتف + + + Show percentage + أظهِر النسبة المئوية + + + SimpleX + SimpleX + + + SimpleX Lock not enabled! + قفل SimpleX غير مفعّل! + + + Bad message hash + تجزئة رسالة سيئة + + + App session + جلسة التطبيق + + + SimpleX links not allowed + روابط SimpleX غير مسموح بها + + + All data is kept private on your device. + جميع البيانات تُحفظ بشكل خاص على جهازك. + + + Archived contacts + جهات الاتصال المؤرشفة + + + Show message status + أظهِر حالة الرسالة + + + Set message expiration in chats. + اضبط انتهاء صلاحية الرسالة في الدردشات. + + + Server address + عنوان الخادم + + + Show last messages + إظهار الرسائل الأخيرة + + + Server operator changed. + تغيّر مُشغل الخادم. + + + SimpleX address and 1-time links are safe to share via any messenger. + عنوان SimpleX والروابط لمرة واحدة آمنة للمشاركة عبر أي برنامج مُراسلة. + + + Add your team members to the conversations. + أضف أعضاء فريقك إلى المحادثات. + + + Advanced settings + إعدادات متقدّمة + + + Add to list + أضف إلى القائمة + + + Additional secondary + ثانوي إضافي + + + Admins can block a member for all. + يمكن للمُدراء حظر عضو للجميع. + + + All + الكل + + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + جميع الرسائل والملفات تُرسل **مشفرة من النهاية-إلى-النهاية**، مع أمان ما-بعد-الحوسبة-الكمية في الرسائل المباشرة. + + + All new messages from %@ will be hidden! + جميع الرسائل الجديدة من %@ سيتم إخفاؤها! + + + Auto-accept + قبول تلقائي + + + Change self-destruct mode + تغيير وضع التدمير الذاتي + + + Chat database exported + صُدرت قاعدة بيانات الدردشة + + + Businesses + الشركات + + + Change automatic message deletion? + تغيير حذف الرسائل التلقائي؟ + + + Can't call contact + لا يمكن مكالمة جهة الاتصال + + + Chat list + قائمة الدردشات + + + Calls prohibited! + المكالمات ممنوعة! + + + Change lock mode + تغيير وضع القفل + + + Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat. + توقفت الدردشة. إذا كنت قد استخدمت قاعدة البيانات هذه بالفعل على جهاز آخر، فيجب عليك نقلها مرة أخرى قبل بدء الدردشة. + + + Cellular + خلوي + + + Chat + الدردشة + + + Chat already exists! + الدردشة موجودة بالفعل! + + + Chat will be deleted for you - this cannot be undone! + سيتم حذف الدردشة لديك - لا يمكن التراجع عن هذا! + + + Chat will be deleted for all members - this cannot be undone! + سيتم حذف الدردشة لجميع الأعضاء - لا يمكن التراجع عن هذا! + + + Change self-destruct passcode + تغيير رمز المرور التدمير الذاتي + + + Camera not available + الكاميرا غير متوفرة + + + Capacity exceeded - recipient did not receive previously sent messages. + تم تجاوز السعة - لم يتلق المُستلم الرسائل المُرسلة مسبقًا. + + + Change passcode + تغيير رمز المرور + + + Chat colors + ألوان الدردشة + + + Chat theme + سمة الدردشة + + + Business address + عنوان العمل التجاري + + + Business chats + دردشات العمل التجاري + + + Cancel migration + ألغِ الترحيل + + + Change chat profiles + غيّر ملفات تعريف الدردشة + + + Chat migrated! + رحّلت الدردشة! + + + Chat profile + ملف تعريف الدردشة + + + Contact deleted! + حُذفت جهة الاتصال! + + + Conditions of use + شروط الاستخدام + + + Connecting + جارِ الاتصال + + + Connect incognito + اتصال متخفي + + + Created at + أُنشئ في + + + Connect via contact address + الاتصال عبر عنوان جهة الاتصال + + + Connected servers + الخوادم المتصلة + + + standard end-to-end encryption + التعمية القياسية بين الطرفين + + + Delete up to 20 messages at once. + حذف ما يصل إلى 20 رسالة في آن واحد. + + + Connect to your friends faster. + تواصل مع أصدقائك بشكل أسرع. + + + Developer options + خيارات المطور + + + Connect to yourself? + اتصل بنفسك؟ + + + Connect via one-time link + اتصال عبر رابط لمرة واحدة + + + Connect to yourself? +This is your own SimpleX address! + اتصل بنفسك؟ +هذا هو عنوان SimpleX الخاص بك! + + + Connecting to contact, please wait or check later! + جارِ الاتصال بجهة الاتصال، يُرجى الانتظار أو التحقق لاحقًا! + + + Database upgrade + ترقية قاعدة البيانات + + + Create list + أنشئ قائمة + + + Create profile + إنشاء ملف تعريف + + + Creating archive link + جارِ إنشاء رابط الأرشيف + + + Details + التفاصيل + + + Customize theme + تخصيص السمة + + + Dark mode colors + ألوان الوضع الداكن + + + Delete and notify contact + حذف وإشعار جهة الاتصال + + + Deleted at: %@ + حُذفت في: %@ + + + Detailed statistics + إحصائيات مفصلة + + + you are observer + أنت المراقب + + + you + أنت + + + when IP hidden + عندما يكون IP مخفيًا + + + video + فيديو + + + Clear or delete group? + مسح أو حذف المجموعة؟ + + + Clear private notes? + مسح الملاحظات الخاصة؟ + + + Community guidelines violation + انتهاك إرشادات المجتمع + + + Connection not ready. + الاتصال غير جاهز. + + + Connection requires encryption renegotiation. + يتطلب الاتصال إعادة التفاوض على التعمية. + + + Contact is deleted. + حُذفت جهة الاتصال. + + + Contacts + جهات الاتصال + + + Create SimpleX address + أنشئ عنوان SimpleX + + + Current conditions text couldn't be loaded, you can review conditions via this link: + لا يمكن تحميل نص الشروط الحالية، يمكنك مراجعة الشروط عبر هذا الرابط: + + + Delete chat messages from your device. + احذف رسائل الدردشة من جهازك. + + + Delete or moderate up to 200 messages. + حذف أو إشراف ما يصل إلى 200 رسالة. + + + Delete profile + حذف ملف التعريف + + + Desktop devices + أجهزة سطح المكتب + + + set new profile picture + عيّن صورة تعريفية جديدة + + + weeks + أسابيع + + + Chunks uploaded + رُفع القطع + + + Color mode + وضع اللون + + + Created + أُنشئت + + + Current Passcode + رمز المرور الحالي + + + Custom time + وقت مخصّص + + + Debug delivery + تسليم التصحيح + + + Deleted + حُذفت + + + Delete file + حذف الملف + + + unknown status + حالة غير معروفة + + + unknown servers + خوادم غير معروفة + + + Connect to yourself? +This is your own one-time link! + اتصل بنفسك؟ +هذا هو الرابط الخاص بك لمرة واحدة! + + + Connect with %@ + الاتصال ب%@ + + + Connected desktop + سطح المكتب متصل + + + Connected to desktop + متصل بسطح المكتب + + + Conversation deleted! + حُذفت المحادثة! + + + Create a group using a random profile. + أنشئ مجموعة باستخدام ملف تعريف عشوائي. + + + Delete chat + احذف الدردشة + + + Delete chat profile + حذف ملف تعريف الدردشة + + + Delete chat? + حذف الدردشة؟ + + + Delete database from this device + احذف قاعدة البيانات من هذا الجهاز + + + Delivery + التوصيل + + + Delivery receipts are disabled! + إيصالات التسليم مُعطَّلة! + + + Connection terminated + انتهى الاتصال + + + Create file + إنشاء ملف + + + Create group + أنشئ مجموعة + + + Database IDs and Transport isolation option. + معرفات قاعدة البيانات وخيار عزل النقل. + + + Database downgrade + الرجوع إلى إصدار سابق من قاعدة البيانات + + + Delivery receipts! + إيصالات التسليم! + + + Desktop address + عنوان سطح المكتب + + + updated profile + حدّثت ملف التعريف + + + Connect to desktop + اتصل بسطح المكتب + + + Connecting to desktop + جار الاتصال بسطح المكتب + + + Completed + اكتملت + + + Connection notifications + إشعارات الاتصال + + + Connection and servers status. + حالة الاتصال والخوادم. + + + Continue + متابعة + + + Connections + الاتصالات + + + Content violates conditions of use + المحتوى ينتهك شروط الاستخدام + + + Corner + ركن + + + Creating link… + جارِ إنشاء الرابط… + + + Database ID: %d + معرّف قاعدة البيانات: %d + + + Decryption error + خطأ في فك التعمية + + + Delete report + احذف البلاغ + + + Delete without notification + احذف دون إشعار + + + Deleted at + حُذفت في + + + Clear group? + مسح المجموعة؟ + + + Compare file + قارن الملف + + + Connect automatically + اتصل تلقائيًا + + + Connection blocked + حُظر الاتصال + + + unprotected + غير محمي + + + Deletion errors + أخطاء الحذف + + + Conditions will be accepted for enabled operators after 30 days. + سيتم قبول الشروط للمُشغلين المفعّلين بعد 30 يومًا. + + + Connection security + أمان الاتصال + + + Contact will be deleted - this cannot be undone! + سيتم حذف جهة الاتصال - لا يمكن التراجع عن هذا! + + + Copy error + خطأ في النسخ + + + Create 1-time link + أنشئ رابط لمرة واحدة + + + Connected + متصل + + + Current profile + ملف التعريف الحالي + + + Customizable message shape. + شكل الرسالة قابل للتخصيص. + + + Chunks deleted + حُذفت القطع + + + Chinese and Spanish interface + الواجهة الصينية والاسبانية + + + Download + نزّل + + + Downloaded + نُزّلت + + + Downloaded files + الملفات التي نُزّلت + + + Don't show again + لا تُظهر مرة أخرى + + + Confirm contact deletion? + تأكيد حذف جهة الاتصال؟ + + + Confirm database upgrades + تأكيد ترقيات قاعدة البيانات + + + Download failed + فشل التنزيل + + + Download file + نزّل الملف + + + Downloading link details + جارِ تنزيل تفاصيل الرابط + + + Downloading archive + جارِ تنزيل الأرشيف + + + Don't enable + لا تُفعل + + + Confirm upload + أكّد الرفع + + + Chunks downloaded + نُزّلت القطع + + + Confirm Passcode + تأكيد رمز المرور + + + Confirm files from unknown servers. + تأكيد الملفات من خوادم غير معروفة. + + + Confirm network settings + أكّد إعدادات الشبكة + + + Confirm that you remember database passphrase to migrate it. + تأكد من أنك تتذكر عبارة مرور قاعدة البيانات لترحيلها. + + + Downgrade and open chat + الرجوع إلى إصدار سابق وفتح الدردشة + + + Don't miss important messages. + لا تفوت رسائل مهمة. + + + E2E encrypted notifications. + إشعارات مُشفرة بين الطرفين E2E + + + Download errors + أخطاء التنزيل + + + Download files + نزّل الملفات + + + Confirm password + تأكيد كلمة المرور + + + Enable self-destruct + تفعيل التدمير الذاتي + + + Enable (keep overrides) + تفعيل (الاحتفاظ بالتجاوزات) + + + Enable Flux + فعّل flux + + + Enable in direct chats (BETA)! + فعّل في الدردشات المباشرة (تجريبي)! + + + Enable for all + تفعيل للجميع + + + Enable lock + تفعيل القفل + + + Enable camera access + فعّل الوصول إلى الكاميرا + + + Enable self-destruct passcode + تفعيل رمز التدمير الذاتي + + + Can't message member + لا يمكن الاتصال بالعضو + + + Color chats with the new themes. + محادثات ملونة مع السمات الجديدة. + + + All chats will be removed from the list %@, and the list deleted. + ستتم إزالة جميع الدردشات من القائمة %@، وسيتم حذف القائمة. + + + Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + البلغارية والفنلندية والتايلاندية والأوكرانية - شكرًا للمستخدمين و[Weblate] (https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + + + Choose _Migrate from another device_ on the new device and scan QR code. + اختر _الترحيل من جهاز آخر_ على الجهاز الجديد وامسح رمز الاستجابة السريعة. + + + Conditions will be accepted for the operator(s): **%@**. + سيتم قبول شروط المشغل (المشغلين): **%@**. + + + Conditions will be accepted on: %@. + سيتم قبول الشروط على: %@. + + + Confirmed + تم التأكيد + + + Connection is blocked by server operator: +%@ + تم حظر الاتصال من قبل مشغل الخادم: +%@ + + + Can't call member + لا يمكن الاتصال بالعضو + + + Chat already exists + الدردشة موجودة بالفعل + + + Check messages every 20 min. + تحقق من الرسائل كل 20 دقيقة. + + + Check messages when allowed. + تحقق من الرسائل عندما يُسمح بذلك. + + + Cannot forward message + لا يمكن إعادة توجيه الرسالة + + + Chat preferences were changed. + تم تغيير تفضيلات المحادثة. + + + Conditions are already accepted for these operator(s): **%@**. + الشروط مقبولة بالفعل لهذا المشغل (المشغلين): **%@**. + + + Conditions will be accepted for operator(s): **%@**. + سيتم قبول شروط المشغل (المشغلين): **%@**. + + + Conditions accepted on: %@. + الشروط المقبولة على: %@. + + + Conditions are accepted for the operator(s): **%@**. + يتم قبول شروط المشغل (المشغلين): **%@**. + + + Conditions will be automatically accepted for enabled operators on: %@. + سيتم قبول الشروط تلقائيًا للمشغلين الممكّنين على: %@. + + + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 + أنشئ ملفًا شخصيًا جديدًا في [تطبيق سطح المكتب](https://simplex.chat/downloads/). 💻 + + + Error adding server + خطأ في إضافة الخادم + + + Created at: %@ + تم الإنشاء في: %@ + + + Delete %lld messages of members? + حذف %lld الرسائل القديمة للأعضاء؟ + + + Disappearing message + رسالة اختفاء + + + Enabled + ممكّنة + + + Encrypted message: database migration error + رسالة مشفرة: خطأ في ترحيل قاعدة البيانات + + + Delete list? + Delete list? + + + Delivered even when Apple drops them. + يتم تسليمها حتى عندما تسقطها شركة Apple. + + + Destination server address of %@ is incompatible with forwarding server %@ settings. + عنوان خادم الوجهة %@ غير متوافق مع إعدادات خادم التوجيه %@. + + + Destination server version of %@ is incompatible with forwarding server %@. + إصدار خادم الوجهة لـ %@ غير متوافق مع خادم التوجيه %@. + + + Don't create address + لا تنشئ عنوان + + + Done + تم + + + Duration + المدة + + + Encrypt local files + تشفير الملفات المحلية + + + Encryption renegotiation in progress. + إعادة التفاوض على التشفير قيد التنفيذ. + + + Enter Passcode + أدخل رمز المرور + + + Enter passphrase + قم بأدخل عبارة المرور + + + Enter welcome message… + أدخل رسالة ترحيب… + + + Enter your name… + أدخل اسمك… + + + Error changing to incognito! + خطأ في التغيير إلى التصفح المتخفي! + + + Delete %lld messages? + حذف %lld رسائل؟ + + + Error aborting address change + خطأ في إجهاض تغيير العنوان + + + Disappears at + يختفي عند + + + Do not use credentials with proxy. + لا تستخدم بيانات الاعتماد مع البروكسي. + + + Error accepting conditions + خطأ في قبول الشروط + + + Enter password above to show! + أدخل كلمة المرور أعلاه للعرض! + + + Error changing connection profile + خطأ في تغيير ملف تعريف الاتصال + + + Desktop app version %@ is not compatible with this app. + إصدار تطبيق سطح المكتب %@ غير متوافق مع هذا التطبيق. + + + Encrypt stored files & media + تشفير الملفات والوسائط المخزنة + + + Enter this device name… + أدخل اسم الجهاز… + + + Enter welcome message… (optional) + أدخل رسالة ترحيب... (اختياري) + + + Correct name to %@? + الاسم الصحيح ل %@؟ + + + Delete member message? + حذف رسالة العضو؟ + + + Disable automatic message deletion? + تعطيل حذف الرسائل التلقائي؟ + + + Disable delete messages + تعطيل حذف الرسائل + + + Disable for all + تعطيل للجميع + + + Disabled + عاجز + + + Documents: + المستندات: + + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + باستخدامك SimpleX Chat، فإنك توافق على: +- إرسال محتوى قانوني فقط في المجموعات العامة. +- احترام المستخدمين الآخرين - ممنوع إرسال رسائل مزعجة. + + + Configure server operators + تكوين مشغلي الخادم + + + Enable Flux in Network & servers settings for better metadata privacy. + تمكين التدفق في إعدادات الشبكة والخوادم لتحسين خصوصية البيانات الوصفية. + + + Discover and join groups + اكتشف المجموعات وانضم إليها + + + Discover via local network + اكتشف عبر الشبكة المحلية + + + Enabled for + ممكّن ل + + + Encrypted message: app is stopped + رسالة مشفرة: تم إيقاف التطبيق + + + Enter group name… + أدخل اسم المجموعة… + + + Do NOT use private routing. + لا تستخدم التوجيه الخاص. + + + Encryption re-negotiation error + خطأ في إعادة تفاوض التشفير + + + Connection with desktop stopped + تم إيقاف الاتصال بسطح المكتب + + + Destination server error: %@ + خطأ خادم الوجهة: %@ + + + Do NOT send messages directly, even if your or destination server does not support private routing. + لا ترسل الرسائل بشكل مباشر، حتى لو كان خادمك أو خادم الوجهة لا يدعم التوجيه الخاص. + + + Direct messages between members are prohibited in this chat. + يُحظر إرسال الرسائل المباشرة بين الأعضاء في هذه الدردشة. + + + Disconnect desktop? + فصل سطح المكتب؟ + + + Disable (keep overrides) + تعطيل (الاحتفاظ بالتجاوزات) + + + Disappears at: %@ + يختفي عند: %@ + + + Do not send history to new members. + لا ترسل التاريخ إلى الأعضاء الجدد. + + + Encryption re-negotiation failed. + فشل إعادة التفاوض على التشفير. + @@ -3723,4 +5805,80 @@ SimpleX servers cannot see your profile. + + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + يمكنك السماح بالمشاركة في إعدادات الخصوصية والأمان / اعدادات "SimpleX Lock" + + + Keychain error + خطأ في Keychain + + + Invalid migration confirmation + تأكيد الترحيل غير صالح + + + %@ + %@ + + + Share + مشاركة + + + Incompatible database version + إصدار قاعدة بيانات غير متوافق + + + File error + خطأ في الملف + + + Database downgrade required + مطلوب الرجوع إلى إصدار سابق من قاعدة البيانات‎ + + + Database encrypted! + قاعدة البيانات مُعمّاة! + + + Wrong database passphrase + عبارة مرور قاعدة بيانات خاطئة + + + Selected chat preferences prohibit this message. + تفضيلات الدردشة المحدّدة تحظر هذه الرسالة. + + + Database error + خطأ في قاعدة البيانات + + + Database passphrase is required to open chat. + عبارة مرور قاعدة البيانات مطلوبة لفتح الدردشة. + + + Error: %@ + خطأ: %@ + + + Cancel + إلغاء + + + Large file! + الملف كبير! + + + + + + + From: %@ + من: %@ + + + diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff index f04880ee91..776199ac1f 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (може да се копира) @@ -109,6 +82,7 @@ %@ downloaded + %@ изтеглено No comment provided by engineer. @@ -126,13 +100,17 @@ %@ е потвърдено No comment provided by engineer. + + %@ server + No comment provided by engineer. + %@ servers - %@ сървъри No comment provided by engineer. %@ uploaded + %@ качено No comment provided by engineer. @@ -140,6 +118,11 @@ %@ иска да се свърже! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ и %lld членове @@ -160,11 +143,31 @@ %d дни time interval + + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d часа time interval + + %d messages not forwarded + alert title + %d min %d мин. @@ -180,6 +183,10 @@ %d сек. time interval + + %d seconds(s) + delete after time + %d skipped message(s) %d пропуснато(и) съобщение(я) @@ -250,11 +257,6 @@ %lld нови езици на интерфейса No comment provided by engineer. - - %lld second(s) - %lld секунда(и) - No comment provided by engineer. - %lld seconds %lld секунди @@ -305,11 +307,6 @@ %u пропуснати съобщения. No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) (ново) @@ -320,19 +317,9 @@ (това устройство v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - **Добави контакт**: за създаване на нов линк или свързване чрез получен линк за връзка. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Добави нов контакт**: за да създадете своя еднократен QR код или линк за вашия контакт. + + **Create 1-time link**: to create and share a new invitation link. + **Добави контакт**: за създаване на нов линк. No comment provided by engineer. @@ -340,18 +327,19 @@ **Създай група**: за създаване на нова група. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **По поверително**: проверявайте новите съобщения на всеки 20 минути. Токенът на устройството се споделя със сървъра за чат SimpleX, но не и колко контакти или съобщения имате. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **Най-поверително**: не използвайте сървъра за известия SimpleX Chat, периодично проверявайте съобщенията във фонов режим (зависи от това колко често използвате приложението). No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Моля, обърнете внимание**: използването на една и съща база данни на две устройства ще наруши декриптирането на съобщенията от вашите връзки като защита на сигурността. No comment provided by engineer. @@ -359,11 +347,15 @@ **Моля, обърнете внимание**: НЯМА да можете да възстановите или промените паролата, ако я загубите. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Препоръчително**: токенът на устройството и известията се изпращат до сървъра за уведомяване на SimpleX Chat, но не и съдържанието, размерът на съобщението или от кого е. No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **Внимание**: Незабавните push известия изискват парола, запазена в Keychain. @@ -371,6 +363,7 @@ **Warning**: the archive will be removed. + **Внимание**: архивът ще бъде изтрит. No comment provided by engineer. @@ -388,11 +381,6 @@ \*удебелен* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -429,11 +417,6 @@ - история на редактиране. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 сек @@ -447,7 +430,8 @@ 1 day 1 ден - time interval + delete after time +time interval 1 hour @@ -462,12 +446,27 @@ 1 month 1 месец - time interval + delete after time +time interval 1 week 1 седмица - time interval + delete after time +time interval + + + 1 year + delete after time + + + 1-time link + Еднократен линк + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + No comment provided by engineer. 5 minutes @@ -484,11 +483,6 @@ 30 секунди No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -538,31 +532,32 @@ Откажи смяна на адрес? No comment provided by engineer. - - About SimpleX - За SimpleX - No comment provided by engineer. - About SimpleX Chat За SimpleX Chat No comment provided by engineer. - - About SimpleX address - Повече за SimpleX адреса + + About operators + За операторите No comment provided by engineer. - - Accent color - Основен цвят + + Accent + Акцент No comment provided by engineer. Accept Приеми accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Приеми условията + No comment provided by engineer. Accept connection request? @@ -577,21 +572,45 @@ Accept incognito Приеми инкогнито - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + Приети условия + No comment provided by engineer. + + + Acknowledged + Потвърден + No comment provided by engineer. + + + Acknowledgement errors + Грешки при потвърждението + No comment provided by engineer. + + + Active + token status text + + + Active connections + Активни връзки + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Добавете адрес към вашия профил, така че вашите контакти да могат да го споделят с други хора. Актуализацията на профила ще бъде изпратена до вашите контакти. No comment provided by engineer. - - Add contact - Добави контакт + + Add friends + Добави приятели No comment provided by engineer. - - Add preset servers - Добави предварително зададени сървъри + + Add list No comment provided by engineer. @@ -599,14 +618,19 @@ Добави профил No comment provided by engineer. + + Add server + Добави сървър + No comment provided by engineer. + Add servers by scanning QR codes. Добави сървъри чрез сканиране на QR кодове. No comment provided by engineer. - - Add server… - Добави сървър… + + Add team members + Добави членове на екипа No comment provided by engineer. @@ -614,11 +638,45 @@ Добави към друго устройство No comment provided by engineer. + + Add to list + No comment provided by engineer. + Add welcome message Добави съобщение при посрещане No comment provided by engineer. + + Add your team members to the conversations. + Добавете членовете на вашия екип към разговорите. + No comment provided by engineer. + + + Added media & file servers + Добавени медийни и файлови сървъри + No comment provided by engineer. + + + Added message servers + Добавени сървъри за съобщения + No comment provided by engineer. + + + Additional accent + Допълнителен акцент + No comment provided by engineer. + + + Additional accent 2 + Допълнителен акцент 2 + No comment provided by engineer. + + + Additional secondary + Допълнителен вторичен + No comment provided by engineer. + Address Адрес @@ -629,8 +687,19 @@ Промяната на адреса ще бъде прекъсната. Ще се използва старият адрес за получаване. No comment provided by engineer. + + Address or 1-time link? + Адрес или еднократен линк? + No comment provided by engineer. + + + Address settings + Настройки на адреса + No comment provided by engineer. + Admins can block a member for all. + Администраторите могат да блокират член за всички. No comment provided by engineer. @@ -643,6 +712,15 @@ Разширени мрежови настройки No comment provided by engineer. + + Advanced settings + Разширени настройки + No comment provided by engineer. + + + All + No comment provided by engineer. + All app data is deleted. Всички данни от приложението бяха изтрити. @@ -653,16 +731,29 @@ Всички чатове и съобщения ще бъдат изтрити - това не може да бъде отменено! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + alert message + All data is erased when it is entered. Всички данни се изтриват при въвеждане. No comment provided by engineer. + + All data is kept private on your device. + Всички данни се съхраняват поверително на вашето устройство. + No comment provided by engineer. + All group members will remain connected. Всички членове на групата ще останат свързани. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! Всички съобщения ще бъдат изтрити - това не може да бъде отменено! @@ -678,6 +769,19 @@ Всички нови съобщения от %@ ще бъдат скрити! No comment provided by engineer. + + All profiles + Всички профили + profile dropdown + + + All reports will be archived for you. + No comment provided by engineer. + + + All servers + No comment provided by engineer. + All your contacts will remain connected. Всички ваши контакти ще останат свързани. @@ -690,6 +794,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Всички ваши контакти, разговори и файлове ще бъдат сигурно криптирани и качени на парчета в конфигурираните XFTP релета. No comment provided by engineer. @@ -702,11 +807,21 @@ Позволи обаждания само ако вашият контакт ги разрешава. No comment provided by engineer. + + Allow calls? + Позволи обаждания? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Позволи изчезващи съобщения само ако вашият контакт ги разрешава. No comment provided by engineer. + + Allow downgrade + Позволи понижаване + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Позволи необратимо изтриване на съобщение само ако вашият контакт го рарешава. (24 часа) @@ -732,11 +847,25 @@ Разреши изпращането на изчезващи съобщения. No comment provided by engineer. + + Allow sharing + Позволи споделяне + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Позволи необратимо изтриване на изпратените съобщения. (24 часа) No comment provided by engineer. + + Allow to report messsages to moderators. + No comment provided by engineer. + + + Allow to send SimpleX links. + Разрешаване на изпращане на SimpleX линкове. + No comment provided by engineer. + Allow to send files and media. Позволи изпращане на файлове и медия. @@ -797,6 +926,11 @@ Вече се присъединихте към групата! No comment provided by engineer. + + Always use private routing. + Винаги използвай поверително рутиране. + No comment provided by engineer. + Always use relay Винаги използвай реле @@ -807,11 +941,20 @@ Създаен беше празен профил за чат с предоставеното име и приложението се отвари както обикновено. No comment provided by engineer. + + Another reason + report reason + Answer call Отговор на повикване No comment provided by engineer. + + Anybody can host servers. + Протокол и код с отворен код – всеки може да оперира собствени сървъри. + No comment provided by engineer. + App build: %@ Компилация на приложението: %@ @@ -819,6 +962,7 @@ App data migration + Миграция на данните от приложението No comment provided by engineer. @@ -826,6 +970,10 @@ Приложението криптира нови локални файлове (с изключение на видеоклипове). No comment provided by engineer. + + App group: + No comment provided by engineer. + App icon Икона на приложението @@ -841,6 +989,11 @@ Кода за достъп до приложение се заменя с код за самоунищожение. No comment provided by engineer. + + App session + Сесия на приложението + No comment provided by engineer. + App version Версия на приложението @@ -858,14 +1011,56 @@ Apply + Приложи + No comment provided by engineer. + + + Apply to + Приложи към + No comment provided by engineer. + + + Archive + No comment provided by engineer. + + + Archive %lld reports? + No comment provided by engineer. + + + Archive all reports? No comment provided by engineer. Archive and upload + Архивиране и качване + No comment provided by engineer. + + + Archive contacts to chat later. + Архивирайте контактите, за да разговаряте по-късно. + No comment provided by engineer. + + + Archive report + No comment provided by engineer. + + + Archive report? + No comment provided by engineer. + + + Archive reports + swipe action + + + Archived contacts + Архивирани контакти No comment provided by engineer. Archiving database + Архивиране на база данни No comment provided by engineer. @@ -928,11 +1123,21 @@ Автоматично приемане на изображения No comment provided by engineer. + + Auto-accept settings + Автоматично приемане на настройки + alert title + Back Назад No comment provided by engineer. + + Background + Фон + No comment provided by engineer. + Bad desktop address Грешен адрес на настолното устройство @@ -948,16 +1153,59 @@ Лош хеш на съобщението No comment provided by engineer. + + Better calls + По-добри обаждания + No comment provided by engineer. + Better groups По-добри групи No comment provided by engineer. + + Better groups performance + No comment provided by engineer. + + + Better message dates. + По-добри дати на съобщението. + No comment provided by engineer. + Better messages По-добри съобщения No comment provided by engineer. + + Better networking + Подобрена мрежа + No comment provided by engineer. + + + Better notifications + Подобрени известия + No comment provided by engineer. + + + Better privacy and security + No comment provided by engineer. + + + Better security ✅ + По-добра сигурност ✅ + No comment provided by engineer. + + + Better user experience + Подобрен интерфейс + No comment provided by engineer. + + + Black + Черна + No comment provided by engineer. + Block Блокирай @@ -993,6 +1241,16 @@ Блокиран от админ No comment provided by engineer. + + Blur for better privacy. + Размазване за по-добра поверителност. + No comment provided by engineer. + + + Blur media + Размазване на медия + No comment provided by engineer. + Both you and your contact can add message reactions. И вие, и вашият контакт можете да добавяте реакции към съобщението. @@ -1023,11 +1281,31 @@ Български, финландски, тайландски и украински - благодарение на потребителите и [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + Бизнес адрес + No comment provided by engineer. + + + Business chats + Бизнес чатове + No comment provided by engineer. + + + Businesses + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). Чрез чат профил (по подразбиране) или [чрез връзка](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (БЕТА). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + No comment provided by engineer. + Call already ended! Разговорът вече приключи! @@ -1038,11 +1316,26 @@ Обаждания No comment provided by engineer. + + Calls prohibited! + Обажданията са забранени! + No comment provided by engineer. + Camera not available Камерата е неодстъпна No comment provided by engineer. + + Can't call contact + Обаждането на контакта не е позволено + No comment provided by engineer. + + + Can't call member + Обаждането на члена не е позволено + No comment provided by engineer. + Can't invite contact! Не може да покани контакта! @@ -1053,13 +1346,19 @@ Не може да поканят контактите! No comment provided by engineer. + + Can't message member + No comment provided by engineer. + Cancel Отказ - No comment provided by engineer. + alert action +alert button Cancel migration + Отмени миграцията No comment provided by engineer. @@ -1067,9 +1366,23 @@ Няма достъп до Keychain за запазване на паролата за базата данни No comment provided by engineer. + + Cannot forward message + No comment provided by engineer. + Cannot receive file Файлът не може да бъде получен + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Капацитетът е надвишен - получателят не е получил предишно изпратените съобщения. + snd error text + + + Cellular + Мобилна мрежа No comment provided by engineer. @@ -1077,6 +1390,15 @@ Промени No comment provided by engineer. + + Change automatic message deletion? + alert title + + + Change chat profiles + Промени чат профилите + authentication reason + Change database passphrase? Промяна на паролата на базата данни? @@ -1121,11 +1443,22 @@ Change self-destruct passcode Промени кода за достъп за самоунищожение authentication reason - set passcode view +set passcode view - - Chat archive - Архив на чата + + Chat + No comment provided by engineer. + + + Chat already exists + No comment provided by engineer. + + + Chat already exists! + No comment provided by engineer. + + + Chat colors No comment provided by engineer. @@ -1135,7 +1468,7 @@ Chat database - База данни за чата + База данни No comment provided by engineer. @@ -1143,9 +1476,13 @@ Базата данни на чата е изтрита No comment provided by engineer. + + Chat database exported + No comment provided by engineer. + Chat database imported - Базата данни на чат е импортирана + Базата данни на е импортирана No comment provided by engineer. @@ -1163,8 +1500,13 @@ Чатът е спрян. Ако вече сте използвали тази база данни на друго устройство, трябва да я прехвърлите обратно, преди да стартирате чата отново. No comment provided by engineer. + + Chat list + No comment provided by engineer. + Chat migrated! + Чатът е мигриран! No comment provided by engineer. @@ -1172,15 +1514,44 @@ Чат настройки No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + Потребителски профил + No comment provided by engineer. + + + Chat theme + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + No comment provided by engineer. + Chats Чатове No comment provided by engineer. + + Check messages every 20 min. + No comment provided by engineer. + + + Check messages when allowed. + No comment provided by engineer. + Check server address and try again. Проверете адреса на сървъра и опитайте отново. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1189,6 +1560,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + Изберете _Мигриране от друго устройство_ на новото устройство и сканирайте QR кода. No comment provided by engineer. @@ -1201,10 +1573,22 @@ Избери от библиотеката No comment provided by engineer. + + Chunks deleted + No comment provided by engineer. + + + Chunks downloaded + No comment provided by engineer. + + + Chunks uploaded + No comment provided by engineer. + Clear Изчисти - No comment provided by engineer. + swipe action Clear conversation @@ -1216,6 +1600,14 @@ Изчисти разговора? No comment provided by engineer. + + Clear group? + No comment provided by engineer. + + + Clear or delete group? + No comment provided by engineer. + Clear private notes? Изчистване на лични бележки? @@ -1226,11 +1618,18 @@ Изчисти проверката No comment provided by engineer. - - Colors - Цветове + + Color chats with the new themes. No comment provided by engineer. + + Color mode + No comment provided by engineer. + + + Community guidelines violation + report reason + Compare file Сравни файл @@ -1241,11 +1640,47 @@ Сравнете кодовете за сигурност с вашите контакти. No comment provided by engineer. + + Completed + No comment provided by engineer. + + + Conditions accepted on: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + No comment provided by engineer. + + + Conditions of use + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + No comment provided by engineer. + Configure ICE servers Конфигурирай ICE сървъри No comment provided by engineer. + + Configure server operators + No comment provided by engineer. + Confirm Потвърди @@ -1256,13 +1691,22 @@ Потвърди kодa за достъп No comment provided by engineer. + + Confirm contact deletion? + No comment provided by engineer. + Confirm database upgrades Потвърди актуализаациите на базата данни No comment provided by engineer. + + Confirm files from unknown servers. + No comment provided by engineer. + Confirm network settings + Потвърди мрежовите настройки No comment provided by engineer. @@ -1277,12 +1721,18 @@ Confirm that you remember database passphrase to migrate it. + Потвърдете, че помните паролата на базата данни, преди да я мигрирате. No comment provided by engineer. Confirm upload + Потвърди качването No comment provided by engineer. + + Confirmed + token status text + Connect Свързване @@ -1303,6 +1753,10 @@ Свързване с настолно устройство No comment provided by engineer. + + Connect to your friends faster. + No comment provided by engineer. + Connect to yourself? Свърване със себе си? @@ -1342,16 +1796,28 @@ This is your own one-time link! Свързване с %@ No comment provided by engineer. + + Connected + No comment provided by engineer. + Connected desktop Свързано настолно устройство No comment provided by engineer. + + Connected servers + No comment provided by engineer. + Connected to desktop Свързан с настолно устройство No comment provided by engineer. + + Connecting + No comment provided by engineer. + Connecting to server… Свързване със сървъра… @@ -1362,6 +1828,10 @@ This is your own one-time link! Свързване със сървър…(грешка: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + No comment provided by engineer. + Connecting to desktop Свързване с настолно устройство @@ -1372,6 +1842,14 @@ This is your own one-time link! Връзка No comment provided by engineer. + + Connection and servers status. + No comment provided by engineer. + + + Connection blocked + No comment provided by engineer. + Connection error Грешка при свързване @@ -1382,11 +1860,32 @@ This is your own one-time link! Грешка при свързване (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + No comment provided by engineer. + + + Connection not ready. + No comment provided by engineer. + + + Connection notifications + No comment provided by engineer. + Connection request sent! Заявката за връзка е изпратена! No comment provided by engineer. + + Connection requires encryption renegotiation. + No comment provided by engineer. + + + Connection security + No comment provided by engineer. + Connection terminated Връзката е прекратена @@ -1397,6 +1896,14 @@ This is your own one-time link! Времето на изчакване за установяване на връзката изтече No comment provided by engineer. + + Connection with desktop stopped + No comment provided by engineer. + + + Connections + No comment provided by engineer. + Contact allows Контактът позволява @@ -1407,6 +1914,10 @@ This is your own one-time link! Контактът вече съществува No comment provided by engineer. + + Contact deleted! + No comment provided by engineer. + Contact hidden: Контактът е скрит: @@ -1417,9 +1928,8 @@ This is your own one-time link! Контактът е свързан notification - - Contact is not connected yet! - Контактът все още не е свързан! + + Contact is deleted. No comment provided by engineer. @@ -1432,6 +1942,10 @@ This is your own one-time link! Настройки за контакт No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + No comment provided by engineer. + Contacts Контакти @@ -1442,21 +1956,37 @@ This is your own one-time link! Контактите могат да маркират съобщения за изтриване; ще можете да ги разглеждате. No comment provided by engineer. + + Content violates conditions of use + blocking reason + Continue Продължи No comment provided by engineer. + + Conversation deleted! + No comment provided by engineer. + Copy Копирай - chat item action + No comment provided by engineer. + + + Copy error + No comment provided by engineer. Core version: v%@ Версия на ядрото: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? Поправи име на %@? @@ -1464,32 +1994,32 @@ This is your own one-time link! Create - Създай + Създаване + No comment provided by engineer. + + + Create 1-time link + Създаване на еднократна препратка No comment provided by engineer. Create SimpleX address - Създай SimpleX адрес + Създаване на адрес в SimpleX No comment provided by engineer. Create a group using a random profile. - Създай група с автоматично генериран профилл. - No comment provided by engineer. - - - Create an address to let people connect with you. - Създайте адрес, за да позволите на хората да се свързват с вас. + Създаване група с автоматично създаден профил. No comment provided by engineer. Create file - Създай файл + Създаване на файл server test step Create group - Създай група + Създаване на група No comment provided by engineer. @@ -1502,6 +2032,10 @@ This is your own one-time link! Създай линк No comment provided by engineer. + + Create list + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 Създайте нов профил в [настолното приложение](https://simplex.chat/downloads/). 💻 @@ -1527,6 +2061,10 @@ This is your own one-time link! Създай своя профил No comment provided by engineer. + + Created + No comment provided by engineer. + Created at Създаден на @@ -1537,13 +2075,9 @@ This is your own one-time link! Създаден на: %@ copied message info - - Created on %@ - Създаден на %@ - No comment provided by engineer. - Creating archive link + Създаване на архивен линк No comment provided by engineer. @@ -1556,11 +2090,19 @@ This is your own one-time link! Текущ kод за достъп No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + No comment provided by engineer. + Current passphrase… Текуща парола… No comment provided by engineer. + + Current profile + No comment provided by engineer. + Currently maximum supported file size is %@. В момента максималният поддържан размер на файла е %@. @@ -1571,11 +2113,23 @@ This is your own one-time link! Персонализирано време No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + + + Customize theme + No comment provided by engineer. + Dark Тъмна No comment provided by engineer. + + Dark mode colors + No comment provided by engineer. + Database ID ID в базата данни @@ -1674,6 +2228,10 @@ This is your own one-time link! Базата данни ще бъде мигрирана, когато приложението се рестартира No comment provided by engineer. + + Debug delivery + No comment provided by engineer. + Decentralized Децентрализиран @@ -1687,18 +2245,18 @@ This is your own one-time link! Delete Изтрий - chat item action + alert action +swipe action + + + Delete %lld messages of members? + No comment provided by engineer. Delete %lld messages? Изтриване на %lld съобщения? No comment provided by engineer. - - Delete Contact - Изтрий контакт - No comment provided by engineer. - Delete address Изтрий адрес @@ -1724,14 +2282,12 @@ This is your own one-time link! Изтрий и уведоми контакт No comment provided by engineer. - - Delete archive - Изтрий архив + + Delete chat No comment provided by engineer. - - Delete chat archive? - Изтриване на архива на чата? + + Delete chat messages from your device. No comment provided by engineer. @@ -1744,6 +2300,10 @@ This is your own one-time link! Изтриване на чат профила? No comment provided by engineer. + + Delete chat? + No comment provided by engineer. + Delete connection Изтрий връзката @@ -1754,11 +2314,8 @@ This is your own one-time link! Изтрий контакт No comment provided by engineer. - - Delete contact? -This cannot be undone! - Изтрий контакт? -Това не може да бъде отменено! + + Delete contact? No comment provided by engineer. @@ -1768,6 +2325,7 @@ This cannot be undone! Delete database from this device + Изтриване на базата данни от това устройство No comment provided by engineer. @@ -1820,6 +2378,10 @@ This cannot be undone! Изтрий линк? No comment provided by engineer. + + Delete list? + alert title + Delete member message? Изтрий съобщението на члена? @@ -1833,7 +2395,7 @@ This cannot be undone! Delete messages Изтрий съобщенията - No comment provided by engineer. + alert button Delete messages after @@ -1850,9 +2412,8 @@ This cannot be undone! Изтрий старата база данни? No comment provided by engineer. - - Delete pending connection - Изтрий предстоящата връзка + + Delete or moderate up to 200 messages. No comment provided by engineer. @@ -1870,11 +2431,27 @@ This cannot be undone! Изтрий опашка server test step + + Delete report + No comment provided by engineer. + + + Delete up to 20 messages at once. + No comment provided by engineer. + Delete user profile? Изтрий потребителския профил? No comment provided by engineer. + + Delete without notification + No comment provided by engineer. + + + Deleted + No comment provided by engineer. + Deleted at Изтрито на @@ -1885,6 +2462,14 @@ This cannot be undone! Изтрито на: %@ copied message info + + Deletion errors + No comment provided by engineer. + + + Delivered even when Apple drops them. + No comment provided by engineer. + Delivery Доставка @@ -1920,11 +2505,35 @@ This cannot be undone! Настолни устройства No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + + + Destination server error: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + + + Detailed statistics + No comment provided by engineer. + + + Details + No comment provided by engineer. + Develop Разработване No comment provided by engineer. + + Developer options + No comment provided by engineer. + Developer tools Инструменти за разработчици @@ -1955,8 +2564,12 @@ This cannot be undone! Лични съобщения chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + No comment provided by engineer. + + + Direct messages between members are prohibited. Личните съобщения между членовете са забранени в тази група. No comment provided by engineer. @@ -1970,11 +2583,23 @@ This cannot be undone! Деактивирай SimpleX заключване authentication reason + + Disable automatic message deletion? + alert title + + + Disable delete messages + alert button + Disable for all Деактивиране за всички No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Изчезващо съобщение @@ -1990,8 +2615,8 @@ This cannot be undone! Изчезващите съобщения са забранени в този чат. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. Изчезващите съобщения са забранени в тази група. No comment provided by engineer. @@ -2025,11 +2650,19 @@ This cannot be undone! Откриване през локалната мрежа No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. НЕ използвайте SimpleX за спешни повиквания. No comment provided by engineer. + + Do NOT use private routing. + No comment provided by engineer. + Do it later Отложи @@ -2040,6 +2673,14 @@ This cannot be undone! Не изпращай история на нови членове. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + + + Documents: + No comment provided by engineer. + Don't create address Не създавай адрес @@ -2050,18 +2691,37 @@ This cannot be undone! Не активирай No comment provided by engineer. + + Don't miss important messages. + No comment provided by engineer. + Don't show again Не показвай отново No comment provided by engineer. + + Done + No comment provided by engineer. + Downgrade and open chat Понижи версията и отвори чата No comment provided by engineer. + + Download + Изтегли + alert button +chat item action + + + Download errors + No comment provided by engineer. + Download failed + Неуспешно изтегляне No comment provided by engineer. @@ -2069,12 +2729,26 @@ This cannot be undone! Свали файл server test step + + Download files + alert action + + + Downloaded + No comment provided by engineer. + + + Downloaded files + No comment provided by engineer. + Downloading archive + Архива се изтегля No comment provided by engineer. Downloading link details + Подробности за линка се изтеглят No comment provided by engineer. @@ -2087,6 +2761,10 @@ This cannot be undone! Продължителност No comment provided by engineer. + + E2E encrypted notifications. + No comment provided by engineer. + Edit Редактирай @@ -2107,6 +2785,10 @@ This cannot be undone! Активиране (запазване на промените) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + No comment provided by engineer. + Enable SimpleX Lock Активирай SimpleX заключване @@ -2120,7 +2802,7 @@ This cannot be undone! Enable automatic message deletion? Активиране на автоматично изтриване на съобщения? - No comment provided by engineer. + alert title Enable camera access @@ -2134,6 +2816,7 @@ This cannot be undone! Enable in direct chats (BETA)! + Активиране в личните чатове (БЕТА)! No comment provided by engineer. @@ -2166,6 +2849,15 @@ This cannot be undone! Активирай kод за достъп за самоунищожение set passcode view + + Enabled + No comment provided by engineer. + + + Enabled for + Активирано за + No comment provided by engineer. + Encrypt Криптирай @@ -2236,6 +2928,10 @@ This cannot be undone! Неуспешно повторно договаряне на криптирането. No comment provided by engineer. + + Encryption renegotiation in progress. + No comment provided by engineer. + Enter Passcode Въведете kодa за достъп @@ -2253,6 +2949,7 @@ This cannot be undone! Enter passphrase + Въведи парола No comment provided by engineer. @@ -2300,30 +2997,33 @@ This cannot be undone! Грешка при отказване на промяна на адреса No comment provided by engineer. + + Error accepting conditions + alert title + Error accepting contact request Грешка при приемане на заявка за контакт No comment provided by engineer. - - Error accessing database file - Грешка при достъпа до файла с базата данни - No comment provided by engineer. - Error adding member(s) Грешка при добавяне на член(ове) No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + alert title Error changing address Грешка при промяна на адреса No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role Грешка при промяна на ролята @@ -2334,6 +3034,18 @@ This cannot be undone! Грешка при промяна на настройката No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + + + Error checking token status + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Грешка при създаване на адрес @@ -2349,6 +3061,10 @@ This cannot be undone! Грешка при създаване на групов линк No comment provided by engineer. + + Error creating list + alert title + Error creating member contact Грешка при създаване на контакт с член @@ -2364,6 +3080,10 @@ This cannot be undone! Грешка при създаване на профил! No comment provided by engineer. + + Error creating report + No comment provided by engineer. + Error decrypting file Грешка при декриптирането на файла @@ -2371,7 +3091,7 @@ This cannot be undone! Error deleting chat database - Грешка при изтриване на чат базата данни + Грешка при изтриване на базата данни No comment provided by engineer. @@ -2384,11 +3104,6 @@ This cannot be undone! Грешка при изтриване на връзката No comment provided by engineer. - - Error deleting contact - Грешка при изтриване на контакт - No comment provided by engineer. - Error deleting database Грешка при изтриване на базата данни @@ -2411,6 +3126,7 @@ This cannot be undone! Error downloading the archive + Грешка при изтеглянето на архива No comment provided by engineer. @@ -2430,12 +3146,16 @@ This cannot be undone! Error exporting chat database - Грешка при експортиране на чат базата данни + Грешка при експортиране на базата данни + No comment provided by engineer. + + + Error exporting theme: %@ No comment provided by engineer. Error importing chat database - Грешка при импортиране на чат базата данни + Грешка при импортиране на базата данни No comment provided by engineer. @@ -2443,9 +3163,12 @@ This cannot be undone! Грешка при присъединяване към група No comment provided by engineer. - - Error loading %@ servers - Грешка при зареждане на %@ сървъри + + Error loading servers + alert title + + + Error migrating settings No comment provided by engineer. @@ -2456,16 +3179,31 @@ This cannot be undone! Error receiving file Грешка при получаване на файл + alert title + + + Error reconnecting server No comment provided by engineer. + + Error reconnecting servers + No comment provided by engineer. + + + Error registering for notifications + alert title + Error removing member Грешка при отстраняване на член No comment provided by engineer. - - Error saving %@ servers - Грешка при запазване на %@ сървъра + + Error reordering lists + alert title + + + Error resetting statistics No comment provided by engineer. @@ -2473,6 +3211,10 @@ This cannot be undone! Грешка при запазване на ICE сървърите No comment provided by engineer. + + Error saving chat list + alert title + Error saving group profile Грешка при запазване на профила на групата @@ -2488,8 +3230,13 @@ This cannot be undone! Грешка при запазване на парола в Кeychain No comment provided by engineer. + + Error saving servers + alert title + Error saving settings + Грешка при запазване на настройките when migrating @@ -2532,16 +3279,24 @@ This cannot be undone! Грешка при спиране на чата No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! Грешка при смяна на профил! - No comment provided by engineer. + alertTitle Error synchronizing connection Грешка при синхронизиране на връзката No comment provided by engineer. + + Error testing server connection + No comment provided by engineer. + Error updating group link Грешка при актуализиране на груповия линк @@ -2552,6 +3307,10 @@ This cannot be undone! Грешка при актуализиране на съобщението No comment provided by engineer. + + Error updating server + alert title + Error updating settings Грешка при актуализиране на настройките @@ -2564,10 +3323,12 @@ This cannot be undone! Error uploading the archive + Грешка при качването на архива No comment provided by engineer. Error verifying passphrase: + Грешка при проверката на паролата: No comment provided by engineer. @@ -2578,7 +3339,9 @@ This cannot be undone! Error: %@ Грешка: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2590,6 +3353,14 @@ This cannot be undone! Грешка: няма файл с база данни No comment provided by engineer. + + Errors + No comment provided by engineer. + + + Errors in servers configuration. + servers error + Even when disabled in the conversation. Дори когато е деактивиран в разговора. @@ -2605,6 +3376,10 @@ This cannot be undone! Разшири chat item action + + Expired + token status text + Export database Експортирай база данни @@ -2615,6 +3390,10 @@ This cannot be undone! Грешка при експортиране: No comment provided by engineer. + + Export theme + No comment provided by engineer. + Exported database archive. Експортиран архив на базата данни. @@ -2622,6 +3401,7 @@ This cannot be undone! Exported file doesn't exist + Експортираният файл не съществува No comment provided by engineer. @@ -2639,16 +3419,58 @@ This cannot be undone! Бързо и без чакане, докато подателят е онлайн! No comment provided by engineer. + + Faster deletion of groups. + No comment provided by engineer. + Faster joining and more reliable messages. По-бързо присъединяване и по-надеждни съобщения. No comment provided by engineer. + + Faster sending messages. + No comment provided by engineer. + Favorite Любим + swipe action + + + Favorites No comment provided by engineer. + + File error + file error alert title + + + File errors: +%@ + alert message + + + File is blocked by server operator: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + file error text + + + File server error: %@ + file error text + + + File status + No comment provided by engineer. + + + File status: %@ + copied message info + File will be deleted from servers. Файлът ще бъде изтрит от сървърите. @@ -2669,6 +3491,10 @@ This cannot be undone! Файл: %@ No comment provided by engineer. + + Files + No comment provided by engineer. + Files & media Файлове и медия @@ -2679,11 +3505,16 @@ This cannot be undone! Файлове и медия chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. Файловете и медията са забранени в тази група. No comment provided by engineer. + + Files and media not allowed + Файлове и медия не са разрешени + No comment provided by engineer. + Files and media prohibited! Файловете и медията са забранени! @@ -2696,10 +3527,12 @@ This cannot be undone! Finalize migration + Завърши миграцията No comment provided by engineer. Finalize migration on another device. + Завършете миграцията на другото устройство. No comment provided by engineer. @@ -2742,11 +3575,97 @@ This cannot be undone! Поправката не се поддържа от члена на групата No comment provided by engineer. + + For all moderators + No comment provided by engineer. + + + For chat profile %@: + servers error + For console За конзолата No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + No comment provided by engineer. + + + For me + No comment provided by engineer. + + + For private routing + No comment provided by engineer. + + + For social media + No comment provided by engineer. + + + Forward + Препрати + chat item action + + + Forward %d message(s)? + alert title + + + Forward and save messages + Препращане и запазване на съобщения + No comment provided by engineer. + + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + + + Forwarded + Препратено + No comment provided by engineer. + + + Forwarded from + Препратено от + No comment provided by engineer. + + + Forwarding %lld messages + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + snd error text + Found desktop Намерено настолно устройство @@ -2767,11 +3686,6 @@ This cannot be undone! Пълно име (незадължително) No comment provided by engineer. - - Full name: - Пълно име: - No comment provided by engineer. - Fully decentralized – visible only to members. Напълно децентрализирана – видима е само за членовете. @@ -2792,6 +3706,18 @@ This cannot be undone! GIF файлове и стикери No comment provided by engineer. + + Get notified when mentioned. + No comment provided by engineer. + + + Good afternoon! + message preview + + + Good morning! + message preview + Group Група @@ -2847,36 +3773,6 @@ This cannot be undone! Групови линкове No comment provided by engineer. - - Group members can add message reactions. - Членовете на групата могат да добавят реакции към съобщенията. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Членовете на групата могат необратимо да изтриват изпратените съобщения. (24 часа) - No comment provided by engineer. - - - Group members can send direct messages. - Членовете на групата могат да изпращат лични съобщения. - No comment provided by engineer. - - - Group members can send disappearing messages. - Членовете на групата могат да изпращат изчезващи съобщения. - No comment provided by engineer. - - - Group members can send files and media. - Членовете на групата могат да изпращат файлове и медия. - No comment provided by engineer. - - - Group members can send voice messages. - Членовете на групата могат да изпращат гласови съобщения. - No comment provided by engineer. - Group message: Групово съобщение: @@ -2917,11 +3813,19 @@ This cannot be undone! Групата ще бъде изтрита за вас - това не може да бъде отменено! No comment provided by engineer. + + Groups + No comment provided by engineer. + Help Помощ No comment provided by engineer. + + Help admins moderating their groups. + No comment provided by engineer. + Hidden Скрит @@ -2972,10 +3876,17 @@ This cannot be undone! Как работи SimpleX No comment provided by engineer. + + How it affects privacy + No comment provided by engineer. + + + How it helps privacy + No comment provided by engineer. + How it works - Как работи - No comment provided by engineer. + alert button How to @@ -2994,6 +3905,7 @@ This cannot be undone! Hungarian interface + Унгарски интерфейс No comment provided by engineer. @@ -3001,6 +3913,10 @@ This cannot be undone! ICE сървъри (по един на ред) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Ако не можете да се срещнете лично, покажете QR код във видеоразговора или споделете линка. @@ -3041,8 +3957,8 @@ This cannot be undone! Веднага No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Защитен от спам и злоупотреби No comment provided by engineer. @@ -3053,7 +3969,7 @@ This cannot be undone! Import chat database? - Импортиране на чат база данни? + Импортиране на база данни? No comment provided by engineer. @@ -3063,10 +3979,21 @@ This cannot be undone! Import failed + Неуспешно импортиране + No comment provided by engineer. + + + Import theme No comment provided by engineer. Importing archive + Импортиране на архив + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! No comment provided by engineer. @@ -3086,6 +4013,7 @@ This cannot be undone! In order to continue, chat should be stopped. + За да продължите, чатът трябва да бъде спрян. No comment provided by engineer. @@ -3093,6 +4021,19 @@ This cannot be undone! В отговор на No comment provided by engineer. + + In-call sounds + Звуци по време на разговор + No comment provided by engineer. + + + Inappropriate content + report reason + + + Inappropriate profile + report reason + Incognito Инкогнито @@ -3163,6 +4104,11 @@ This cannot be undone! Инсталирайте [SimpleX Chat за терминал](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Мигновено + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3170,16 +4116,35 @@ This cannot be undone! No comment provided by engineer. - - Instantly - Мигновено - No comment provided by engineer. - Interface Интерфейс No comment provided by engineer. + + Interface colors + No comment provided by engineer. + + + Invalid + token status text + + + Invalid (bad token) + token status text + + + Invalid (expired) + token status text + + + Invalid (unregistered) + token status text + + + Invalid (wrong topic) + token status text + Invalid QR code Невалиден QR код @@ -3202,6 +4167,7 @@ This cannot be undone! Invalid migration confirmation + Невалидно потвърждение за мигриране No comment provided by engineer. @@ -3217,7 +4183,7 @@ This cannot be undone! Invalid server address! Невалиден адрес на сървъра! - No comment provided by engineer. + alert title Invalid status @@ -3239,6 +4205,10 @@ This cannot be undone! Покани членове No comment provided by engineer. + + Invite to chat + No comment provided by engineer. + Invite to group Покани в групата @@ -3254,8 +4224,8 @@ This cannot be undone! Необратимото изтриване на съобщения е забранено в този чат. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. Необратимото изтриване на съобщения е забранено в тази група. No comment provided by engineer. @@ -3280,6 +4250,10 @@ This cannot be undone! 3. Връзката е била компрометирана. No comment provided by engineer. + + It protects your IP address and connections. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Изглежда, че вече сте свързани чрез този линк. Ако не е така, има грешка (%@). @@ -3298,7 +4272,7 @@ This cannot be undone! Join Присъединяване - No comment provided by engineer. + swipe action Join group @@ -3340,6 +4314,10 @@ This is your link for group %@! Keep Запази + alert action + + + Keep conversation No comment provided by engineer. @@ -3350,7 +4328,7 @@ This is your link for group %@! Keep unused invitation? Запази неизползваната покана за връзка? - No comment provided by engineer. + alert title Keep your connections @@ -3385,6 +4363,14 @@ This is your link for group %@! Leave Напусни + swipe action + + + Leave chat + No comment provided by engineer. + + + Leave chat? No comment provided by engineer. @@ -3427,6 +4413,18 @@ This is your link for group %@! Запомнени настолни устройства No comment provided by engineer. + + List + swipe action + + + List name and emoji should be different for all lists. + No comment provided by engineer. + + + List name... + No comment provided by engineer. + Live message! Съобщение на живо! @@ -3437,11 +4435,6 @@ This is your link for group %@! Съобщения на живо No comment provided by engineer. - - Local - Локално - No comment provided by engineer. - Local name Локално име @@ -3462,11 +4455,6 @@ This is your link for group %@! Режим на заключване No comment provided by engineer. - - Make a private connection - Добави поверителна връзка - No comment provided by engineer. - Make one message disappear Накарайте едно съобщение да изчезне @@ -3477,21 +4465,11 @@ This is your link for group %@! Направи профила поверителен! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Уверете се, че %@ сървърните адреси са в правилен формат, разделени на редове и не се дублират (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Уверете се, че адресите на WebRTC ICE сървъра са в правилен формат, разделени на редове и не са дублирани. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Много хора попитаха: *ако SimpleX няма потребителски идентификатори, как може да доставя съобщения?* - No comment provided by engineer. - Mark deleted for everyone Маркирай като изтрито за всички @@ -3517,11 +4495,31 @@ This is your link for group %@! Макс. 30 секунди, получено незабавно. No comment provided by engineer. + + Media & file servers + No comment provided by engineer. + + + Medium + blur media + Member Член No comment provided by engineer. + + Member inactive + item status text + + + Member reports + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. Ролята на члена ще бъде променена на "%@". Всички членове на групата ще бъдат уведомени. @@ -3532,11 +4530,62 @@ This is your link for group %@! Ролята на члена ще бъде променена на "%@". Членът ще получи нова покана. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! Членът ще бъде премахнат от групата - това не може да бъде отменено! No comment provided by engineer. + + Members can add message reactions. + Членовете на групата могат да добавят реакции към съобщенията. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + Членовете на групата могат необратимо да изтриват изпратените съобщения. (24 часа) + No comment provided by engineer. + + + Members can report messsages to moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + Членовете на групата могат да изпращат SimpleX линкове. + No comment provided by engineer. + + + Members can send direct messages. + Членовете на групата могат да изпращат лични съобщения. + No comment provided by engineer. + + + Members can send disappearing messages. + Членовете на групата могат да изпращат изчезващи съобщения. + No comment provided by engineer. + + + Members can send files and media. + Членовете на групата могат да изпращат файлове и медия. + No comment provided by engineer. + + + Members can send voice messages. + Членовете на групата могат да изпращат гласови съобщения. + No comment provided by engineer. + + + Mention members 👋 + No comment provided by engineer. + + + Menus + No comment provided by engineer. + Message delivery error Грешка при доставката на съобщението @@ -3547,11 +4596,27 @@ This is your link for group %@! Потвърждениe за доставка на съобщения! No comment provided by engineer. + + Message delivery warning + item status text + Message draft Чернова на съобщение No comment provided by engineer. + + Message forwarded + item status text + + + Message may be delivered later if member becomes active. + item status description + + + Message queue info + No comment provided by engineer. + Message reactions Реакции на съобщения @@ -3562,11 +4627,36 @@ This is your link for group %@! Реакциите на съобщения са забранени в този чат. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. Реакциите на съобщения са забранени в тази група. No comment provided by engineer. + + Message reception + No comment provided by engineer. + + + Message servers + No comment provided by engineer. + + + Message shape + No comment provided by engineer. + + + Message source remains private. + Източникът на съобщението остава скрит. + No comment provided by engineer. + + + Message status + No comment provided by engineer. + + + Message status: %@ + copied message info + Message text Текст на съобщението @@ -3574,6 +4664,7 @@ This is your link for group %@! Message too large + Съобщението е твърде голямо No comment provided by engineer. @@ -3591,36 +4682,60 @@ This is your link for group %@! Съобщенията от %@ ще бъдат показани! No comment provided by engineer. + + Messages in this chat will never be deleted. + alert message + + + Messages received + No comment provided by engineer. + + + Messages sent + No comment provided by engineer. + + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + Съобщенията, файловете и разговорите са защитени чрез **криптиране от край до край** с перфектна секретност при препращане, правдоподобно опровержение и възстановяване при взлом. No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + Съобщенията, файловете и разговорите са защитени чрез **квантово устойчиво e2e криптиране** с перфектна секретност при препращане, правдоподобно опровержение и възстановяване при взлом. No comment provided by engineer. Migrate device + Мигрирай устройството No comment provided by engineer. Migrate from another device + Мигриране от друго устройство No comment provided by engineer. Migrate here + Мигрирай тук No comment provided by engineer. Migrate to another device + Миграция към друго устройство No comment provided by engineer. Migrate to another device via QR code. + Мигрирайте към друго устройство чрез QR код. No comment provided by engineer. Migrating + Мигриране No comment provided by engineer. @@ -3630,6 +4745,7 @@ This is your link for group %@! Migration complete + Миграцията е завършена No comment provided by engineer. @@ -3647,9 +4763,9 @@ This is your link for group %@! Миграцията е завършена No comment provided by engineer. - - Migrations: %@ - Миграции: %@ + + Migrations: + Миграции: No comment provided by engineer. @@ -3667,21 +4783,29 @@ This is your link for group %@! Модерирано в: %@ copied message info + + More + swipe action + More improvements are coming soon! Очаквайте скоро още подобрения! No comment provided by engineer. + + More reliable network connection. + По-надеждна мрежова връзка. + No comment provided by engineer. + + + More reliable notifications + No comment provided by engineer. + Most likely this connection is deleted. Най-вероятно тази връзка е изтрита. item status description - - Most likely this contact has deleted the connection with you. - Най-вероятно този контакт е изтрил връзката с вас. - No comment provided by engineer. - Multiple chat profiles Множество профили за чат @@ -3690,7 +4814,11 @@ This is your link for group %@! Mute Без звук - No comment provided by engineer. + notification label action + + + Mute all + notification label action Muted when inactive! @@ -3700,13 +4828,35 @@ This is your link for group %@! Name Име - No comment provided by engineer. + swipe action Network & servers Мрежа и сървъри No comment provided by engineer. + + Network connection + Мрежова връзка + No comment provided by engineer. + + + Network decentralization + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + snd error text + + + Network management + Управление на мрежата + No comment provided by engineer. + + + Network operator + No comment provided by engineer. + Network settings Мрежови настройки @@ -3717,16 +4867,32 @@ This is your link for group %@! Състояние на мрежата No comment provided by engineer. + + New + token status text + New Passcode Нов kод за достъп No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat Нов чат No comment provided by engineer. + + New chat experience 🎉 + No comment provided by engineer. + New contact request Нова заявка за контакт @@ -3737,11 +4903,6 @@ This is your link for group %@! Нов контакт: notification - - New database archive - Нов архив на база данни - No comment provided by engineer. - New desktop app! Ново настолно приложение! @@ -3752,11 +4913,19 @@ This is your link for group %@! Ново име No comment provided by engineer. + + New events + notification + New in %@ Ново в %@ No comment provided by engineer. + + New media options + No comment provided by engineer. + New member role Нова членска роля @@ -3772,6 +4941,10 @@ This is your link for group %@! Нова парола… No comment provided by engineer. + + New server + No comment provided by engineer. + No Не @@ -3782,6 +4955,18 @@ This is your link for group %@! Приложението няма kод за достъп Authentication unavailable + + No chats + No comment provided by engineer. + + + No chats found + No comment provided by engineer. + + + No chats in list %@ + No comment provided by engineer. + No contacts selected Няма избрани контакти @@ -3802,6 +4987,10 @@ This is your link for group %@! Няма токен за устройство! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + item status description + No filtered chats Няма филтрирани чатове @@ -3817,21 +5006,96 @@ This is your link for group %@! Няма история No comment provided by engineer. + + No info, try to reload + No comment provided by engineer. + + + No media & file servers. + servers error + + + No message + No comment provided by engineer. + + + No message servers. + servers error + + + No network connection + Няма мрежова връзка + No comment provided by engineer. + + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message Няма разрешение за запис на гласово съобщение No comment provided by engineer. + + No push server + Локално + No comment provided by engineer. + No received or sent files Няма получени или изпратени файлове No comment provided by engineer. + + No servers for private message routing. + servers error + + + No servers to receive files. + servers error + + + No servers to receive messages. + servers error + + + No servers to send files. + servers error + + + No token! + alert title + + + No unread chats + No comment provided by engineer. + + + No user identifiers. + Първата платформа без никакви потребителски идентификатори – поверителна по дизайн. + No comment provided by engineer. + Not compatible! Несъвместим! No comment provided by engineer. + + Notes + No comment provided by engineer. + + + Nothing selected + No comment provided by engineer. + + + Nothing to forward! + alert title + Notifications Известия @@ -3842,6 +5106,18 @@ This is your link for group %@! Известията са деактивирани! No comment provided by engineer. + + Notifications error + alert title + + + Notifications privacy + No comment provided by engineer. + + + Notifications status + alert title + Now admins can: - delete members' messages. @@ -3859,36 +5135,35 @@ This is your link for group %@! Off Изключено - No comment provided by engineer. + blur media Ok Ок - No comment provided by engineer. + alert button Old database Стара база данни No comment provided by engineer. - - Old database archive - Стар архив на база данни - No comment provided by engineer. - One-time invitation link Линк за еднократна покана No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - За свързване ще са необходими Onion хостове. Изисква се активиране на VPN. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + За свързване ще са **необходими** Onion хостове. +Изисква се активиране на VPN. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Ще се използват Onion хостове, когато са налични. Изисква се активиране на VPN. + + Onion hosts will be used when available. +Requires compatible VPN. + Ще се използват Onion хостове, когато са налични. +Изисква се активиране на VPN. No comment provided by engineer. @@ -3896,11 +5171,19 @@ This is your link for group %@! Няма се използват Onion хостове. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. Само потребителските устройства съхраняват потребителски профили, контакти, групи и съобщения, изпратени с **двуслойно криптиране от край до край**. No comment provided by engineer. + + Only delete conversation + No comment provided by engineer. + Only group owners can change group preferences. Само собствениците на групата могат да променят груповите настройки. @@ -3916,6 +5199,14 @@ This is your link for group %@! Само собствениците на групата могат да активират гласови съобщения. No comment provided by engineer. + + Only sender and moderators see it + No comment provided by engineer. + + + Only you and moderators see it + No comment provided by engineer. + Only you can add message reactions. Само вие можете да добавяте реакции на съобщенията. @@ -3969,13 +5260,17 @@ This is your link for group %@! Open Отвори - No comment provided by engineer. + alert action Open Settings Отвори настройки No comment provided by engineer. + + Open changes + No comment provided by engineer. + Open chat Отвори чат @@ -3986,32 +5281,44 @@ This is your link for group %@! Отвори конзолата authentication reason + + Open conditions + No comment provided by engineer. + Open group Отвори група No comment provided by engineer. + + Open link? + alert title + Open migration to another device + Отвори миграцията към друго устройство authentication reason - - Open user profiles - Отвори потребителските профили - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Протокол и код с отворен код – всеки може да оперира собствени сървъри. - No comment provided by engineer. - Opening app… Приложението се отваря… No comment provided by engineer. + + Operator + No comment provided by engineer. + + + Operator server + alert title + + + Or import archive file + No comment provided by engineer. + Or paste archive link + Или постави архивен линк No comment provided by engineer. @@ -4021,6 +5328,7 @@ This is your link for group %@! Or securely share this file link + Или сигурно споделете този линк към файла No comment provided by engineer. @@ -4028,6 +5336,24 @@ This is your link for group %@! Или покажи този код No comment provided by engineer. + + Or to share privately + No comment provided by engineer. + + + Organize chats into lists + No comment provided by engineer. + + + Other + Други + No comment provided by engineer. + + + Other file errors: +%@ + alert message + PING count PING бройка @@ -4063,6 +5389,10 @@ This is your link for group %@! Кодът за достъп е зададен! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show Парола за показване @@ -4093,13 +5423,12 @@ This is your link for group %@! Постави получения линк No comment provided by engineer. - - People can connect to you only via the links you share. - Хората могат да се свържат с вас само чрез ликовете, които споделяте. + + Pending No comment provided by engineer. - - Periodically + + Periodic Периодично No comment provided by engineer. @@ -4110,6 +5439,15 @@ This is your link for group %@! Picture-in-picture calls + Обаждания "картина в картина" + No comment provided by engineer. + + + Play from the chat list. + No comment provided by engineer. + + + Please ask your contact to enable calls. No comment provided by engineer. @@ -4117,6 +5455,11 @@ This is your link for group %@! Моля, попитайте вашия контакт, за да активирате изпращане на гласови съобщения. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Моля, проверете дали сте използвали правилния линк или поискайте вашия контакт, за да ви изпрати друг. @@ -4134,6 +5477,7 @@ This is your link for group %@! Please confirm that network settings are correct for this device. + Моля, потвърдете, че мрежовите настройки са правилни за това устройство. No comment provided by engineer. @@ -4183,60 +5527,107 @@ Error: %@ Моля, съхранявайте паролата на сигурно място, НЯМА да можете да я промените, ако я загубите. No comment provided by engineer. + + Please try to disable and re-enable notfications. + token info + + + Please wait for token activation to complete. + token info + + + Please wait for token to be registered. + token info + Polish interface Полски интерфейс No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Въжможно е пръстовият отпечатък на сертификата в адреса на сървъра да е неправилен server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Запазете последната чернова на съобщението с прикачени файлове. No comment provided by engineer. - - Preset server - Предварително зададен сървър - No comment provided by engineer. - Preset server address Предварително зададен адрес на сървъра No comment provided by engineer. + + Preset servers + No comment provided by engineer. + Preview Визуализация No comment provided by engineer. + + Previously connected servers + No comment provided by engineer. + Privacy & security Поверителност и сигурност No comment provided by engineer. + + Privacy for your customers. + No comment provided by engineer. + + + Privacy policy and conditions of use. + No comment provided by engineer. + Privacy redefined Поверителността преосмислена No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + No comment provided by engineer. + Private filenames Поверителни имена на файлове No comment provided by engineer. + + Private media file names. + No comment provided by engineer. + + + Private message routing + No comment provided by engineer. + + + Private message routing 🚀 + No comment provided by engineer. + Private notes Лични бележки name of notes to self + + Private routing + No comment provided by engineer. + + + Private routing error + No comment provided by engineer. + Profile and server connections Профилни и сървърни връзки @@ -4247,14 +5638,9 @@ Error: %@ Профилно изображение No comment provided by engineer. - - Profile name - Име на профила - No comment provided by engineer. - - - Profile name: - Име на профила: + + Profile images + Профилни изображения No comment provided by engineer. @@ -4262,10 +5648,14 @@ Error: %@ Профилна парола No comment provided by engineer. + + Profile theme + No comment provided by engineer. + Profile update will be sent to your contacts. Актуализацията на профила ще бъде изпратена до вашите контакти. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4287,6 +5677,15 @@ Error: %@ Забрани реакциите на съобщенията. No comment provided by engineer. + + Prohibit reporting messages to moderators. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + Забранете изпращането на SimpleX линкове. + No comment provided by engineer. + Prohibit sending direct messages to members. Забрани изпращането на лични съобщения до членовете. @@ -4307,11 +5706,20 @@ Error: %@ Забрани изпращането на гласови съобщения. No comment provided by engineer. + + Protect IP address + No comment provided by engineer. + Protect app screen Защити екрана на приложението No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + No comment provided by engineer. + Protect your chat profiles with a password! Защитете чат профилите с парола! @@ -4327,6 +5735,18 @@ Error: %@ Време за изчакване на протокола за KB No comment provided by engineer. + + Proxied + No comment provided by engineer. + + + Proxied servers + No comment provided by engineer. + + + Proxy requires password + No comment provided by engineer. + Push notifications Push известия @@ -4334,10 +5754,12 @@ Error: %@ Push server + Push сървър No comment provided by engineer. Quantum resistant encryption + Квантово устойчиво криптиране No comment provided by engineer. @@ -4345,6 +5767,10 @@ Error: %@ Оценете приложението No comment provided by engineer. + + Reachable chat toolbar + No comment provided by engineer. + React… Реагирай… @@ -4353,33 +5779,28 @@ Error: %@ Read Прочетено - No comment provided by engineer. + swipe action Read more Прочетете още No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Прочетете повече в [Ръководство за потребителя](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). Прочетете повече в [Ръководство за потребителя](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Прочетете повече в [Ръководство за потребителя](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). Прочетете повече в [Ръководство на потребителя](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - - Read more in our GitHub repository. - Прочетете повече в нашето хранилище в GitHub. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). Прочетете повече в нашето [GitHub хранилище](https://github.com/simplex-chat/simplex-chat#readme). @@ -4390,6 +5811,10 @@ Error: %@ Потвърждениeто за доставка е деактивирано No comment provided by engineer. + + Receive errors + No comment provided by engineer. + Received at Получено в @@ -4410,6 +5835,18 @@ Error: %@ Получено съобщение message info title + + Received messages + No comment provided by engineer. + + + Received reply + No comment provided by engineer. + + + Received total + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. Получаващият адрес ще бъде променен към друг сървър. Промяната на адреса ще завърши, след като подателят е онлайн. @@ -4430,16 +5867,41 @@ Error: %@ Скорошна история и подобрен [bot за директория за групи](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPd jdLW3%23%2F%3Fv%3D1-2% 26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + Получателят(ите) не могат да видят от кого е това съобщение. + No comment provided by engineer. + Recipients see updates as you type them. Получателите виждат актуализации, докато ги въвеждате. No comment provided by engineer. + + Reconnect + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Повторно се свържете с всички свързани сървъри, за да принудите доставката на съобщенията. Използва се допълнителен трафик. No comment provided by engineer. + + Reconnect all servers + No comment provided by engineer. + + + Reconnect all servers? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + No comment provided by engineer. + + + Reconnect server? + No comment provided by engineer. + Reconnect servers? Повторно свърване със сървърите? @@ -4460,10 +5922,23 @@ Error: %@ Намалена консумация на батерията No comment provided by engineer. + + Register + No comment provided by engineer. + + + Register notification token? + token info + + + Registered + token status text + Reject Отхвърляне - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4490,6 +5965,14 @@ Error: %@ Премахване No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + + + Remove image + No comment provided by engineer. + Remove member Острани член @@ -4527,10 +6010,12 @@ Error: %@ Repeat download + Повтори изтеглянето No comment provided by engineer. Repeat import + Повтори импортирането No comment provided by engineer. @@ -4540,6 +6025,7 @@ Error: %@ Repeat upload + Повтори качването No comment provided by engineer. @@ -4547,6 +6033,46 @@ Error: %@ Отговори chat item action + + Report + chat item action + + + Report content: only group moderators will see it. + report reason + + + Report member profile: only group moderators will see it. + report reason + + + Report other: only group moderators will see it. + report reason + + + Report reason? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + report reason + + + Report violation: only group moderators will see it. + report reason + + + Report: %@ + report in notification + + + Reporting messages to moderators is prohibited. + No comment provided by engineer. + + + Reports + No comment provided by engineer. + Required Задължително @@ -4557,16 +6083,36 @@ Error: %@ Нулиране No comment provided by engineer. + + Reset all hints + No comment provided by engineer. + + + Reset all statistics + No comment provided by engineer. + + + Reset all statistics? + No comment provided by engineer. + Reset colors Нулирай цветовете No comment provided by engineer. + + Reset to app theme + No comment provided by engineer. + Reset to defaults Възстановяване на настройките по подразбиране No comment provided by engineer. + + Reset to user theme + No comment provided by engineer. + Restart the app to create a new chat profile Рестартирайте приложението, за да създадете нов чат профил @@ -4607,9 +6153,8 @@ Error: %@ Покажи chat item action - - Revert - Отмени промените + + Review conditions No comment provided by engineer. @@ -4637,55 +6182,62 @@ Error: %@ Стартиране на чат No comment provided by engineer. - - SMP servers - SMP сървъри + + SMP server + No comment provided by engineer. + + + SOCKS proxy + No comment provided by engineer. + + + Safely receive files No comment provided by engineer. Safer groups + По-безопасни групи No comment provided by engineer. Save Запази - chat item action + alert button +chat item action Save (and notify contacts) Запази (и уведоми контактите) - No comment provided by engineer. + alert button Save and notify contact Запази и уведоми контакта - No comment provided by engineer. + alert button Save and notify group members Запази и уведоми членовете на групата No comment provided by engineer. + + Save and reconnect + No comment provided by engineer. + Save and update group profile Запази и актуализирай профила на групата No comment provided by engineer. - - Save archive - Запази архив - No comment provided by engineer. - - - Save auto-accept settings - Запази настройките за автоматично приемане - No comment provided by engineer. - Save group profile Запази профила на групата No comment provided by engineer. + + Save list + No comment provided by engineer. + Save passphrase and open chat Запази паролата и отвори чата @@ -4699,7 +6251,7 @@ Error: %@ Save preferences? Запази настройките? - No comment provided by engineer. + alert title Save profile password @@ -4714,28 +6266,49 @@ Error: %@ Save servers? Запази сървърите? - No comment provided by engineer. - - - Save settings? - Запази настройките? - No comment provided by engineer. + alert title Save welcome message? Запази съобщението при посрещане? No comment provided by engineer. + + Save your profile? + alert title + + + Saved + Запазено + No comment provided by engineer. + Saved WebRTC ICE servers will be removed Запазените WebRTC ICE сървъри ще бъдат премахнати No comment provided by engineer. + + Saved from + Запазено от + No comment provided by engineer. + Saved message Запазено съобщение message info title + + Saving %lld messages + No comment provided by engineer. + + + Scale + No comment provided by engineer. + + + Scan / Paste link + No comment provided by engineer. + Scan QR code Сканирай QR код @@ -4776,11 +6349,19 @@ Error: %@ Търсене или поставяне на SimpleX линк No comment provided by engineer. + + Secondary + No comment provided by engineer. + Secure queue Сигурна опашка server test step + + Secured + No comment provided by engineer. + Security assessment Оценка на сигурността @@ -4794,6 +6375,18 @@ Error: %@ Select Избери + chat item action + + + Select chat profile + No comment provided by engineer. + + + Selected %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. No comment provided by engineer. @@ -4831,11 +6424,6 @@ Error: %@ Изпращайте потвърждениe за доставка на No comment provided by engineer. - - Send direct message - Изпрати лично съобщение - No comment provided by engineer. - Send direct message to connect Изпрати лично съобщение за свързване @@ -4846,6 +6434,10 @@ Error: %@ Изпрати изчезващо съобщение No comment provided by engineer. + + Send errors + No comment provided by engineer. + Send link previews Изпрати визуализация на линковете @@ -4856,14 +6448,25 @@ Error: %@ Изпрати съобщение на живо No comment provided by engineer. + + Send message to enable calls. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + No comment provided by engineer. + Send notifications Изпращай известия No comment provided by engineer. - - Send notifications: - Изпратени известия: + + Send private reports No comment provided by engineer. @@ -4889,7 +6492,7 @@ Error: %@ Sender cancelled file transfer. Подателят отмени прехвърлянето на файла. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4946,6 +6549,10 @@ Error: %@ Изпратено на: %@ copied message info + + Sent directly + No comment provided by engineer. + Sent file event Събитие за изпратен файл @@ -4956,11 +6563,59 @@ Error: %@ Изпратено съобщение message info title + + Sent messages + No comment provided by engineer. + Sent messages will be deleted after set time. Изпратените съобщения ще бъдат изтрити след зададеното време. No comment provided by engineer. + + Sent reply + No comment provided by engineer. + + + Sent total + No comment provided by engineer. + + + Sent via proxy + No comment provided by engineer. + + + Server + No comment provided by engineer. + + + Server added to operator %@. + alert message + + + Server address + No comment provided by engineer. + + + Server address is incompatible with network settings. + srv error text. + + + Server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Server operator changed. + alert title + + + Server operators + No comment provided by engineer. + + + Server protocol changed. + alert title + Server requires authorization to create queues, check password Сървърът изисква оторизация за създаване на опашки, проверете паролата @@ -4976,11 +6631,31 @@ Error: %@ Тестът на сървъра е неуспешен! No comment provided by engineer. + + Server type + No comment provided by engineer. + + + Server version is incompatible with network settings. + srv error text + + + Server version is incompatible with your app: %@. + No comment provided by engineer. + Servers Сървъри No comment provided by engineer. + + Servers info + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + No comment provided by engineer. + Session code Код на сесията @@ -4991,11 +6666,19 @@ Error: %@ Задай 1 ден No comment provided by engineer. + + Set chat name… + No comment provided by engineer. + Set contact name… Задай име на контакт… No comment provided by engineer. + + Set default theme + No comment provided by engineer. + Set group preferences Задай групови настройки @@ -5006,6 +6689,10 @@ Error: %@ Задайте го вместо системната идентификация. No comment provided by engineer. + + Set message expiration in chats. + No comment provided by engineer. + Set passcode Задай kод за достъп @@ -5013,6 +6700,7 @@ Error: %@ Set passphrase + Задаване на парола No comment provided by engineer. @@ -5035,24 +6723,50 @@ Error: %@ Настройки No comment provided by engineer. + + Settings were changed. + alert message + + + Shape profile images + Променете формата на профилните изображения + No comment provided by engineer. + Share Сподели - chat item action + alert action +chat item action Share 1-time link Сподели еднократен линк No comment provided by engineer. + + Share 1-time link with a friend + No comment provided by engineer. + + + Share SimpleX address on social media. + No comment provided by engineer. + Share address Сподели адрес No comment provided by engineer. + + Share address publicly + No comment provided by engineer. + Share address with contacts? Сподели адреса с контактите? + alert title + + + Share from other apps. No comment provided by engineer. @@ -5060,18 +6774,31 @@ Error: %@ Сподели линк No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link Сподели този еднократен линк за връзка No comment provided by engineer. + + Share to SimpleX + No comment provided by engineer. + Share with contacts Сподели с контактите No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code + Покажи QR код No comment provided by engineer. @@ -5089,21 +6816,41 @@ Error: %@ Показване на последните съобщения в листа с чатовете No comment provided by engineer. + + Show message status + No comment provided by engineer. + + + Show percentage + No comment provided by engineer. + Show preview Показване на визуализация No comment provided by engineer. + + Show → on messages sent via private routing. + No comment provided by engineer. + Show: Покажи: No comment provided by engineer. + + SimpleX + No comment provided by engineer. + SimpleX Address SimpleX Адрес No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. Сигурността на SimpleX Chat беше одитирана от Trail of Bits. @@ -5134,6 +6881,18 @@ Error: %@ SimpleX адрес No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + No comment provided by engineer. + + + SimpleX address or 1-time link? + No comment provided by engineer. + + + SimpleX channel link + simplex link type + SimpleX contact address SimpleX адрес за контакт @@ -5152,6 +6911,16 @@ Error: %@ SimpleX links SimpleX линкове + chat feature + + + SimpleX links are prohibited. + SimpleX линкове са забранени в тази група. + No comment provided by engineer. + + + SimpleX links not allowed + SimpleX линковете не са разрешени No comment provided by engineer. @@ -5159,11 +6928,19 @@ Error: %@ Еднократна покана за SimpleX simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Опростен режим инкогнито No comment provided by engineer. + + Size + No comment provided by engineer. + Skip Пропускане @@ -5179,16 +6956,47 @@ Error: %@ Малки групи (максимум 20) No comment provided by engineer. + + Soft + blur media + + + Some app settings were not migrated. + No comment provided by engineer. + + + Some file(s) were not exported: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. Някои не-фатални грешки са възникнали по време на импортиране - може да видите конзолата за повече подробности. No comment provided by engineer. + + Some non-fatal errors occurred during import: + No comment provided by engineer. + + + Some servers failed the test: +%@ + alert message + Somebody Някой notification title + + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + Квадрат, кръг или нещо между тях. + No comment provided by engineer. + Start chat Започни чат @@ -5204,6 +7012,14 @@ Error: %@ Започни миграция No comment provided by engineer. + + Starting from %@. + No comment provided by engineer. + + + Statistics + No comment provided by engineer. + Stop Спри @@ -5216,11 +7032,7 @@ Error: %@ Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - Спрете чата, за да активирате действията с базата данни + Спри чата No comment provided by engineer. @@ -5251,27 +7063,56 @@ Error: %@ Stop sharing Спри споделянето - No comment provided by engineer. + alert action Stop sharing address? Спри споделянето на адреса? - No comment provided by engineer. + alert title Stopping chat + Спиране на чата No comment provided by engineer. + + Storage + No comment provided by engineer. + + + Strong + blur media + Submit Изпрати No comment provided by engineer. + + Subscribed + No comment provided by engineer. + + + Subscription errors + No comment provided by engineer. + + + Subscriptions ignored + No comment provided by engineer. + Support SimpleX Chat Подкрепете SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System Системен @@ -5282,11 +7123,19 @@ Error: %@ Системна идентификация No comment provided by engineer. + + TCP connection + No comment provided by engineer. + TCP connection timeout Времето на изчакване за установяване на TCP връзка No comment provided by engineer. + + TCP port for messaging + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5302,11 +7151,19 @@ Error: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture Направи снимка No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + No comment provided by engineer. + Tap button Докосни бутона @@ -5342,16 +7199,19 @@ Error: %@ Докосни за сканиране No comment provided by engineer. - - Tap to start a new chat - Докосни за започване на нов чат - No comment provided by engineer. + + Temporary file error + file error alert title Test failed at step %@. Тестът е неуспешен на стъпка %@. server test failure + + Test notifications + No comment provided by engineer. + Test server Тествай сървър @@ -5365,7 +7225,7 @@ Error: %@ Tests failed! Тестовете са неуспешни! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5382,11 +7242,6 @@ Error: %@ Благодарение на потребителите – допринесете през Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - Първата платформа без никакви потребителски идентификатори – поверителна по дизайн. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5399,6 +7254,14 @@ It can happen because of some bug or when the connection is compromised.Приложението може да ви уведоми, когато получите съобщения или заявки за контакт - моля, отворете настройките, за да активирате. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + No comment provided by engineer. + The attempt to change database passphrase was not completed. Опитът за промяна на паролата на базата данни не беше завършен. @@ -5409,6 +7272,10 @@ It can happen because of some bug or when the connection is compromised.QR кодът, който сканирахте, не е SimpleX линк за връзка. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + No comment provided by engineer. + The connection you accepted will be cancelled! Връзката, която приехте, ще бъде отказана! @@ -5429,6 +7296,11 @@ It can happen because of some bug or when the connection is compromised.Криптирането работи и новото споразумение за криптиране не е необходимо. Това може да доведе до грешки при свързване! No comment provided by engineer. + + The future of messaging + Ново поколение поверителни съобщения + No comment provided by engineer. + The hash of the previous message is different. Хешът на предишното съобщение е различен. @@ -5444,9 +7316,12 @@ It can happen because of some bug or when the connection is compromised.Съобщението ще бъде маркирано като модерирано за всички членове. No comment provided by engineer. - - The next generation of private messaging - Ново поколение поверителни съобщения + + The messages will be deleted for all members. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. No comment provided by engineer. @@ -5454,9 +7329,12 @@ It can happen because of some bug or when the connection is compromised.Старата база данни не бе премахната по време на миграцията, тя може да бъде изтрита. No comment provided by engineer. - - The profile is only shared with your contacts. - Профилът се споделя само с вашите контакти. + + The same conditions will apply to operator **%@**. + No comment provided by engineer. + + + The second preset operator in the app! No comment provided by engineer. @@ -5474,14 +7352,25 @@ It can happen because of some bug or when the connection is compromised.Сървърите за нови връзки на текущия ви чат профил **%@**. No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. Текстът, който поставихте, не е SimpleX линк за връзка. No comment provided by engineer. - - Theme - Тема + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + + + Themes + No comment provided by engineer. + + + These conditions will also apply for: **%@**. No comment provided by engineer. @@ -5504,6 +7393,10 @@ It can happen because of some bug or when the connection is compromised.Това действие не може да бъде отменено - съобщенията, изпратени и получени по-рано от избраното, ще бъдат изтрити. Може да отнеме няколко минути. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. Това действие не може да бъде отменено - вашият профил, контакти, съобщения и файлове ще бъдат безвъзвратно загубени. @@ -5511,10 +7404,12 @@ It can happen because of some bug or when the connection is compromised. This chat is protected by end-to-end encryption. + Този чат е защитен чрез криптиране от край до край. E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + Този чат е защитен от квантово устойчиво криптиране от край до край. E2EE info chat item @@ -5547,11 +7442,27 @@ It can happen because of some bug or when the connection is compromised.Това е вашят еднократен линк за връзка! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + No comment provided by engineer. + + + This message was deleted or not received yet. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. Тази настройка се прилага за съобщения в текущия ви профил **%@**. No comment provided by engineer. + + Title + No comment provided by engineer. + To ask any questions and to receive updates: За да задавате въпроси и да получавате актуализации: @@ -5572,9 +7483,8 @@ It can happen because of some bug or when the connection is compromised.За да направите нова връзка No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - За да се защити поверителността, вместо потребителски идентификатори, използвани от всички други платформи, SimpleX има идентификатори за опашки от съобщения, отделни за всеки от вашите контакти. + + To protect against your link being replaced, you can compare contact security codes. No comment provided by engineer. @@ -5582,6 +7492,10 @@ It can happen because of some bug or when the connection is compromised.За да не се разкрива часовата зона, файловете с изображения/глас използват UTC. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5589,6 +7503,23 @@ You will be prompted to complete authentication before this feature is enabled.< Ще бъдете подканени да извършите идентификация, преди тази функция да бъде активирана. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + За да се защити поверителността, вместо потребителски идентификатори, използвани от всички други платформи, SimpleX има идентификатори за опашки от съобщения, отделни за всеки от вашите контакти. + No comment provided by engineer. + + + To receive + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. За да запишете гласово съобщение, моля, дайте разрешение за използване на микрофон. @@ -5599,26 +7530,54 @@ You will be prompted to complete authentication before this feature is enabled.< За да разкриете своя скрит профил, въведете пълна парола в полето за търсене на страницата **Вашите чат профили**. No comment provided by engineer. + + To send + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. За поддръжка на незабавни push известия, базата данни за чат трябва да бъде мигрирана. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. За да проверите криптирането от край до край с вашия контакт, сравнете (или сканирайте) кода на вашите устройства. No comment provided by engineer. + + Toggle chat list: + No comment provided by engineer. + Toggle incognito when connecting. Избор на инкогнито при свързване. No comment provided by engineer. + + Token status: %@. + token status + + + Toolbar opacity + No comment provided by engineer. + + + Total + No comment provided by engineer. + Transport isolation Транспортна изолация No comment provided by engineer. + + Transport sessions + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Опит за свързване със сървъра, използван за получаване на съобщения от този контакт (грешка: %@). @@ -5674,10 +7633,9 @@ You will be prompted to complete authentication before this feature is enabled.< Отблокирай член? No comment provided by engineer. - - Unexpected error: %@ - Неочаквана грешка: %@ - item status description + + Undelivered messages + No comment provided by engineer. Unexpected migration state @@ -5687,7 +7645,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unfav. Премахни от любимите - No comment provided by engineer. + swipe action Unhide @@ -5724,6 +7682,10 @@ You will be prompted to complete authentication before this feature is enabled.< Непозната грешка No comment provided by engineer. + + Unknown servers! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. Освен ако не използвате интерфейса за повикване на iOS, активирайте режима "Не безпокой", за да избегнете прекъсвания. @@ -5759,11 +7721,15 @@ To connect, please ask your contact to create another connection link and check Unmute Уведомявай - No comment provided by engineer. + notification label action Unread Непрочетено + swipe action + + + Unsupported connection link No comment provided by engineer. @@ -5776,11 +7742,6 @@ To connect, please ask your contact to create another connection link and check Актуализация No comment provided by engineer. - - Update .onion hosts setting? - Актуализиране на настройката за .onion хостове? - No comment provided by engineer. - Update database passphrase Актуализирай паролата на базата данни @@ -5791,9 +7752,12 @@ To connect, please ask your contact to create another connection link and check Актуализиране на мрежовите настройки? No comment provided by engineer. - - Update transport isolation mode? - Актуализиране на режима на изолация на транспорта? + + Update settings? + No comment provided by engineer. + + + Updated conditions No comment provided by engineer. @@ -5801,18 +7765,18 @@ To connect, please ask your contact to create another connection link and check Актуализирането на настройките ще свърже отново клиента към всички сървъри. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - Актуализирането на тази настройка ще свърже повторно клиента към всички сървъри. - No comment provided by engineer. - Upgrade and open chat Актуализирай и отвори чата No comment provided by engineer. + + Upload errors + No comment provided by engineer. + Upload failed + Неуспешно качване No comment provided by engineer. @@ -5820,8 +7784,21 @@ To connect, please ask your contact to create another connection link and check Качи файл server test step + + Uploaded + No comment provided by engineer. + + + Uploaded files + No comment provided by engineer. + Uploading archive + Архивът се качва + No comment provided by engineer. + + + Use %@ No comment provided by engineer. @@ -5829,11 +7806,23 @@ To connect, please ask your contact to create another connection link and check Използвай .onion хостове No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? Използвай сървърите на SimpleX Chat? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat Използвай чата @@ -5844,6 +7833,14 @@ To connect, please ask your contact to create another connection link and check Използвай текущия профил No comment provided by engineer. + + Use for files + No comment provided by engineer. + + + Use for messages + No comment provided by engineer. + Use for new connections Използвай за нови връзки @@ -5869,23 +7866,46 @@ To connect, please ask your contact to create another connection link and check Използвай само локални известия? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + No comment provided by engineer. + + + Use private routing with unknown servers. + No comment provided by engineer. + Use server Използвай сървър No comment provided by engineer. + + Use servers + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. + Използвайте приложението по време на разговора. No comment provided by engineer. - - User profile - Потребителски профил + + Use the app with one hand. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - Използването на .onion хостове изисква съвместим VPN доставчик. + + Use web port + No comment provided by engineer. + + + User selection + No comment provided by engineer. + + + Username No comment provided by engineer. @@ -5915,10 +7935,12 @@ To connect, please ask your contact to create another connection link and check Verify database passphrase + Проверете паролата на базата данни No comment provided by engineer. Verify passphrase + Провери паролата No comment provided by engineer. @@ -5956,11 +7978,19 @@ To connect, please ask your contact to create another connection link and check Видео и файлове до 1gb No comment provided by engineer. + + View conditions + No comment provided by engineer. + View security code Виж кода за сигурност No comment provided by engineer. + + View updated conditions + No comment provided by engineer. + Visible history Видима история @@ -5976,11 +8006,16 @@ To connect, please ask your contact to create another connection link and check Гласовите съобщения са забранени в този чат. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. Гласовите съобщения са забранени в тази група. No comment provided by engineer. + + Voice messages not allowed + Гласовите съобщения не са разрешени + No comment provided by engineer. + Voice messages prohibited! Гласовите съобщения са забранени! @@ -6011,8 +8046,17 @@ To connect, please ask your contact to create another connection link and check Изчаква се получаването на видеото No comment provided by engineer. + + Wallpaper accent + No comment provided by engineer. + + + Wallpaper background + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures + Внимание: стартирането на чата на множество устройства не се поддържа и ще доведе до неуспешно изпращане на съобщения No comment provided by engineer. @@ -6037,6 +8081,7 @@ To connect, please ask your contact to create another connection link and check Welcome message is too long + Съобщението при посрещане е твърде дълго No comment provided by engineer. @@ -6049,9 +8094,13 @@ To connect, please ask your contact to create another connection link and check Когато са налични No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Когато хората искат да се свържат с вас, можете да ги приемете или отхвърлите. + + When connecting audio and video calls. + При свързване на аудио и видео разговори. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. No comment provided by engineer. @@ -6059,6 +8108,21 @@ To connect, please ask your contact to create another connection link and check Когато споделяте инкогнито профил с някого, този профил ще се използва за групите, в които той ви кани. No comment provided by engineer. + + WiFi + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + Ще бъде активирано в личните чатове! + No comment provided by engineer. + + + Wired ethernet + Кабелен Ethernet + No comment provided by engineer. + With encrypted files and media. С криптирани файлове и медия. @@ -6074,28 +8138,39 @@ To connect, please ask your contact to create another connection link and check С намален разход на батерията. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + alert message + Wrong database passphrase Грешна парола за базата данни No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + file error text + Wrong passphrase! Грешна парола! No comment provided by engineer. - - XFTP servers - XFTP сървъри - No comment provided by engineer. - - - You - Вие + + XFTP server No comment provided by engineer. You **must not** use the same database on two devices. + **Не трябва** да използвате една и съща база данни на две устройства. No comment provided by engineer. @@ -6118,6 +8193,10 @@ To connect, please ask your contact to create another connection link and check Вече сте вече свързани с %@. No comment provided by engineer. + + You are already connected with %@. + No comment provided by engineer. + You are already connecting to %@. Вече се свързвате с %@. @@ -6165,11 +8244,23 @@ Repeat join request? Поканени сте в групата No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. Можете да приемате обаждания от заключен екран, без идентификация на устройство и приложението. No comment provided by engineer. + + You can change it in Appearance settings. + No comment provided by engineer. + + + You can configure servers via settings. + No comment provided by engineer. + You can create it later Можете да го създадете по-късно @@ -6187,6 +8278,7 @@ Repeat join request? You can give another try. + Можете да опитате още веднъж. No comment provided by engineer. @@ -6199,11 +8291,19 @@ Repeat join request? Можете да го направите видим за вашите контакти в SimpleX чрез Настройки. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ Вече можете да изпращате съобщения до %@ notification body + + You can send messages to %@ from Archived contacts. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + No comment provided by engineer. + You can set lock screen notification preview via settings. Можете да зададете визуализация на известията на заключен екран през настройките. @@ -6219,16 +8319,15 @@ Repeat join request? Можете да споделите този адрес с вашите контакти, за да им позволите да се свържат с **%@**. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - Можете да споделите адреса си като линк или QR код - всеки може да се свърже с вас. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app Можете да започнете чат през Настройки на приложението / База данни или като рестартирате приложението No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. Можете да включите SimpleX заключване през Настройки. @@ -6242,23 +8341,23 @@ Repeat join request? You can view invitation link again in connection details. Можете да видите отново линкът за покана в подробностите за връзката. - No comment provided by engineer. + alert message You can't send messages! Не може да изпращате съобщения! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Вие контролирате през кой сървър(и) **да получавате** съобщенията, вашите контакти – сървърите, които използвате, за да им изпращате съобщения. - No comment provided by engineer. - You could not be verified; please try again. Не можахте да бъдете потвърдени; Моля, опитайте отново. No comment provided by engineer. + + You decide who can connect. + Хората могат да се свържат с вас само чрез ликовете, които споделяте. + No comment provided by engineer. + You have already requested connection via this address! Вече сте заявили връзка през този адрес! @@ -6271,11 +8370,6 @@ Repeat connection request? Изпрати отново заявката за свързване? No comment provided by engineer. - - You have no chats - Нямате чатове - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. Трябва да въвеждате парола при всяко стартиране на приложението - тя не се съхранява на устройството. @@ -6296,11 +8390,23 @@ Repeat connection request? Вие се присъединихте към тази група. Свързване с поканващия член на групата. No comment provided by engineer. + + You may migrate the exported database. + No comment provided by engineer. + + + You may save the exported archive. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. Трябва да използвате най-новата версия на вашата чат база данни САМО на едно устройство, в противен случай може да спрете да получавате съобщения от някои контакти. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. Трябва да разрешите на вашия контакт да изпраща гласови съобщения, за да можете да ги изпращате. @@ -6316,6 +8422,10 @@ Repeat connection request? Изпратихте покана за групата No comment provided by engineer. + + You should receive notifications. + token info + You will be connected to group when the group host's device is online, please wait or check later! Ще бъдете свързани с групата, когато устройството на домакина на групата е онлайн, моля, изчакайте или проверете по-късно! @@ -6351,6 +8461,10 @@ Repeat connection request? Все още ще получавате обаждания и известия от заглушени профили, когато са активни. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Ще спрете да получавате съобщения от тази група. Историята на чата ще бъде запазена. @@ -6371,29 +8485,14 @@ Repeat connection request? Използвате инкогнито профил за тази група - за да се предотврати споделянето на основния ви профил, поканите на контакти не са разрешени No comment provided by engineer. - - Your %@ servers - Вашите %@ сървъри - No comment provided by engineer. - Your ICE servers Вашите ICE сървъри No comment provided by engineer. - - Your SMP servers - Вашите SMP сървъри - No comment provided by engineer. - Your SimpleX address - Вашият SimpleX адрес - No comment provided by engineer. - - - Your XFTP servers - Вашите XFTP сървъри + Вашият адрес в SimpleX No comment provided by engineer. @@ -6403,24 +8502,25 @@ Repeat connection request? Your chat database - Вашата чат база данни + Вашата база данни No comment provided by engineer. Your chat database is not encrypted - set passphrase to encrypt it. - Вашата чат база данни не е криптирана - задайте парола, за да я криптирате. + Вашата база данни не е криптирана - задайте парола, за да я криптирате. No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles Вашите чат профили No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Вашият контакт трябва да бъде онлайн, за да осъществите връзката. -Можете да откажете тази връзка и да премахнете контакта (и да опитате по -късно с нов линк). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. No comment provided by engineer. @@ -6438,9 +8538,13 @@ You can cancel this connection and remove the contact (and try later with a new Вашите контакти ще останат свързани. No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. - Вашата текуща чат база данни ще бъде ИЗТРИТА и ЗАМЕНЕНА с импортираната. + Вашата текуща база данни ще бъде ИЗТРИТА и ЗАМЕНЕНА с импортираната. No comment provided by engineer. @@ -6468,33 +8572,34 @@ You can cancel this connection and remove the contact (and try later with a new Вашият профил **%@** ще бъде споделен. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Вашият профил се съхранява на вашето устройство и се споделя само с вашите контакти. -SimpleX сървърите не могат да видят вашия профил. + + Your profile is stored on your device and only shared with your contacts. + Профилът се споделя само с вашите контакти. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Вашият профил, контакти и доставени съобщения се съхраняват на вашето устройство. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Вашият профил се съхранява на вашето устройство и се споделя само с вашите контакти. SimpleX сървърите не могат да видят вашия профил. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your random profile Вашият автоматично генериран профил No comment provided by engineer. - - Your server - Вашият сървър - No comment provided by engineer. - Your server address Вашият адрес на сървъра No comment provided by engineer. + + Your servers + No comment provided by engineer. + Your settings Вашите настройки @@ -6535,11 +8640,20 @@ SimpleX сървърите не могат да видят вашия профи обаждането прието call status + + accepted invitation + chat list item title + admin админ member role + + admins + администратори + feature role + agreeing encryption for %@… съгласуване на криптиране за %@… @@ -6550,6 +8664,11 @@ SimpleX сървърите не могат да видят вашия профи съгласуване на криптиране… chat item text + + all members + всички членове + feature role + always винаги @@ -6560,6 +8679,14 @@ SimpleX сървърите не могат да видят вашия профи и %lld други събития No comment provided by engineer. + + archived report + No comment provided by engineer. + + + attempts + No comment provided by engineer. + audio call (not e2e encrypted) аудио разговор (не е e2e криптиран) @@ -6593,13 +8720,18 @@ SimpleX сървърите не могат да видят вашия профи blocked by admin блокиран от админ - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold удебелен No comment provided by engineer. + + call + No comment provided by engineer. + call error грешка при повикване @@ -6703,7 +8835,7 @@ SimpleX сървърите не могат да видят вашия профи connecting… свързване… - chat list item title + No comment provided by engineer. connection established @@ -6750,10 +8882,15 @@ SimpleX сървърите не могат да видят вашия профи дни time unit + + decryption errors + No comment provided by engineer. + default (%@) по подразбиране (%@) - pref value + delete after time +pref value default (no) @@ -6777,7 +8914,7 @@ SimpleX сървърите не могат да видят вашия профи deleted group - групата изтрита + групата е изтрита rcv group event chat item @@ -6800,6 +8937,10 @@ SimpleX сървърите не могат да видят вашия профи дублирано съобщение integrity error chat item + + duplicates + No comment provided by engineer. + e2e encrypted e2e криптиран @@ -6875,9 +9016,13 @@ SimpleX сървърите не могат да видят вашия профи грешка No comment provided by engineer. - - event happened - събитие се случи + + expired + No comment provided by engineer. + + + forwarded + препратено No comment provided by engineer. @@ -6905,6 +9050,10 @@ SimpleX сървърите не могат да видят вашия профи iOS Keychain ще се използва за сигурно съхраняване на паролата, след като рестартирате приложението или промените паролата - това ще позволи получаването на push известия. No comment provided by engineer. + + inactive + No comment provided by engineer. + incognito via contact address link инкогнито чрез линк с адрес за контакт @@ -6945,6 +9094,10 @@ SimpleX сървърите не могат да видят вашия профи покана за група %@ group name + + invite + No comment provided by engineer. + invited поканен @@ -7000,6 +9153,10 @@ SimpleX сървърите не могат да видят вашия профи свързан rcv group event chat item + + message + No comment provided by engineer. + message received получено съобщение @@ -7025,6 +9182,10 @@ SimpleX сървърите не могат да видят вашия профи модерирано от %@ marked deleted chat item preview text + + moderator + member role + months месеци @@ -7033,7 +9194,7 @@ SimpleX сървърите не могат да видят вашия профи never никога - No comment provided by engineer. + delete after time new message @@ -7064,8 +9225,8 @@ SimpleX сървърите не могат да видят вашия профи off изключено enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7082,18 +9243,40 @@ SimpleX сървърите не могат да видят вашия профи включено group pref value + + other + No comment provided by engineer. + + + other errors + No comment provided by engineer. + owner собственик member role + + owners + собственици + feature role + peer-to-peer peer-to-peer No comment provided by engineer. + + pending + No comment provided by engineer. + + + pending approval + No comment provided by engineer. + quantum resistant e2e encryption + квантово устойчиво e2e криптиране chat item text @@ -7106,6 +9289,10 @@ SimpleX сървърите не могат да видят вашия профи получено потвърждение… No comment provided by engineer. + + rejected + No comment provided by engineer. + rejected call отхвърлено повикване @@ -7136,6 +9323,24 @@ SimpleX сървърите не могат да видят вашия профи ви острани rcv group event chat item + + requested to connect + chat list item title + + + saved + запазено + No comment provided by engineer. + + + saved from %@ + запазено от %@ + No comment provided by engineer. + + + search + No comment provided by engineer. + sec сек. @@ -7161,6 +9366,12 @@ SimpleX сървърите не могат да видят вашия профи изпрати лично съобщение No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + queue info + set new contact address зададен нов адрес за контакт @@ -7173,6 +9384,7 @@ SimpleX сървърите не могат да видят вашия профи standard end-to-end encryption + стандартно криптиране от край до край chat item text @@ -7200,11 +9412,19 @@ SimpleX сървърите не могат да видят вашия профи неизвестен connection info + + unknown servers + No comment provided by engineer. + unknown status неизвестен статус No comment provided by engineer. + + unprotected + No comment provided by engineer. + updated group profile актуализиран профил на групата @@ -7245,6 +9465,10 @@ SimpleX сървърите не могат да видят вашия профи чрез реле No comment provided by engineer. + + video + No comment provided by engineer. + video call (not e2e encrypted) видео разговор (не е e2e криптиран) @@ -7270,11 +9494,20 @@ SimpleX сървърите не могат да видят вашия профи седмици time unit + + when IP hidden + No comment provided by engineer. + yes да pref value + + you + вие + No comment provided by engineer. + you are invited to group вие сте поканени в групата @@ -7349,7 +9582,7 @@ SimpleX сървърите не могат да видят вашия профи
- +
@@ -7386,7 +9619,7 @@ SimpleX сървърите не могат да видят вашия профи
- +
@@ -7406,4 +9639,205 @@ SimpleX сървърите не могат да видят вашия профи
+ +
+ +
+ + + %d new events + notification body + + + From %d chat(s) + notification body + + + From: %@ + notification body + + + New events + notification + + + New messages + notification + + +
+ +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + App is locked! + No comment provided by engineer. + + + Cancel + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Cannot forward message + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database encrypted! + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database passphrase is required to open chat. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Passphrase + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + No comment provided by engineer. + + + Sending a message takes longer than expected. + No comment provided by engineer. + + + Sending message… + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Slow network? + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wait + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/contents.json b/apps/ios/SimpleX Localizations/bg.xcloc/contents.json index 23e8239ce8..66d64e6539 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/bg.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "bg", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/bn.xcloc/Localized Contents/bn.xliff b/apps/ios/SimpleX Localizations/bn.xcloc/Localized Contents/bn.xliff index f599f9c300..bf7753675e 100644 --- a/apps/ios/SimpleX Localizations/bn.xcloc/Localized Contents/bn.xliff +++ b/apps/ios/SimpleX Localizations/bn.xcloc/Localized Contents/bn.xliff @@ -193,20 +193,16 @@ ) No comment provided by engineer.
- - **Add new contact**: to create your one-time QR Code or link for your contact. - No comment provided by engineer. - **Create link / QR code** for your contact to use. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. No comment provided by engineer. @@ -217,8 +213,8 @@ **Please note**: you will NOT be able to recover or change passphrase if you lose it. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. No comment provided by engineer. @@ -386,8 +382,8 @@ Add servers by scanning QR codes. No comment provided by engineer. - - Add server… + + Add server No comment provided by engineer. @@ -1251,8 +1247,8 @@ Direct messages chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited. No comment provided by engineer. @@ -1271,8 +1267,8 @@ Disappearing messages are prohibited in this chat. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. No comment provided by engineer. @@ -1751,24 +1747,24 @@ Group links No comment provided by engineer. - - Group members can add message reactions. + + Members can add message reactions. No comment provided by engineer. Group members can irreversibly delete sent messages. No comment provided by engineer. - - Group members can send direct messages. + + Members can send direct messages. No comment provided by engineer. - - Group members can send disappearing messages. + + Members can send disappearing messages. No comment provided by engineer. - - Group members can send voice messages. + + Members can send voice messages. No comment provided by engineer. @@ -1899,8 +1895,8 @@ Immediately No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam No comment provided by engineer. @@ -2020,8 +2016,8 @@ Irreversible message deletion is prohibited in this chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. No comment provided by engineer. @@ -2207,8 +2203,8 @@ Message reactions are prohibited in this chat. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. No comment provided by engineer. @@ -2239,8 +2235,8 @@ Migration is completed No comment provided by engineer. - - Migrations: %@ + + Migrations: No comment provided by engineer. @@ -2409,8 +2405,8 @@ Onion hosts will not be used. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only client devices store user profiles, contacts, groups, and messages. No comment provided by engineer. @@ -2477,8 +2473,8 @@ Open user profiles authentication reason - - Open-source protocol and code – anybody can run the servers. + + Anybody can host servers. No comment provided by engineer. @@ -2537,8 +2533,8 @@ Paste the link you received into the box below to connect with your contact. No comment provided by engineer. - - People can connect to you only via the links you share. + + You decide who can connect. No comment provided by engineer. @@ -3373,8 +3369,8 @@ Thanks to the users – contribute via Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. + + No user identifiers. No comment provided by engineer. @@ -3418,16 +3414,16 @@ It can happen because of some bug or when the connection is compromised.The message will be marked as moderated for all members. No comment provided by engineer. - - The next generation of private messaging + + The future of messaging No comment provided by engineer. The old database was not removed during the migration, it can be deleted. No comment provided by engineer. - - The profile is only shared with your contacts. + + Your profile is stored on your device and only shared with your contacts. No comment provided by engineer. @@ -3490,8 +3486,8 @@ It can happen because of some bug or when the connection is compromised.To make a new connection No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. No comment provided by engineer. @@ -3724,8 +3720,8 @@ To connect, please ask your contact to create another connection link and check Voice messages are prohibited in this chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. No comment provided by engineer. @@ -3876,10 +3872,6 @@ To connect, please ask your contact to create another connection link and check You can't send messages! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - You could not be verified; please try again. No comment provided by engineer. diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff index cf6e0e1fcd..0400839cb0 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (lze kopírovat) @@ -109,6 +82,7 @@ %@ downloaded + %@ staženo No comment provided by engineer. @@ -126,6 +100,11 @@ %@ je ověřený No comment provided by engineer. + + %@ server + %@ server + No comment provided by engineer. + %@ servers %@ servery @@ -140,6 +119,11 @@ %@ se chce připojit! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members No comment provided by engineer. @@ -159,11 +143,35 @@ %d dní time interval + + %d file(s) are still being downloaded. + %d soubor(y) stále stahován(y). + forward confirmation reason + + + %d file(s) failed to download. + %d soubor(y) se nepodařilo stáhnout. + forward confirmation reason + + + %d file(s) were deleted. + %d soubor(y) smazán(y). + forward confirmation reason + + + %d file(s) were not downloaded. + %d soubor(y) nestažen(y). + forward confirmation reason + %d hours %d hodin time interval + + %d messages not forwarded + alert title + %d min %d minuty @@ -179,6 +187,10 @@ %d sek time interval + + %d seconds(s) + delete after time + %d skipped message(s) %d přeskočené zprávy @@ -220,14 +232,17 @@ %lld messages blocked + %lld zprávy blokovaný No comment provided by engineer. %lld messages blocked by admin + %lld zprávy blokovaný adminem No comment provided by engineer. %lld messages marked deleted + %lld zprávy označeno jako smazáno No comment provided by engineer. @@ -244,11 +259,6 @@ %d nové jazyky rozhraní No comment provided by engineer. - - %lld second(s) - %lld vteřin - No comment provided by engineer. - %lld seconds %lld vteřin @@ -299,44 +309,30 @@ %u zpráv přeskočeno. No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) No comment provided by engineer. (this device v%@) + (toto zařízení v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Přidat nový kontakt**: pro vytvoření jednorázového QR kódu nebo odkazu pro váš kontakt. + + **Create 1-time link**: to create and share a new invitation link. No comment provided by engineer. **Create group**: to create a new group. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **Soukromější**: kontrolovat nové zprávy každých 20 minut. Token zařízení je sdílen se serverem SimpleX Chat, ale ne kolik máte kontaktů nebo zpráv. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **Nejsoukromější**: nepoužívejte server oznámení SimpleX Chat, pravidelně kontrolujte zprávy na pozadí (závisí na tom, jak často aplikaci používáte). No comment provided by engineer. @@ -349,11 +345,15 @@ **Upozornění**: Pokud heslo ztratíte, NEBUDETE jej moci obnovit ani změnit. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Doporučeno**: Token zařízení a oznámení se odesílají na oznamovací server SimpleX Chat, ale nikoli obsah, velikost nebo od koho jsou zprávy. No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **Upozornění**: Okamžitě doručovaná oznámení vyžadují přístupové heslo uložené v Klíčence. @@ -378,11 +378,6 @@ \*tučně* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -416,11 +411,6 @@ - historie úprav. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec time to disappear @@ -433,7 +423,8 @@ 1 day 1 den - time interval + delete after time +time interval 1 hour @@ -448,12 +439,26 @@ 1 month 1 měsíc - time interval + delete after time +time interval 1 week 1 týden - time interval + delete after time +time interval + + + 1 year + delete after time + + + 1-time link + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + No comment provided by engineer. 5 minutes @@ -470,11 +475,6 @@ 30 vteřin No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -524,31 +524,29 @@ Přerušit změnu adresy? No comment provided by engineer. - - About SimpleX - O SimpleX - No comment provided by engineer. - About SimpleX Chat O SimpleX chat No comment provided by engineer. - - About SimpleX address - O SimpleX adrese + + About operators No comment provided by engineer. - - Accent color - Zbarvení + + Accent No comment provided by engineer. Accept Přijmout accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + No comment provided by engineer. Accept connection request? @@ -563,20 +561,40 @@ Accept incognito Přijmout inkognito - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + No comment provided by engineer. + + + Acknowledged + No comment provided by engineer. + + + Acknowledgement errors + No comment provided by engineer. + + + Active + token status text + + + Active connections + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Přidejte adresu do svého profilu, aby ji vaše kontakty mohly sdílet s dalšími lidmi. Aktualizace profilu bude zaslána vašim kontaktům. No comment provided by engineer. - - Add contact + + Add friends No comment provided by engineer. - - Add preset servers - Přidejte přednastavené servery + + Add list No comment provided by engineer. @@ -584,14 +602,18 @@ Přidat profil No comment provided by engineer. + + Add server + Přidat server + No comment provided by engineer. + Add servers by scanning QR codes. Přidejte servery skenováním QR kódů. No comment provided by engineer. - - Add server… - Přidat server… + + Add team members No comment provided by engineer. @@ -599,11 +621,39 @@ Přidat do jiného zařízení No comment provided by engineer. + + Add to list + No comment provided by engineer. + Add welcome message Přidat uvítací zprávu No comment provided by engineer. + + Add your team members to the conversations. + No comment provided by engineer. + + + Added media & file servers + No comment provided by engineer. + + + Added message servers + No comment provided by engineer. + + + Additional accent + No comment provided by engineer. + + + Additional accent 2 + No comment provided by engineer. + + + Additional secondary + No comment provided by engineer. + Address Adresa @@ -614,6 +664,14 @@ Změna adresy bude přerušena. Budou použity staré přijímací adresy. No comment provided by engineer. + + Address or 1-time link? + No comment provided by engineer. + + + Address settings + No comment provided by engineer. + Admins can block a member for all. No comment provided by engineer. @@ -628,6 +686,14 @@ Pokročilá nastavení sítě No comment provided by engineer. + + Advanced settings + No comment provided by engineer. + + + All + No comment provided by engineer. + All app data is deleted. Všechna data aplikace jsou smazána. @@ -638,16 +704,28 @@ Všechny chaty a zprávy budou smazány – tuto akci nelze vrátit zpět! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + alert message + All data is erased when it is entered. Všechna data se při zadání vymažou. No comment provided by engineer. + + All data is kept private on your device. + No comment provided by engineer. + All group members will remain connected. Všichni členové skupiny zůstanou připojeni. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! No comment provided by engineer. @@ -661,6 +739,18 @@ All new messages from %@ will be hidden! No comment provided by engineer. + + All profiles + profile dropdown + + + All reports will be archived for you. + No comment provided by engineer. + + + All servers + No comment provided by engineer. + All your contacts will remain connected. Všechny vaše kontakty zůstanou připojeny. @@ -685,11 +775,19 @@ Povolte hovory, pouze pokud je váš kontakt povolí. No comment provided by engineer. + + Allow calls? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Povolte mizící zprávy, pouze pokud vám to váš kontakt dovolí. No comment provided by engineer. + + Allow downgrade + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Povolte nevratné smazání zprávy pouze v případě, že vám to váš kontakt dovolí. (24 hodin) @@ -715,11 +813,23 @@ Povolit odesílání mizících zpráv. No comment provided by engineer. + + Allow sharing + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Povolit nevratné smazání odeslaných zpráv. (24 hodin) No comment provided by engineer. + + Allow to report messsages to moderators. + No comment provided by engineer. + + + Allow to send SimpleX links. + No comment provided by engineer. + Allow to send files and media. Povolit odesílání souborů a médii. @@ -778,6 +888,10 @@ Already joining the group! No comment provided by engineer. + + Always use private routing. + No comment provided by engineer. + Always use relay Spojení přes relé @@ -788,11 +902,20 @@ Vytvořit prázdný chat profil se zadaným názvem a otevřít aplikaci jako obvykle. No comment provided by engineer. + + Another reason + report reason + Answer call Přijmout hovor No comment provided by engineer. + + Anybody can host servers. + Servery může provozovat kdokoli. + No comment provided by engineer. + App build: %@ Sestavení aplikace: %@ @@ -807,6 +930,10 @@ Aplikace šifruje nové místní soubory (s výjimkou videí). No comment provided by engineer. + + App group: + No comment provided by engineer. + App icon Ikona aplikace @@ -822,6 +949,10 @@ Přístupový kód aplikace je nahrazen sebedestrukčním přístupovým heslem. No comment provided by engineer. + + App session + No comment provided by engineer. + App version Verze aplikace @@ -841,10 +972,46 @@ Apply No comment provided by engineer. + + Apply to + No comment provided by engineer. + + + Archive + No comment provided by engineer. + + + Archive %lld reports? + No comment provided by engineer. + + + Archive all reports? + No comment provided by engineer. + Archive and upload No comment provided by engineer. + + Archive contacts to chat later. + No comment provided by engineer. + + + Archive report + No comment provided by engineer. + + + Archive report? + No comment provided by engineer. + + + Archive reports + swipe action + + + Archived contacts + No comment provided by engineer. + Archiving database No comment provided by engineer. @@ -909,11 +1076,19 @@ Automaticky přijímat obrázky No comment provided by engineer. + + Auto-accept settings + alert title + Back Zpět No comment provided by engineer. + + Background + No comment provided by engineer. + Bad desktop address No comment provided by engineer. @@ -928,15 +1103,51 @@ Špatný hash zprávy No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups No comment provided by engineer. + + Better groups performance + No comment provided by engineer. + + + Better message dates. + No comment provided by engineer. + Better messages Lepší zprávy No comment provided by engineer. + + Better networking + No comment provided by engineer. + + + Better notifications + No comment provided by engineer. + + + Better privacy and security + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + + + Black + No comment provided by engineer. + Block No comment provided by engineer. @@ -965,6 +1176,14 @@ Blocked by admin No comment provided by engineer. + + Blur for better privacy. + No comment provided by engineer. + + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Vy i váš kontakt můžete přidávat reakce na zprávy. @@ -995,11 +1214,29 @@ Bulharský, finský, thajský a ukrajinský - díky uživatelům a [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + No comment provided by engineer. + + + Business chats + No comment provided by engineer. + + + Businesses + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). Podle chat profilu (výchozí) nebo [podle připojení](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + No comment provided by engineer. + Call already ended! Hovor již skončil! @@ -1010,10 +1247,22 @@ Hovory No comment provided by engineer. + + Calls prohibited! + No comment provided by engineer. + Camera not available No comment provided by engineer. + + Can't call contact + No comment provided by engineer. + + + Can't call member + No comment provided by engineer. + Can't invite contact! Nelze pozvat kontakt! @@ -1024,10 +1273,15 @@ Nelze pozvat kontakty! No comment provided by engineer. + + Can't message member + No comment provided by engineer. + Cancel Zrušit - No comment provided by engineer. + alert action +alert button Cancel migration @@ -1038,9 +1292,21 @@ Nelze získat přístup ke klíčence pro uložení hesla databáze No comment provided by engineer. + + Cannot forward message + No comment provided by engineer. + Cannot receive file Nelze přijmout soubor + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + snd error text + + + Cellular No comment provided by engineer. @@ -1048,6 +1314,14 @@ Změnit No comment provided by engineer. + + Change automatic message deletion? + alert title + + + Change chat profiles + authentication reason + Change database passphrase? Změnit přístupovou frázi databáze? @@ -1092,11 +1366,22 @@ Change self-destruct passcode Změnit sebedestrukční heslo authentication reason - set passcode view +set passcode view - - Chat archive - Chat se archivuje + + Chat + No comment provided by engineer. + + + Chat already exists + No comment provided by engineer. + + + Chat already exists! + No comment provided by engineer. + + + Chat colors No comment provided by engineer. @@ -1114,6 +1399,10 @@ Databáze chatu odstraněna No comment provided by engineer. + + Chat database exported + No comment provided by engineer. + Chat database imported Importovaná databáze chatu @@ -1133,6 +1422,10 @@ Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat. No comment provided by engineer. + + Chat list + No comment provided by engineer. + Chat migrated! No comment provided by engineer. @@ -1142,15 +1435,44 @@ Předvolby chatu No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + Profil uživatele + No comment provided by engineer. + + + Chat theme + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + No comment provided by engineer. + Chats Chaty No comment provided by engineer. + + Check messages every 20 min. + No comment provided by engineer. + + + Check messages when allowed. + No comment provided by engineer. + Check server address and try again. Zkontrolujte adresu serveru a zkuste to znovu. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1171,10 +1493,22 @@ Vybrat z knihovny No comment provided by engineer. + + Chunks deleted + No comment provided by engineer. + + + Chunks downloaded + No comment provided by engineer. + + + Chunks uploaded + No comment provided by engineer. + Clear Vyčistit - No comment provided by engineer. + swipe action Clear conversation @@ -1186,6 +1520,14 @@ Vyčistit konverzaci? No comment provided by engineer. + + Clear group? + No comment provided by engineer. + + + Clear or delete group? + No comment provided by engineer. + Clear private notes? No comment provided by engineer. @@ -1195,11 +1537,18 @@ Zrušte ověření No comment provided by engineer. - - Colors - Barvy + + Color chats with the new themes. No comment provided by engineer. + + Color mode + No comment provided by engineer. + + + Community guidelines violation + report reason + Compare file Porovnat soubor @@ -1210,11 +1559,47 @@ Porovnejte bezpečnostní kódy se svými kontakty. No comment provided by engineer. + + Completed + No comment provided by engineer. + + + Conditions accepted on: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + No comment provided by engineer. + + + Conditions of use + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + No comment provided by engineer. + Configure ICE servers Konfigurace serverů ICE No comment provided by engineer. + + Configure server operators + No comment provided by engineer. + Confirm Potvrdit @@ -1225,11 +1610,19 @@ Potvrdit heslo No comment provided by engineer. + + Confirm contact deletion? + No comment provided by engineer. + Confirm database upgrades Potvrdit aktualizaci databáze No comment provided by engineer. + + Confirm files from unknown servers. + No comment provided by engineer. + Confirm network settings No comment provided by engineer. @@ -1252,6 +1645,10 @@ Confirm upload No comment provided by engineer. + + Confirmed + token status text + Connect Připojit @@ -1270,6 +1667,10 @@ Connect to desktop No comment provided by engineer. + + Connect to your friends faster. + No comment provided by engineer. + Connect to yourself? No comment provided by engineer. @@ -1302,14 +1703,26 @@ This is your own one-time link! Connect with %@ No comment provided by engineer. + + Connected + No comment provided by engineer. + Connected desktop No comment provided by engineer. + + Connected servers + No comment provided by engineer. + Connected to desktop No comment provided by engineer. + + Connecting + No comment provided by engineer. + Connecting to server… Připojování k serveru… @@ -1320,6 +1733,10 @@ This is your own one-time link! Připojování k serveru... (chyba: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + No comment provided by engineer. + Connecting to desktop No comment provided by engineer. @@ -1329,6 +1746,14 @@ This is your own one-time link! Připojení No comment provided by engineer. + + Connection and servers status. + No comment provided by engineer. + + + Connection blocked + No comment provided by engineer. + Connection error Chyba připojení @@ -1339,11 +1764,32 @@ This is your own one-time link! Chyba spojení (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + No comment provided by engineer. + + + Connection not ready. + No comment provided by engineer. + + + Connection notifications + No comment provided by engineer. + Connection request sent! Požadavek na připojení byl odeslán! No comment provided by engineer. + + Connection requires encryption renegotiation. + No comment provided by engineer. + + + Connection security + No comment provided by engineer. + Connection terminated No comment provided by engineer. @@ -1353,6 +1799,14 @@ This is your own one-time link! Časový limit připojení No comment provided by engineer. + + Connection with desktop stopped + No comment provided by engineer. + + + Connections + No comment provided by engineer. + Contact allows Kontakt povolil @@ -1363,6 +1817,10 @@ This is your own one-time link! Kontakt již existuje No comment provided by engineer. + + Contact deleted! + No comment provided by engineer. + Contact hidden: Skrytý kontakt: @@ -1373,9 +1831,8 @@ This is your own one-time link! Kontakt je připojen notification - - Contact is not connected yet! - Kontakt ještě není připojen! + + Contact is deleted. No comment provided by engineer. @@ -1388,6 +1845,10 @@ This is your own one-time link! Předvolby kontaktů No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + No comment provided by engineer. + Contacts Kontakty @@ -1398,21 +1859,37 @@ This is your own one-time link! Kontakty mohou označit zprávy ke smazání; vy je budete moci zobrazit. No comment provided by engineer. + + Content violates conditions of use + blocking reason + Continue Pokračovat No comment provided by engineer. + + Conversation deleted! + No comment provided by engineer. + Copy Kopírovat - chat item action + No comment provided by engineer. + + + Copy error + No comment provided by engineer. Core version: v%@ Verze jádra: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? No comment provided by engineer. @@ -1422,6 +1899,10 @@ This is your own one-time link! Vytvořit No comment provided by engineer. + + Create 1-time link + No comment provided by engineer. + Create SimpleX address Vytvořit SimpleX adresu @@ -1431,11 +1912,6 @@ This is your own one-time link! Create a group using a random profile. No comment provided by engineer. - - Create an address to let people connect with you. - Vytvořit adresu, aby se s vámi lidé mohli spojit. - No comment provided by engineer. - Create file Vytvořit soubor @@ -1455,6 +1931,10 @@ This is your own one-time link! Vytvořit odkaz No comment provided by engineer. + + Create list + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 Vytvořit nový profil v [desktop app](https://simplex.chat/downloads/). 💻 @@ -1462,6 +1942,7 @@ This is your own one-time link! Create profile + Vytvořte si profil No comment provided by engineer. @@ -1479,6 +1960,10 @@ This is your own one-time link! Vytvořte si profil No comment provided by engineer. + + Created + No comment provided by engineer. + Created at No comment provided by engineer. @@ -1487,11 +1972,6 @@ This is your own one-time link! Created at: %@ copied message info - - Created on %@ - Vytvořeno na %@ - No comment provided by engineer. - Creating archive link No comment provided by engineer. @@ -1505,11 +1985,19 @@ This is your own one-time link! Aktuální heslo No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + No comment provided by engineer. + Current passphrase… Aktuální přístupová fráze… No comment provided by engineer. + + Current profile + No comment provided by engineer. + Currently maximum supported file size is %@. Aktuálně maximální podporovaná velikost souboru je %@. @@ -1520,11 +2008,23 @@ This is your own one-time link! Vlastní čas No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + + + Customize theme + No comment provided by engineer. + Dark Tmavý No comment provided by engineer. + + Dark mode colors + No comment provided by engineer. + Database ID ID databáze @@ -1623,6 +2123,10 @@ This is your own one-time link! Databáze bude přenesena po restartu aplikace No comment provided by engineer. + + Debug delivery + No comment provided by engineer. + Decentralized Decentralizované @@ -1636,17 +2140,17 @@ This is your own one-time link! Delete Smazat - chat item action + alert action +swipe action + + + Delete %lld messages of members? + No comment provided by engineer. Delete %lld messages? No comment provided by engineer. - - Delete Contact - Smazat kontakt - No comment provided by engineer. - Delete address Odstranit adresu @@ -1671,14 +2175,12 @@ This is your own one-time link! Delete and notify contact No comment provided by engineer. - - Delete archive - Smazat archiv + + Delete chat No comment provided by engineer. - - Delete chat archive? - Smazat archiv chatu? + + Delete chat messages from your device. No comment provided by engineer. @@ -1691,6 +2193,10 @@ This is your own one-time link! Smazat chat profil? No comment provided by engineer. + + Delete chat? + No comment provided by engineer. + Delete connection Smazat připojení @@ -1701,9 +2207,8 @@ This is your own one-time link! Smazat kontakt No comment provided by engineer. - - Delete contact? -This cannot be undone! + + Delete contact? No comment provided by engineer. @@ -1765,6 +2270,10 @@ This cannot be undone! Smazat odkaz? No comment provided by engineer. + + Delete list? + alert title + Delete member message? Smazat zprávu člena? @@ -1778,7 +2287,7 @@ This cannot be undone! Delete messages Smazat zprávy - No comment provided by engineer. + alert button Delete messages after @@ -1795,9 +2304,8 @@ This cannot be undone! Smazat starou databázi? No comment provided by engineer. - - Delete pending connection - Smazat čekající připojení + + Delete or moderate up to 200 messages. No comment provided by engineer. @@ -1815,11 +2323,27 @@ This cannot be undone! Odstranit frontu server test step + + Delete report + No comment provided by engineer. + + + Delete up to 20 messages at once. + No comment provided by engineer. + Delete user profile? Smazat uživatelský profil? No comment provided by engineer. + + Delete without notification + No comment provided by engineer. + + + Deleted + No comment provided by engineer. + Deleted at Smazáno v @@ -1830,6 +2354,14 @@ This cannot be undone! Smazáno v: %@ copied message info + + Deletion errors + No comment provided by engineer. + + + Delivered even when Apple drops them. + No comment provided by engineer. + Delivery Doručenka @@ -1862,11 +2394,35 @@ This cannot be undone! Desktop devices No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + + + Destination server error: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + + + Detailed statistics + No comment provided by engineer. + + + Details + No comment provided by engineer. + Develop Vyvinout No comment provided by engineer. + + Developer options + No comment provided by engineer. + Developer tools Nástroje pro vývojáře @@ -1897,8 +2453,12 @@ This cannot be undone! Přímé zprávy chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + No comment provided by engineer. + + + Direct messages between members are prohibited. Přímé zprávy mezi členy jsou v této skupině zakázány. No comment provided by engineer. @@ -1912,11 +2472,23 @@ This cannot be undone! Vypnutí zámku SimpleX authentication reason + + Disable automatic message deletion? + alert title + + + Disable delete messages + alert button + Disable for all Vypnout pro všechny No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Mizící zpráva @@ -1932,8 +2504,8 @@ This cannot be undone! Mizící zprávy jsou v tomto chatu zakázány. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. Mizící zprávy jsou v této skupině zakázány. No comment provided by engineer. @@ -1965,11 +2537,19 @@ This cannot be undone! Discover via local network No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. NEpoužívejte SimpleX pro tísňová volání. No comment provided by engineer. + + Do NOT use private routing. + No comment provided by engineer. + Do it later Udělat později @@ -1979,6 +2559,14 @@ This cannot be undone! Do not send history to new members. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + + + Documents: + No comment provided by engineer. + Don't create address Nevytvářet adresu @@ -1989,16 +2577,33 @@ This cannot be undone! Nepovolovat No comment provided by engineer. + + Don't miss important messages. + No comment provided by engineer. + Don't show again Znovu neukazuj No comment provided by engineer. + + Done + No comment provided by engineer. + Downgrade and open chat Snížit a otevřít chat No comment provided by engineer. + + Download + alert button +chat item action + + + Download errors + No comment provided by engineer. + Download failed No comment provided by engineer. @@ -2008,6 +2613,18 @@ This cannot be undone! Stáhnout soubor server test step + + Download files + alert action + + + Downloaded + No comment provided by engineer. + + + Downloaded files + No comment provided by engineer. + Downloading archive No comment provided by engineer. @@ -2026,6 +2643,10 @@ This cannot be undone! Trvání No comment provided by engineer. + + E2E encrypted notifications. + No comment provided by engineer. + Edit Upravit @@ -2046,6 +2667,10 @@ This cannot be undone! Povolit (zachovat přepsání) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + No comment provided by engineer. + Enable SimpleX Lock Zapnutí zámku SimpleX @@ -2059,7 +2684,7 @@ This cannot be undone! Enable automatic message deletion? Povolit automatické mazání zpráv? - No comment provided by engineer. + alert title Enable camera access @@ -2104,6 +2729,14 @@ This cannot be undone! Povolit sebedestrukční heslo set passcode view + + Enabled + No comment provided by engineer. + + + Enabled for + No comment provided by engineer. + Encrypt Šifrovat @@ -2171,6 +2804,10 @@ This cannot be undone! Encryption re-negotiation failed. No comment provided by engineer. + + Encryption renegotiation in progress. + No comment provided by engineer. + Enter Passcode Zadat heslo @@ -2232,30 +2869,33 @@ This cannot be undone! Chyba přerušení změny adresy No comment provided by engineer. + + Error accepting conditions + alert title + Error accepting contact request Chyba při přijímání žádosti o kontakt No comment provided by engineer. - - Error accessing database file - Chyba přístupu k souboru databáze - No comment provided by engineer. - Error adding member(s) Chyba přidávání člena(ů) No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + alert title Error changing address Chuba změny adresy No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role Chyba při změně role @@ -2266,6 +2906,18 @@ This cannot be undone! Chyba změny nastavení No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + + + Error checking token status + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Chyba při vytváření adresy @@ -2281,6 +2933,10 @@ This cannot be undone! Chyba při vytváření odkazu skupiny No comment provided by engineer. + + Error creating list + alert title + Error creating member contact Chyba vytvoření kontaktu člena @@ -2295,6 +2951,10 @@ This cannot be undone! Chyba při vytváření profilu! No comment provided by engineer. + + Error creating report + No comment provided by engineer. + Error decrypting file Chyba dešifrování souboru @@ -2315,11 +2975,6 @@ This cannot be undone! Chyba při mazání připojení No comment provided by engineer. - - Error deleting contact - Chyba mazání kontaktu - No comment provided by engineer. - Error deleting database Chyba při mazání databáze @@ -2364,6 +3019,10 @@ This cannot be undone! Chyba při exportu databáze chatu No comment provided by engineer. + + Error exporting theme: %@ + No comment provided by engineer. + Error importing chat database Chyba při importu databáze chatu @@ -2374,9 +3033,12 @@ This cannot be undone! Chyba při připojování ke skupině No comment provided by engineer. - - Error loading %@ servers - Chyba načítání %@ serverů + + Error loading servers + alert title + + + Error migrating settings No comment provided by engineer. @@ -2386,16 +3048,31 @@ This cannot be undone! Error receiving file Chyba při příjmu souboru + alert title + + + Error reconnecting server No comment provided by engineer. + + Error reconnecting servers + No comment provided by engineer. + + + Error registering for notifications + alert title + Error removing member Chyba při odebrání člena No comment provided by engineer. - - Error saving %@ servers - Chyba při ukládání serverů %@ + + Error reordering lists + alert title + + + Error resetting statistics No comment provided by engineer. @@ -2403,6 +3080,10 @@ This cannot be undone! Chyba při ukládání serverů ICE No comment provided by engineer. + + Error saving chat list + alert title + Error saving group profile Chyba při ukládání profilu skupiny @@ -2418,6 +3099,10 @@ This cannot be undone! Při ukládání přístupové fráze do klíčenky došlo k chybě No comment provided by engineer. + + Error saving servers + alert title + Error saving settings when migrating @@ -2461,16 +3146,24 @@ This cannot be undone! Chyba při zastavení chatu No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! Chyba při přepínání profilu! - No comment provided by engineer. + alertTitle Error synchronizing connection Chyba synchronizace připojení No comment provided by engineer. + + Error testing server connection + No comment provided by engineer. + Error updating group link Chyba aktualizace odkazu skupiny @@ -2481,6 +3174,10 @@ This cannot be undone! Chyba aktualizace zprávy No comment provided by engineer. + + Error updating server + alert title + Error updating settings Chyba při aktualizaci nastavení @@ -2507,7 +3204,9 @@ This cannot be undone! Error: %@ Chyba: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2519,6 +3218,14 @@ This cannot be undone! Chyba: žádný soubor databáze No comment provided by engineer. + + Errors + No comment provided by engineer. + + + Errors in servers configuration. + servers error + Even when disabled in the conversation. I při vypnutí v konverzaci. @@ -2533,6 +3240,10 @@ This cannot be undone! Expand chat item action + + Expired + token status text + Export database Export databáze @@ -2543,6 +3254,10 @@ This cannot be undone! Chyba exportu: No comment provided by engineer. + + Export theme + No comment provided by engineer. + Exported database archive. Exportovaný archiv databáze. @@ -2567,15 +3282,57 @@ This cannot be undone! Rychle a bez čekání, než bude odesílatel online! No comment provided by engineer. + + Faster deletion of groups. + No comment provided by engineer. + Faster joining and more reliable messages. No comment provided by engineer. + + Faster sending messages. + No comment provided by engineer. + Favorite Oblíbené + swipe action + + + Favorites No comment provided by engineer. + + File error + file error alert title + + + File errors: +%@ + alert message + + + File is blocked by server operator: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + file error text + + + File server error: %@ + file error text + + + File status + No comment provided by engineer. + + + File status: %@ + copied message info + File will be deleted from servers. Soubor bude smazán ze serverů. @@ -2596,6 +3353,10 @@ This cannot be undone! Soubor: %@ No comment provided by engineer. + + Files + No comment provided by engineer. + Files & media Soubory a média @@ -2606,11 +3367,15 @@ This cannot be undone! Soubory a média chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. Soubory a média jsou zakázány v této skupině. No comment provided by engineer. + + Files and media not allowed + No comment provided by engineer. + Files and media prohibited! Soubory a média jsou zakázány! @@ -2669,11 +3434,93 @@ This cannot be undone! Opravit nepodporované členem skupiny No comment provided by engineer. + + For all moderators + No comment provided by engineer. + + + For chat profile %@: + servers error + For console Pro konzoli No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + No comment provided by engineer. + + + For me + No comment provided by engineer. + + + For private routing + No comment provided by engineer. + + + For social media + No comment provided by engineer. + + + Forward + chat item action + + + Forward %d message(s)? + alert title + + + Forward and save messages + No comment provided by engineer. + + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + + + Forwarded + No comment provided by engineer. + + + Forwarded from + No comment provided by engineer. + + + Forwarding %lld messages + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + snd error text + Found desktop No comment provided by engineer. @@ -2693,11 +3540,6 @@ This cannot be undone! Celé jméno (volitelně) No comment provided by engineer. - - Full name: - Celé jméno: - No comment provided by engineer. - Fully decentralized – visible only to members. No comment provided by engineer. @@ -2717,6 +3559,18 @@ This cannot be undone! GIFy a nálepky No comment provided by engineer. + + Get notified when mentioned. + No comment provided by engineer. + + + Good afternoon! + message preview + + + Good morning! + message preview + Group Skupina @@ -2770,36 +3624,6 @@ This cannot be undone! Odkazy na skupiny No comment provided by engineer. - - Group members can add message reactions. - Členové skupin mohou přidávat reakce na zprávy. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Členové skupiny mohou nevratně mazat odeslané zprávy. (24 hodin) - No comment provided by engineer. - - - Group members can send direct messages. - Členové skupiny mohou posílat přímé zprávy. - No comment provided by engineer. - - - Group members can send disappearing messages. - Členové skupiny mohou posílat mizící zprávy. - No comment provided by engineer. - - - Group members can send files and media. - Členové skupiny mohou posílat soubory a média. - No comment provided by engineer. - - - Group members can send voice messages. - Členové skupiny mohou posílat hlasové zprávy. - No comment provided by engineer. - Group message: Skupinová zpráva: @@ -2840,11 +3664,19 @@ This cannot be undone! Skupina bude smazána pro vás - toto nelze vzít zpět! No comment provided by engineer. + + Groups + No comment provided by engineer. + Help Pomoc No comment provided by engineer. + + Help admins moderating their groups. + No comment provided by engineer. + Hidden Skryté @@ -2894,10 +3726,17 @@ This cannot be undone! Jak SimpleX funguje No comment provided by engineer. + + How it affects privacy + No comment provided by engineer. + + + How it helps privacy + No comment provided by engineer. + How it works - Jak to funguje - No comment provided by engineer. + alert button How to @@ -2923,6 +3762,10 @@ This cannot be undone! Servery ICE (jeden na řádek) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Pokud se nemůžete setkat osobně, zobrazte QR kód ve videohovoru nebo sdílejte odkaz. @@ -2963,8 +3806,8 @@ This cannot be undone! Ihned No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Odolná vůči spamu a zneužití No comment provided by engineer. @@ -2987,10 +3830,19 @@ This cannot be undone! Import failed No comment provided by engineer. + + Import theme + No comment provided by engineer. + Importing archive No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery No comment provided by engineer. @@ -3014,6 +3866,18 @@ This cannot be undone! V odpovědi na No comment provided by engineer. + + In-call sounds + No comment provided by engineer. + + + Inappropriate content + report reason + + + Inappropriate profile + report reason + Incognito Inkognito @@ -3082,6 +3946,11 @@ This cannot be undone! Nainstalujte [SimpleX Chat pro terminál](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Okamžitě + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3089,16 +3958,35 @@ This cannot be undone! No comment provided by engineer. - - Instantly - Okamžitě - No comment provided by engineer. - Interface Rozhranní No comment provided by engineer. + + Interface colors + No comment provided by engineer. + + + Invalid + token status text + + + Invalid (bad token) + token status text + + + Invalid (expired) + token status text + + + Invalid (unregistered) + token status text + + + Invalid (wrong topic) + token status text + Invalid QR code No comment provided by engineer. @@ -3131,7 +4019,7 @@ This cannot be undone! Invalid server address! Neplatná adresa serveru! - No comment provided by engineer. + alert title Invalid status @@ -3153,6 +4041,10 @@ This cannot be undone! Pozvat členy No comment provided by engineer. + + Invite to chat + No comment provided by engineer. + Invite to group Pozvat do skupiny @@ -3168,8 +4060,8 @@ This cannot be undone! Nevratné mazání zpráv je v tomto chatu zakázáno. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. Nevratné mazání zpráv je v této skupině zakázáno. No comment provided by engineer. @@ -3194,6 +4086,10 @@ This cannot be undone! 3. Spojení je kompromitováno. No comment provided by engineer. + + It protects your IP address and connections. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Zdá se, že jste již připojeni prostřednictvím tohoto odkazu. Pokud tomu tak není, došlo k chybě (%@). @@ -3212,7 +4108,7 @@ This cannot be undone! Join Připojte se na - No comment provided by engineer. + swipe action Join group @@ -3248,6 +4144,10 @@ This is your link for group %@! Keep + alert action + + + Keep conversation No comment provided by engineer. @@ -3256,7 +4156,7 @@ This is your link for group %@! Keep unused invitation? - No comment provided by engineer. + alert title Keep your connections @@ -3291,6 +4191,14 @@ This is your link for group %@! Leave Opustit + swipe action + + + Leave chat + No comment provided by engineer. + + + Leave chat? No comment provided by engineer. @@ -3330,6 +4238,18 @@ This is your link for group %@! Linked desktops No comment provided by engineer. + + List + swipe action + + + List name and emoji should be different for all lists. + No comment provided by engineer. + + + List name... + No comment provided by engineer. + Live message! Živé zprávy! @@ -3340,11 +4260,6 @@ This is your link for group %@! Živé zprávy No comment provided by engineer. - - Local - Místní - No comment provided by engineer. - Local name Místní název @@ -3365,11 +4280,6 @@ This is your link for group %@! Režim zámku No comment provided by engineer. - - Make a private connection - Vytvořte si soukromé připojení - No comment provided by engineer. - Make one message disappear Nechat jednu zprávu zmizet @@ -3380,21 +4290,11 @@ This is your link for group %@! Změnit profil na soukromý! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Ujistěte se, že adresy %@ serverů jsou ve správném formátu, oddělené řádky a nejsou duplicitní (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Ujistěte se, že adresy serverů WebRTC ICE jsou ve správném formátu, oddělené na řádcích a nejsou duplicitní. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Mnoho lidí se ptalo: *Pokud SimpleX nemá žádné uživatelské identifikátory, jak může doručovat zprávy?* - No comment provided by engineer. - Mark deleted for everyone Označit jako smazané pro všechny @@ -3420,11 +4320,31 @@ This is your link for group %@! Max 30 vteřin, přijato okamžitě. No comment provided by engineer. + + Media & file servers + No comment provided by engineer. + + + Medium + blur media + Member Člen No comment provided by engineer. + + Member inactive + item status text + + + Member reports + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. Role člena se změní na "%@". Všichni členové skupiny budou upozorněni. @@ -3435,11 +4355,61 @@ This is your link for group %@! Role člena se změní na "%@". Člen obdrží novou pozvánku. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! Člen bude odstraněn ze skupiny - toto nelze vzít zpět! No comment provided by engineer. + + Members can add message reactions. + Členové skupin mohou přidávat reakce na zprávy. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + Členové skupiny mohou nevratně mazat odeslané zprávy. (24 hodin) + No comment provided by engineer. + + + Members can report messsages to moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + No comment provided by engineer. + + + Members can send direct messages. + Členové skupiny mohou posílat přímé zprávy. + No comment provided by engineer. + + + Members can send disappearing messages. + Členové skupiny mohou posílat mizící zprávy. + No comment provided by engineer. + + + Members can send files and media. + Členové skupiny mohou posílat soubory a média. + No comment provided by engineer. + + + Members can send voice messages. + Členové skupiny mohou posílat hlasové zprávy. + No comment provided by engineer. + + + Mention members 👋 + No comment provided by engineer. + + + Menus + No comment provided by engineer. + Message delivery error Chyba doručení zprávy @@ -3450,11 +4420,27 @@ This is your link for group %@! Potvrzení o doručení zprávy! No comment provided by engineer. + + Message delivery warning + item status text + Message draft Návrh zprávy No comment provided by engineer. + + Message forwarded + item status text + + + Message may be delivered later if member becomes active. + item status description + + + Message queue info + No comment provided by engineer. + Message reactions Reakce na zprávy @@ -3465,11 +4451,35 @@ This is your link for group %@! Reakce na zprávy jsou v tomto chatu zakázány. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. Reakce na zprávy jsou v této skupině zakázány. No comment provided by engineer. + + Message reception + No comment provided by engineer. + + + Message servers + No comment provided by engineer. + + + Message shape + No comment provided by engineer. + + + Message source remains private. + No comment provided by engineer. + + + Message status + No comment provided by engineer. + + + Message status: %@ + copied message info + Message text Text zprávy @@ -3493,6 +4503,22 @@ This is your link for group %@! Messages from %@ will be shown! No comment provided by engineer. + + Messages in this chat will never be deleted. + alert message + + + Messages received + No comment provided by engineer. + + + Messages sent + No comment provided by engineer. + + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. No comment provided by engineer. @@ -3549,9 +4575,9 @@ This is your link for group %@! Přenesení dokončeno No comment provided by engineer. - - Migrations: %@ - Migrace: %@ + + Migrations: + Migrace: No comment provided by engineer. @@ -3569,21 +4595,28 @@ This is your link for group %@! Upraveno v: %@ copied message info + + More + swipe action + More improvements are coming soon! Další vylepšení se chystají již brzy! No comment provided by engineer. + + More reliable network connection. + No comment provided by engineer. + + + More reliable notifications + No comment provided by engineer. + Most likely this connection is deleted. Pravděpodobně je toto spojení smazáno. item status description - - Most likely this contact has deleted the connection with you. - Tento kontakt s největší pravděpodobností smazal spojení s vámi. - No comment provided by engineer. - Multiple chat profiles Více chatovacích profilů @@ -3592,7 +4625,11 @@ This is your link for group %@! Mute Ztlumit - No comment provided by engineer. + notification label action + + + Mute all + notification label action Muted when inactive! @@ -3602,13 +4639,33 @@ This is your link for group %@! Name Jméno - No comment provided by engineer. + swipe action Network & servers Síť a servery No comment provided by engineer. + + Network connection + No comment provided by engineer. + + + Network decentralization + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + snd error text + + + Network management + No comment provided by engineer. + + + Network operator + No comment provided by engineer. + Network settings Nastavení sítě @@ -3619,15 +4676,31 @@ This is your link for group %@! Stav sítě No comment provided by engineer. + + New + token status text + New Passcode Nové heslo No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat No comment provided by engineer. + + New chat experience 🎉 + No comment provided by engineer. + New contact request Žádost o nový kontakt @@ -3638,11 +4711,6 @@ This is your link for group %@! Nový kontakt: notification - - New database archive - Archiv nové databáze - No comment provided by engineer. - New desktop app! Nová desktopová aplikace! @@ -3653,11 +4721,19 @@ This is your link for group %@! Nově zobrazované jméno No comment provided by engineer. + + New events + notification + New in %@ Nový V %@ No comment provided by engineer. + + New media options + No comment provided by engineer. + New member role Nová role člena @@ -3673,6 +4749,10 @@ This is your link for group %@! Nová přístupová fráze… No comment provided by engineer. + + New server + No comment provided by engineer. + No Ne @@ -3683,6 +4763,18 @@ This is your link for group %@! Žádné heslo aplikace Authentication unavailable + + No chats + No comment provided by engineer. + + + No chats found + No comment provided by engineer. + + + No chats in list %@ + No comment provided by engineer. + No contacts selected Nebyl vybrán žádný kontakt @@ -3703,6 +4795,10 @@ This is your link for group %@! Žádný token zařízení! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + item status description + No filtered chats Žádné filtrované chaty @@ -3718,20 +4814,94 @@ This is your link for group %@! Žádná historie No comment provided by engineer. + + No info, try to reload + No comment provided by engineer. + + + No media & file servers. + servers error + + + No message + No comment provided by engineer. + + + No message servers. + servers error + + + No network connection + No comment provided by engineer. + + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message Nemáte oprávnění nahrávat hlasové zprávy No comment provided by engineer. + + No push server + Místní + No comment provided by engineer. + No received or sent files Žádné přijaté ani odeslané soubory No comment provided by engineer. + + No servers for private message routing. + servers error + + + No servers to receive files. + servers error + + + No servers to receive messages. + servers error + + + No servers to send files. + servers error + + + No token! + alert title + + + No unread chats + No comment provided by engineer. + + + No user identifiers. + Bez uživatelských identifikátorů + No comment provided by engineer. + Not compatible! No comment provided by engineer. + + Notes + No comment provided by engineer. + + + Nothing selected + No comment provided by engineer. + + + Nothing to forward! + alert title + Notifications Oznámení @@ -3742,6 +4912,18 @@ This is your link for group %@! Oznámení jsou zakázána! No comment provided by engineer. + + Notifications error + alert title + + + Notifications privacy + No comment provided by engineer. + + + Notifications status + alert title + Now admins can: - delete members' messages. @@ -3758,36 +4940,35 @@ This is your link for group %@! Off Vypnout - No comment provided by engineer. + blur media Ok Ok - No comment provided by engineer. + alert button Old database Stará databáze No comment provided by engineer. - - Old database archive - Archiv staré databáze - No comment provided by engineer. - One-time invitation link Jednorázový zvací odkaz No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Pro připojení budou vyžadováni Onion hostitelé. Vyžaduje povolení sítě VPN. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Pro připojení budou vyžadováni Onion hostitelé. +Vyžaduje povolení sítě VPN. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Onion hostitelé budou použiti, pokud jsou k dispozici. Vyžaduje povolení sítě VPN. + + Onion hosts will be used when available. +Requires compatible VPN. + Onion hostitelé budou použiti, pokud jsou k dispozici. +Vyžaduje povolení sítě VPN. No comment provided by engineer. @@ -3795,11 +4976,19 @@ This is your link for group %@! Onion hostitelé nebudou použiti. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. Pouze klientská zařízení ukládají uživatelské profily, kontakty, skupiny a zprávy odeslané s **2vrstvým šifrováním typu end-to-end**. No comment provided by engineer. + + Only delete conversation + No comment provided by engineer. + Only group owners can change group preferences. Předvolby skupiny mohou měnit pouze vlastníci skupiny. @@ -3815,6 +5004,14 @@ This is your link for group %@! Pouze majitelé skupin mohou povolit zasílání hlasových zpráv. No comment provided by engineer. + + Only sender and moderators see it + No comment provided by engineer. + + + Only you and moderators see it + No comment provided by engineer. + Only you can add message reactions. Reakce na zprávy můžete přidávat pouze vy. @@ -3868,13 +5065,17 @@ This is your link for group %@! Open Otevřít - No comment provided by engineer. + alert action Open Settings Otevřít nastavení No comment provided by engineer. + + Open changes + No comment provided by engineer. + Open chat Otevřete chat @@ -3885,28 +5086,38 @@ This is your link for group %@! Otevřete konzolu chatu authentication reason + + Open conditions + No comment provided by engineer. + Open group No comment provided by engineer. + + Open link? + alert title + Open migration to another device authentication reason - - Open user profiles - Otevřít uživatelské profily - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Protokol a kód s otevřeným zdrojovým kódem - servery může provozovat kdokoli. - No comment provided by engineer. - Opening app… No comment provided by engineer. + + Operator + No comment provided by engineer. + + + Operator server + alert title + + + Or import archive file + No comment provided by engineer. + Or paste archive link No comment provided by engineer. @@ -3923,6 +5134,23 @@ This is your link for group %@! Or show this code No comment provided by engineer. + + Or to share privately + No comment provided by engineer. + + + Organize chats into lists + No comment provided by engineer. + + + Other + No comment provided by engineer. + + + Other file errors: +%@ + alert message + PING count Počet PING @@ -3958,6 +5186,10 @@ This is your link for group %@! Heslo nastaveno! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show Heslo k zobrazení @@ -3984,13 +5216,12 @@ This is your link for group %@! Paste the link you received No comment provided by engineer. - - People can connect to you only via the links you share. - Lidé se s vámi mohou spojit pouze prostřednictvím odkazů, které sdílíte. + + Pending No comment provided by engineer. - - Periodically + + Periodic Pravidelně No comment provided by engineer. @@ -4003,11 +5234,24 @@ This is your link for group %@! Picture-in-picture calls No comment provided by engineer. + + Play from the chat list. + No comment provided by engineer. + + + Please ask your contact to enable calls. + No comment provided by engineer. + Please ask your contact to enable sending voice messages. Prosím, požádejte kontaktní osobu, aby umožnila odesílání hlasových zpráv. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Zkontrolujte, zda jste použili správný odkaz, nebo požádejte kontakt, aby vám poslal jiný. @@ -4072,59 +5316,106 @@ Error: %@ Heslo uložte bezpečně, v případě jeho ztráty jej NEBUDE možné změnit. No comment provided by engineer. + + Please try to disable and re-enable notfications. + token info + + + Please wait for token activation to complete. + token info + + + Please wait for token to be registered. + token info + Polish interface Polské rozhraní No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Je možné, že otisk certifikátu v adrese serveru je nesprávný server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Zachování posledního návrhu zprávy s přílohami. No comment provided by engineer. - - Preset server - Přednastavený server - No comment provided by engineer. - Preset server address Přednastavená adresa serveru No comment provided by engineer. + + Preset servers + No comment provided by engineer. + Preview Náhled No comment provided by engineer. + + Previously connected servers + No comment provided by engineer. + Privacy & security Ochrana osobních údajů a zabezpečení No comment provided by engineer. + + Privacy for your customers. + No comment provided by engineer. + + + Privacy policy and conditions of use. + No comment provided by engineer. + Privacy redefined Nové vymezení soukromí No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + No comment provided by engineer. + Private filenames Soukromé názvy souborů No comment provided by engineer. + + Private media file names. + No comment provided by engineer. + + + Private message routing + No comment provided by engineer. + + + Private message routing 🚀 + No comment provided by engineer. + Private notes name of notes to self + + Private routing + No comment provided by engineer. + + + Private routing error + No comment provided by engineer. + Profile and server connections Profil a připojení k serveru @@ -4135,12 +5426,8 @@ Error: %@ Profilový obrázek No comment provided by engineer. - - Profile name - No comment provided by engineer. - - - Profile name: + + Profile images No comment provided by engineer. @@ -4148,10 +5435,14 @@ Error: %@ Heslo profilu No comment provided by engineer. + + Profile theme + No comment provided by engineer. + Profile update will be sent to your contacts. Aktualizace profilu bude zaslána vašim kontaktům. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4173,6 +5464,14 @@ Error: %@ Zakázat reakce na zprávy. No comment provided by engineer. + + Prohibit reporting messages to moderators. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + No comment provided by engineer. + Prohibit sending direct messages to members. Zakázat odesílání přímých zpráv členům. @@ -4193,11 +5492,20 @@ Error: %@ Zakázat odesílání hlasových zpráv. No comment provided by engineer. + + Protect IP address + No comment provided by engineer. + Protect app screen Ochrana obrazovky aplikace No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + No comment provided by engineer. + Protect your chat profiles with a password! Chraňte své chat profily heslem! @@ -4213,6 +5521,18 @@ Error: %@ Časový limit protokolu na KB No comment provided by engineer. + + Proxied + No comment provided by engineer. + + + Proxied servers + No comment provided by engineer. + + + Proxy requires password + No comment provided by engineer. + Push notifications Nabízená oznámení @@ -4231,6 +5551,10 @@ Error: %@ Ohodnoťte aplikaci No comment provided by engineer. + + Reachable chat toolbar + No comment provided by engineer. + React… Reagovat… @@ -4239,32 +5563,27 @@ Error: %@ Read Číst - No comment provided by engineer. + swipe action Read more Přečíst více No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Další informace naleznete v [Uživatelské příručce](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Další informace naleznete v [Uživatelské příručce](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). Přečtěte si více v [Uživatelské příručce](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - - Read more in our GitHub repository. - Další informace najdete v našem repozitáři GitHub. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). Přečtěte si více v našem [GitHub repozitáři](https://github.com/simplex-chat/simplex-chat#readme). @@ -4275,6 +5594,10 @@ Error: %@ Informace o dodání jsou zakázány No comment provided by engineer. + + Receive errors + No comment provided by engineer. + Received at Přijato v @@ -4295,6 +5618,18 @@ Error: %@ Přijatá zpráva message info title + + Received messages + No comment provided by engineer. + + + Received reply + No comment provided by engineer. + + + Received total + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. Přijímací adresa bude změněna na jiný server. Změna adresy bude dokončena po připojení odesílatele. @@ -4314,16 +5649,40 @@ Error: %@ Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + No comment provided by engineer. + Recipients see updates as you type them. Příjemci uvidí aktualizace během jejich psaní. No comment provided by engineer. + + Reconnect + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Znovu připojte všechny připojené servery a vynuťte doručení zprávy. Využívá další provoz. No comment provided by engineer. + + Reconnect all servers + No comment provided by engineer. + + + Reconnect all servers? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + No comment provided by engineer. + + + Reconnect server? + No comment provided by engineer. + Reconnect servers? Znovu připojit servery? @@ -4344,10 +5703,23 @@ Error: %@ Snížení spotřeby baterie No comment provided by engineer. + + Register + No comment provided by engineer. + + + Register notification token? + token info + + + Registered + token status text + Reject Odmítnout - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4374,6 +5746,14 @@ Error: %@ Odstranit No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + + + Remove image + No comment provided by engineer. + Remove member Odstranit člena @@ -4429,6 +5809,46 @@ Error: %@ Odpověď chat item action + + Report + chat item action + + + Report content: only group moderators will see it. + report reason + + + Report member profile: only group moderators will see it. + report reason + + + Report other: only group moderators will see it. + report reason + + + Report reason? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + report reason + + + Report violation: only group moderators will see it. + report reason + + + Report: %@ + report in notification + + + Reporting messages to moderators is prohibited. + No comment provided by engineer. + + + Reports + No comment provided by engineer. + Required Povinné @@ -4439,16 +5859,36 @@ Error: %@ Obnovit No comment provided by engineer. + + Reset all hints + No comment provided by engineer. + + + Reset all statistics + No comment provided by engineer. + + + Reset all statistics? + No comment provided by engineer. + Reset colors Obnovení barev No comment provided by engineer. + + Reset to app theme + No comment provided by engineer. + Reset to defaults Obnovení výchozího nastavení No comment provided by engineer. + + Reset to user theme + No comment provided by engineer. + Restart the app to create a new chat profile Restartujte aplikaci pro vytvoření nového chat profilu @@ -4488,9 +5928,8 @@ Error: %@ Odhalit chat item action - - Revert - Vrátit + + Review conditions No comment provided by engineer. @@ -4518,9 +5957,16 @@ Error: %@ Spustit chat No comment provided by engineer. - - SMP servers - SMP servery + + SMP server + No comment provided by engineer. + + + SOCKS proxy + No comment provided by engineer. + + + Safely receive files No comment provided by engineer. @@ -4530,43 +5976,42 @@ Error: %@ Save Uložit - chat item action + alert button +chat item action Save (and notify contacts) Uložit (a informovat kontakty) - No comment provided by engineer. + alert button Save and notify contact Uložit a upozornit kontakt - No comment provided by engineer. + alert button Save and notify group members Uložit a upozornit členy skupiny No comment provided by engineer. + + Save and reconnect + No comment provided by engineer. + Save and update group profile Uložit a aktualizovat profil skupiny No comment provided by engineer. - - Save archive - Uložit archiv - No comment provided by engineer. - - - Save auto-accept settings - Uložit nastavení automatického přijímání - No comment provided by engineer. - Save group profile Uložení profilu skupiny No comment provided by engineer. + + Save list + No comment provided by engineer. + Save passphrase and open chat Uložte heslo a otevřete chat @@ -4580,7 +6025,7 @@ Error: %@ Save preferences? Uložit předvolby? - No comment provided by engineer. + alert title Save profile password @@ -4595,27 +6040,46 @@ Error: %@ Save servers? Uložit servery? - No comment provided by engineer. - - - Save settings? - Uložit nastavení? - No comment provided by engineer. + alert title Save welcome message? Uložit uvítací zprávu? No comment provided by engineer. + + Save your profile? + alert title + + + Saved + No comment provided by engineer. + Saved WebRTC ICE servers will be removed Uložené servery WebRTC ICE budou odstraněny No comment provided by engineer. + + Saved from + No comment provided by engineer. + Saved message message info title + + Saving %lld messages + No comment provided by engineer. + + + Scale + No comment provided by engineer. + + + Scan / Paste link + No comment provided by engineer. + Scan QR code Skenovat QR kód @@ -4653,11 +6117,19 @@ Error: %@ Search or paste SimpleX link No comment provided by engineer. + + Secondary + No comment provided by engineer. + Secure queue Zabezpečit frontu server test step + + Secured + No comment provided by engineer. + Security assessment Posouzení bezpečnosti @@ -4671,6 +6143,18 @@ Error: %@ Select Vybrat + chat item action + + + Select chat profile + No comment provided by engineer. + + + Selected %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. No comment provided by engineer. @@ -4708,11 +6192,6 @@ Error: %@ Potvrzení o doručení zasílat na No comment provided by engineer. - - Send direct message - Odeslat přímou zprávu - No comment provided by engineer. - Send direct message to connect Odeslat přímou zprávu pro připojení @@ -4723,6 +6202,10 @@ Error: %@ Poslat mizící zprávu No comment provided by engineer. + + Send errors + No comment provided by engineer. + Send link previews Odesílání náhledů odkazů @@ -4733,14 +6216,25 @@ Error: %@ Odeslat živou zprávu No comment provided by engineer. + + Send message to enable calls. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + No comment provided by engineer. + Send notifications Odeslat oznámení No comment provided by engineer. - - Send notifications: - Odeslat oznámení: + + Send private reports No comment provided by engineer. @@ -4765,7 +6259,7 @@ Error: %@ Sender cancelled file transfer. Odesílatel zrušil přenos souboru. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4822,6 +6316,10 @@ Error: %@ Posláno v: % @ copied message info + + Sent directly + No comment provided by engineer. + Sent file event Odeslaná událost souboru @@ -4832,11 +6330,59 @@ Error: %@ Poslaná zpráva message info title + + Sent messages + No comment provided by engineer. + Sent messages will be deleted after set time. Odeslané zprávy se po uplynutí nastavené doby odstraní. No comment provided by engineer. + + Sent reply + No comment provided by engineer. + + + Sent total + No comment provided by engineer. + + + Sent via proxy + No comment provided by engineer. + + + Server + No comment provided by engineer. + + + Server added to operator %@. + alert message + + + Server address + No comment provided by engineer. + + + Server address is incompatible with network settings. + srv error text. + + + Server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Server operator changed. + alert title + + + Server operators + No comment provided by engineer. + + + Server protocol changed. + alert title + Server requires authorization to create queues, check password Server vyžaduje autorizaci pro vytváření front, zkontrolujte heslo @@ -4852,11 +6398,31 @@ Error: %@ Test serveru se nezdařil! No comment provided by engineer. + + Server type + No comment provided by engineer. + + + Server version is incompatible with network settings. + srv error text + + + Server version is incompatible with your app: %@. + No comment provided by engineer. + Servers Servery No comment provided by engineer. + + Servers info + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + No comment provided by engineer. + Session code No comment provided by engineer. @@ -4866,11 +6432,19 @@ Error: %@ Nastavit 1 den No comment provided by engineer. + + Set chat name… + No comment provided by engineer. + Set contact name… Nastavení jména kontaktu… No comment provided by engineer. + + Set default theme + No comment provided by engineer. + Set group preferences Nastavení skupinových předvoleb @@ -4881,6 +6455,10 @@ Error: %@ Nastavte jej namísto ověřování systému. No comment provided by engineer. + + Set message expiration in chats. + No comment provided by engineer. + Set passcode Nastavit heslo @@ -4910,24 +6488,49 @@ Error: %@ Nastavení No comment provided by engineer. + + Settings were changed. + alert message + + + Shape profile images + No comment provided by engineer. + Share Sdílet - chat item action + alert action +chat item action Share 1-time link Sdílet jednorázovou pozvánku No comment provided by engineer. + + Share 1-time link with a friend + No comment provided by engineer. + + + Share SimpleX address on social media. + No comment provided by engineer. + Share address Sdílet adresu No comment provided by engineer. + + Share address publicly + No comment provided by engineer. + Share address with contacts? Sdílet adresu s kontakty? + alert title + + + Share from other apps. No comment provided by engineer. @@ -4935,15 +6538,27 @@ Error: %@ Sdílet odkaz No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link No comment provided by engineer. + + Share to SimpleX + No comment provided by engineer. + Share with contacts Sdílet s kontakty No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code No comment provided by engineer. @@ -4963,21 +6578,41 @@ Error: %@ Zobrazit poslední zprávy No comment provided by engineer. + + Show message status + No comment provided by engineer. + + + Show percentage + No comment provided by engineer. + Show preview Zobrazení náhledu No comment provided by engineer. + + Show → on messages sent via private routing. + No comment provided by engineer. + Show: Zobrazit: No comment provided by engineer. + + SimpleX + No comment provided by engineer. + SimpleX Address SimpleX Adresa No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. Zabezpečení SimpleX chatu bylo auditováno společností Trail of Bits. @@ -5008,6 +6643,18 @@ Error: %@ Adresa SimpleX No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + No comment provided by engineer. + + + SimpleX address or 1-time link? + No comment provided by engineer. + + + SimpleX channel link + simplex link type + SimpleX contact address SimpleX kontaktní adresa @@ -5026,6 +6673,14 @@ Error: %@ SimpleX links Odkazy na SimpleX + chat feature + + + SimpleX links are prohibited. + No comment provided by engineer. + + + SimpleX links not allowed No comment provided by engineer. @@ -5033,11 +6688,19 @@ Error: %@ Jednorázová pozvánka SimpleX simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Zjednodušený inkognito režim No comment provided by engineer. + + Size + No comment provided by engineer. + Skip Přeskočit @@ -5053,16 +6716,46 @@ Error: %@ Malé skupiny (max. 20) No comment provided by engineer. + + Soft + blur media + + + Some app settings were not migrated. + No comment provided by engineer. + + + Some file(s) were not exported: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. Během importu došlo k nezávažným chybám - podrobnosti naleznete v chat konzoli. No comment provided by engineer. + + Some non-fatal errors occurred during import: + No comment provided by engineer. + + + Some servers failed the test: +%@ + alert message + Somebody Někdo notification title + + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + No comment provided by engineer. + Start chat Začít chat @@ -5077,6 +6770,14 @@ Error: %@ Zahájit přenesení No comment provided by engineer. + + Starting from %@. + No comment provided by engineer. + + + Statistics + No comment provided by engineer. + Stop Zastavit @@ -5091,11 +6792,6 @@ Error: %@ Stop chat No comment provided by engineer. - - Stop chat to enable database actions - Zastavte chat pro povolení akcí databáze - No comment provided by engineer. - Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. Zastavení chatu pro export, import nebo smazání databáze chatu. Během zastavení chatu nebudete moci přijímat a odesílat zprávy. @@ -5124,27 +6820,55 @@ Error: %@ Stop sharing Přestat sdílet - No comment provided by engineer. + alert action Stop sharing address? Přestat sdílet adresu? - No comment provided by engineer. + alert title Stopping chat No comment provided by engineer. + + Storage + No comment provided by engineer. + + + Strong + blur media + Submit Odeslat No comment provided by engineer. + + Subscribed + No comment provided by engineer. + + + Subscription errors + No comment provided by engineer. + + + Subscriptions ignored + No comment provided by engineer. + Support SimpleX Chat Podpořte SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System Systém @@ -5155,11 +6879,19 @@ Error: %@ Ověření systému No comment provided by engineer. + + TCP connection + No comment provided by engineer. + TCP connection timeout Časový limit připojení TCP No comment provided by engineer. + + TCP port for messaging + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5175,11 +6907,19 @@ Error: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture Vyfotit No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + No comment provided by engineer. + Tap button Klepněte na tlačítko @@ -5212,16 +6952,19 @@ Error: %@ Tap to scan No comment provided by engineer. - - Tap to start a new chat - Klepnutím na zahájíte nový chat - No comment provided by engineer. + + Temporary file error + file error alert title Test failed at step %@. Test selhal v kroku %@. server test failure + + Test notifications + No comment provided by engineer. + Test server Testovací server @@ -5235,7 +6978,7 @@ Error: %@ Tests failed! Testy selhaly! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5252,11 +6995,6 @@ Error: %@ Díky uživatelům - přispívejte prostřednictvím Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - 1. Platforma bez identifikátorů uživatelů - soukromá už od záměru. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5269,6 +7007,14 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován Aplikace vás může upozornit na přijaté zprávy nebo žádosti o kontakt - povolte to v nastavení. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + No comment provided by engineer. + The attempt to change database passphrase was not completed. Pokus o změnu přístupové fráze databáze nebyl dokončen. @@ -5278,6 +7024,10 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován The code you scanned is not a SimpleX link QR code. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + No comment provided by engineer. + The connection you accepted will be cancelled! Připojení, které jste přijali, bude zrušeno! @@ -5298,6 +7048,11 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován Šifrování funguje a nové povolení šifrování není vyžadováno. To může vyvolat chybu v připojení! No comment provided by engineer. + + The future of messaging + Nová generace soukromých zpráv + No comment provided by engineer. + The hash of the previous message is different. Hash předchozí zprávy se liší. @@ -5313,9 +7068,12 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován Zpráva bude pro všechny členy označena jako moderovaná. No comment provided by engineer. - - The next generation of private messaging - Nová generace soukromých zpráv + + The messages will be deleted for all members. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. No comment provided by engineer. @@ -5323,9 +7081,12 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován Stará databáze nebyla během přenášení odstraněna, lze ji smazat. No comment provided by engineer. - - The profile is only shared with your contacts. - Profil je sdílen pouze s vašimi kontakty. + + The same conditions will apply to operator **%@**. + No comment provided by engineer. + + + The second preset operator in the app! No comment provided by engineer. @@ -5343,13 +7104,24 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován Servery pro nová připojení vašeho aktuálního chat profilu **%@**. No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. No comment provided by engineer. - - Theme - Téma + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + + + Themes + No comment provided by engineer. + + + These conditions will also apply for: **%@**. No comment provided by engineer. @@ -5372,6 +7144,10 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován Tuto akci nelze vzít zpět - zprávy odeslané a přijaté dříve, než bylo zvoleno, budou smazány. Může to trvat několik minut. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. Tuto akci nelze vzít zpět - váš profil, kontakty, zprávy a soubory budou nenávratně ztraceny. @@ -5411,11 +7187,27 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován This is your own one-time link! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + No comment provided by engineer. + + + This message was deleted or not received yet. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. Toto nastavení platí pro zprávy ve vašem aktuálním chat profilu **%@**. No comment provided by engineer. + + Title + No comment provided by engineer. + To ask any questions and to receive updates: Chcete-li položit jakékoli dotazy a dostávat aktuality: @@ -5435,9 +7227,8 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován Vytvoření nového připojení No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Pro ochranu soukromí namísto ID uživatelů používaných všemi ostatními platformami má SimpleX identifikátory pro fronty zpráv, oddělené pro každý z vašich kontaktů. + + To protect against your link being replaced, you can compare contact security codes. No comment provided by engineer. @@ -5445,6 +7236,10 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován K ochraně časového pásma používají obrazové/hlasové soubory UTC. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5452,6 +7247,23 @@ You will be prompted to complete authentication before this feature is enabled.< Před zapnutím této funkce budete vyzváni k dokončení ověření. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Pro ochranu soukromí namísto ID uživatelů používaných všemi ostatními platformami má SimpleX identifikátory pro fronty zpráv, oddělené pro každý z vašich kontaktů. + No comment provided by engineer. + + + To receive + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Chcete-li nahrávat hlasové zprávy, udělte povolení k použití mikrofonu. @@ -5462,26 +7274,54 @@ Před zapnutím této funkce budete vyzváni k dokončení ověření. Chcete-li odhalit svůj skrytý profil, zadejte celé heslo do vyhledávacího pole na stránce **Chat profily**. No comment provided by engineer. + + To send + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. Pro podporu doručování okamžitých upozornění musí být přenesena chat databáze. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. Chcete-li ověřit koncové šifrování u svého kontaktu, porovnejte (nebo naskenujte) kód na svých zařízeních. No comment provided by engineer. + + Toggle chat list: + No comment provided by engineer. + Toggle incognito when connecting. Změnit inkognito režim při připojení. No comment provided by engineer. + + Token status: %@. + token status + + + Toolbar opacity + No comment provided by engineer. + + + Total + No comment provided by engineer. + Transport isolation Izolace transportu No comment provided by engineer. + + Transport sessions + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Pokus o připojení k serveru používanému k přijímání zpráv od tohoto kontaktu (chyba: %@). @@ -5531,10 +7371,9 @@ Před zapnutím této funkce budete vyzváni k dokončení ověření. Unblock member? No comment provided by engineer. - - Unexpected error: %@ - Neočekávaná chyba: %@ - item status description + + Undelivered messages + No comment provided by engineer. Unexpected migration state @@ -5544,7 +7383,7 @@ Před zapnutím této funkce budete vyzváni k dokončení ověření. Unfav. Odobl. - No comment provided by engineer. + swipe action Unhide @@ -5581,6 +7420,10 @@ Před zapnutím této funkce budete vyzváni k dokončení ověření. Neznámá chyba No comment provided by engineer. + + Unknown servers! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. Při nepoužívání rozhraní volání iOS, povolte režim Nerušit, abyste se vyhnuli vyrušování. @@ -5614,11 +7457,15 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Unmute Zrušit ztlumení - No comment provided by engineer. + notification label action Unread Nepřečtený + swipe action + + + Unsupported connection link No comment provided by engineer. @@ -5630,11 +7477,6 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Aktualizovat No comment provided by engineer. - - Update .onion hosts setting? - Aktualizovat nastavení hostitelů .onion? - No comment provided by engineer. - Update database passphrase Aktualizovat přístupovou frázi databáze @@ -5645,9 +7487,12 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Aktualizovat nastavení sítě? No comment provided by engineer. - - Update transport isolation mode? - Aktualizovat režim dopravní izolace? + + Update settings? + No comment provided by engineer. + + + Updated conditions No comment provided by engineer. @@ -5655,16 +7500,15 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Aktualizací nastavení se klient znovu připojí ke všem serverům. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - Aktualizace tohoto nastavení znovu připojí klienta ke všem serverům. - No comment provided by engineer. - Upgrade and open chat Zvýšit a otevřít chat No comment provided by engineer. + + Upload errors + No comment provided by engineer. + Upload failed No comment provided by engineer. @@ -5674,20 +7518,44 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Nahrát soubor server test step + + Uploaded + No comment provided by engineer. + + + Uploaded files + No comment provided by engineer. + Uploading archive No comment provided by engineer. + + Use %@ + No comment provided by engineer. + Use .onion hosts Použít hostitele .onion No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? Používat servery SimpleX Chat? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat Použijte chat @@ -5698,6 +7566,14 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Použít aktuální profil No comment provided by engineer. + + Use for files + No comment provided by engineer. + + + Use for messages + No comment provided by engineer. + Use for new connections Použít pro nová připojení @@ -5721,23 +7597,45 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Use only local notifications? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + No comment provided by engineer. + + + Use private routing with unknown servers. + No comment provided by engineer. + Use server Použít server No comment provided by engineer. + + Use servers + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. No comment provided by engineer. - - User profile - Profil uživatele + + Use the app with one hand. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - Použití hostitelů .onion vyžaduje kompatibilního poskytovatele VPN. + + Use web port + No comment provided by engineer. + + + User selection + No comment provided by engineer. + + + Username No comment provided by engineer. @@ -5804,11 +7702,19 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Videa a soubory až do velikosti 1 gb No comment provided by engineer. + + View conditions + No comment provided by engineer. + View security code Zobrazení bezpečnostního kódu No comment provided by engineer. + + View updated conditions + No comment provided by engineer. + Visible history chat feature @@ -5823,11 +7729,15 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Hlasové zprávy jsou v tomto chatu zakázány. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. Hlasové zprávy jsou v této skupině zakázány. No comment provided by engineer. + + Voice messages not allowed + No comment provided by engineer. + Voice messages prohibited! Hlasové zprávy jsou zakázány! @@ -5857,6 +7767,14 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Čekám na video No comment provided by engineer. + + Wallpaper accent + No comment provided by engineer. + + + Wallpaper background + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures No comment provided by engineer. @@ -5895,9 +7813,12 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Když je k dispozici No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Když někdo požádá o připojení, můžete žádost přijmout nebo odmítnout. + + When connecting audio and video calls. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. No comment provided by engineer. @@ -5905,6 +7826,18 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Pokud s někým sdílíte inkognito profil, bude tento profil použit pro skupiny, do kterých vás pozve. No comment provided by engineer. + + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + No comment provided by engineer. + + + Wired ethernet + No comment provided by engineer. + With encrypted files and media. No comment provided by engineer. @@ -5918,24 +7851,34 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu With reduced battery usage. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + alert message + Wrong database passphrase Špatná přístupová fráze k databázi No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + file error text + Wrong passphrase! Špatná přístupová fráze! No comment provided by engineer. - - XFTP servers - XFTP servery - No comment provided by engineer. - - - You - Vy + + XFTP server No comment provided by engineer. @@ -5962,6 +7905,10 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Již jste připojeni k %@. No comment provided by engineer. + + You are already connected with %@. + No comment provided by engineer. + You are already connecting to %@. No comment provided by engineer. @@ -6001,11 +7948,23 @@ Repeat join request? Jste pozváni do skupiny No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. Můžete přijímat hovory z obrazovky zámku, bez ověření zařízení a aplikace. No comment provided by engineer. + + You can change it in Appearance settings. + No comment provided by engineer. + + + You can configure servers via settings. + No comment provided by engineer. + You can create it later Můžete vytvořit později @@ -6034,11 +7993,19 @@ Repeat join request? You can make it visible to your SimpleX contacts via Settings. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ Nyní můžete posílat zprávy %@ notification body + + You can send messages to %@ from Archived contacts. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + No comment provided by engineer. + You can set lock screen notification preview via settings. Náhled oznámení na zamykací obrazovce můžete změnit v nastavení. @@ -6054,16 +8021,15 @@ Repeat join request? Tuto adresu můžete sdílet s vašimi kontakty, abyse se mohli spojit s **%@**. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - Můžete sdílet svou adresu jako odkaz nebo jako QR kód - kdokoli se k vám bude moci připojit. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app Chat můžete zahájit prostřednictvím aplikace Nastavení / Databáze nebo restartováním aplikace No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. Zámek SimpleX můžete zapnout v Nastavení. @@ -6076,23 +8042,23 @@ Repeat join request? You can view invitation link again in connection details. - No comment provided by engineer. + alert message You can't send messages! Nemůžete posílat zprávy! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Sami řídíte, přes který server(y) **přijímat** zprávy, své kontakty – servery, které používáte k odesílání zpráv. - No comment provided by engineer. - You could not be verified; please try again. Nemohli jste být ověřeni; Zkuste to prosím znovu. No comment provided by engineer. + + You decide who can connect. + Lidé se s vámi mohou spojit pouze prostřednictvím odkazu, který sdílíte. + No comment provided by engineer. + You have already requested connection via this address! No comment provided by engineer. @@ -6102,11 +8068,6 @@ Repeat join request? Repeat connection request? No comment provided by engineer. - - You have no chats - Nemáte žádné konverzace - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. Musíte zadat přístupovou frázi při každém spuštění aplikace - není uložena v zařízení. @@ -6127,11 +8088,23 @@ Repeat connection request? Připojili jste se k této skupině. Připojení k pozvání člena skupiny. No comment provided by engineer. + + You may migrate the exported database. + No comment provided by engineer. + + + You may save the exported archive. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. Nejnovější verzi databáze chatu musíte používat POUZE v jednom zařízení, jinak se může stát, že přestanete přijímat zprávy od některých kontaktů. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. Abyste mohli odesílat hlasové zprávy, musíte je povolit svému kontaktu. @@ -6147,6 +8120,10 @@ Repeat connection request? Odeslali jste pozvánku do skupiny No comment provided by engineer. + + You should receive notifications. + token info + You will be connected to group when the group host's device is online, please wait or check later! Ke skupině budete připojeni, až bude zařízení hostitele skupiny online, vyčkejte prosím nebo se podívejte později! @@ -6180,6 +8157,10 @@ Repeat connection request? Stále budete přijímat volání a upozornění od umlčených profilů pokud budou aktivní. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Přestanete dostávat zprávy z této skupiny. Historie chatu bude zachována. @@ -6200,31 +8181,16 @@ Repeat connection request? Pro tuto skupinu používáte inkognito profil - abyste zabránili sdílení svého hlavního profilu, není pozvání kontaktů povoleno No comment provided by engineer. - - Your %@ servers - Vaše servery %@ - No comment provided by engineer. - Your ICE servers Vaše servery ICE No comment provided by engineer. - - Your SMP servers - Vaše servery SMP - No comment provided by engineer. - Your SimpleX address Vaše SimpleX adresa No comment provided by engineer. - - Your XFTP servers - Vaše XFTP servery - No comment provided by engineer. - Your calls Vaše hovory @@ -6240,16 +8206,17 @@ Repeat connection request? Vaše chat databáze není šifrována – nastavte přístupovou frázi pro její šifrování. No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles Vaše chat profily No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - K dokončení připojení, musí být váš kontakt online. -Toto připojení můžete zrušit a kontakt odebrat (a zkusit to později s novým odkazem). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. No comment provided by engineer. @@ -6267,6 +8234,10 @@ Toto připojení můžete zrušit a kontakt odebrat (a zkusit to později s nov Vaše kontakty zůstanou připojeny. No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Vaše aktuální chat databáze bude ODSTRANĚNA a NAHRAZENA importovanou. @@ -6296,33 +8267,34 @@ Toto připojení můžete zrušit a kontakt odebrat (a zkusit to později s nov Váš profil **%@** bude sdílen. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Váš profil je uložen ve vašem zařízení a sdílen pouze s vašimi kontakty. -Servery SimpleX nevidí váš profil. + + Your profile is stored on your device and only shared with your contacts. + Profil je sdílen pouze s vašimi kontakty. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Váš profil, kontakty a doručené zprávy jsou uloženy ve vašem zařízení. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Váš profil je uložen ve vašem zařízení a sdílen pouze s vašimi kontakty. Servery SimpleX nevidí váš profil. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your random profile Váš náhodný profil No comment provided by engineer. - - Your server - Váš server - No comment provided by engineer. - Your server address Adresa vašeho serveru No comment provided by engineer. + + Your servers + No comment provided by engineer. + Your settings Vaše nastavení @@ -6363,11 +8335,19 @@ Servery SimpleX nevidí váš profil. přijatý hovor call status + + accepted invitation + chat list item title + admin správce member role + + admins + feature role + agreeing encryption for %@… povoluji šifrování pro %@… @@ -6378,6 +8358,10 @@ Servery SimpleX nevidí váš profil. povoluji šifrování… chat item text + + all members + feature role + always vždy @@ -6387,6 +8371,14 @@ Servery SimpleX nevidí váš profil. and %lld other events No comment provided by engineer. + + archived report + No comment provided by engineer. + + + attempts + No comment provided by engineer. + audio call (not e2e encrypted) zvukový hovor (nešifrovaný e2e) @@ -6416,13 +8408,18 @@ Servery SimpleX nevidí váš profil. blocked by admin - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold tučně No comment provided by engineer. + + call + No comment provided by engineer. + call error chyba volání @@ -6526,7 +8523,7 @@ Servery SimpleX nevidí váš profil. connecting… připojení… - chat list item title + No comment provided by engineer. connection established @@ -6572,10 +8569,15 @@ Servery SimpleX nevidí váš profil. dní time unit + + decryption errors + No comment provided by engineer. + default (%@) výchozí (%@) - pref value + delete after time +pref value default (no) @@ -6621,6 +8623,10 @@ Servery SimpleX nevidí váš profil. duplicitní zpráva integrity error chat item + + duplicates + No comment provided by engineer. + e2e encrypted e2e šifrované @@ -6696,8 +8702,12 @@ Servery SimpleX nevidí váš profil. chyba No comment provided by engineer. - - event happened + + expired + No comment provided by engineer. + + + forwarded No comment provided by engineer. @@ -6725,6 +8735,10 @@ Servery SimpleX nevidí váš profil. Klíčenka pro iOS bude použita k bezpečnému uložení přístupové fráze po restartování aplikace nebo změně přístupové fráze – umožní příjem oznámení push. No comment provided by engineer. + + inactive + No comment provided by engineer. + incognito via contact address link inkognito přes odkaz na kontaktní adresu @@ -6765,6 +8779,10 @@ Servery SimpleX nevidí váš profil. pozvánka do skupiny %@ group name + + invite + No comment provided by engineer. + invited pozvánka @@ -6819,6 +8837,10 @@ Servery SimpleX nevidí váš profil. připojeno rcv group event chat item + + message + No comment provided by engineer. + message received zpráva přijata @@ -6844,6 +8866,10 @@ Servery SimpleX nevidí váš profil. moderovaný %@ marked deleted chat item preview text + + moderator + member role + months měsíců @@ -6852,7 +8878,7 @@ Servery SimpleX nevidí váš profil. never nikdy - No comment provided by engineer. + delete after time new message @@ -6883,8 +8909,8 @@ Servery SimpleX nevidí váš profil. off vypnuto enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -6901,16 +8927,36 @@ Servery SimpleX nevidí váš profil. zapnuto group pref value + + other + No comment provided by engineer. + + + other errors + No comment provided by engineer. + owner vlastník member role + + owners + feature role + peer-to-peer peer-to-peer No comment provided by engineer. + + pending + No comment provided by engineer. + + + pending approval + No comment provided by engineer. + quantum resistant e2e encryption chat item text @@ -6925,6 +8971,10 @@ Servery SimpleX nevidí váš profil. obdržel potvrzení… No comment provided by engineer. + + rejected + No comment provided by engineer. + rejected call odmítnutý hovor @@ -6953,6 +9003,22 @@ Servery SimpleX nevidí váš profil. odstranil vás rcv group event chat item + + requested to connect + chat list item title + + + saved + No comment provided by engineer. + + + saved from %@ + No comment provided by engineer. + + + search + No comment provided by engineer. + sec sek @@ -6978,6 +9044,12 @@ Servery SimpleX nevidí váš profil. odeslat přímou zprávu No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + queue info + set new contact address profile update event chat item @@ -7014,10 +9086,18 @@ Servery SimpleX nevidí váš profil. neznámý connection info + + unknown servers + No comment provided by engineer. + unknown status No comment provided by engineer. + + unprotected + No comment provided by engineer. + updated group profile aktualizoval profil skupiny @@ -7056,6 +9136,10 @@ Servery SimpleX nevidí váš profil. přes relé No comment provided by engineer. + + video + No comment provided by engineer. + video call (not e2e encrypted) videohovoru (nešifrovaného e2e) @@ -7081,11 +9165,19 @@ Servery SimpleX nevidí váš profil. týdnů time unit + + when IP hidden + No comment provided by engineer. + yes ano pref value + + you + No comment provided by engineer. + you are invited to group jste pozváni do skupiny @@ -7158,7 +9250,7 @@ Servery SimpleX nevidí váš profil.
- +
@@ -7194,7 +9286,7 @@ Servery SimpleX nevidí váš profil.
- +
@@ -7214,4 +9306,205 @@ Servery SimpleX nevidí váš profil.
+ +
+ +
+ + + %d new events + notification body + + + From %d chat(s) + notification body + + + From: %@ + notification body + + + New events + notification + + + New messages + notification + + +
+ +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + App is locked! + No comment provided by engineer. + + + Cancel + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Cannot forward message + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database encrypted! + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database passphrase is required to open chat. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Passphrase + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + No comment provided by engineer. + + + Sending a message takes longer than expected. + No comment provided by engineer. + + + Sending message… + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Slow network? + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wait + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/contents.json b/apps/ios/SimpleX Localizations/cs.xcloc/contents.json index 5c7c929ee3..9cd5922c24 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/cs.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "cs", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff index 6788b5e8e6..06fd7c5a1d 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (kann kopiert werden) @@ -109,6 +82,7 @@ %@ downloaded + %@ heruntergeladen No comment provided by engineer. @@ -126,13 +100,19 @@ %@ wurde erfolgreich überprüft No comment provided by engineer. + + %@ server + %@ Server + No comment provided by engineer. + %@ servers - %@-Server + %@ Server No comment provided by engineer. %@ uploaded + %@ hochgeladen No comment provided by engineer. @@ -140,6 +120,11 @@ %@ will sich mit Ihnen verbinden! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ und %lld Mitglieder @@ -160,11 +145,36 @@ %d Tage time interval + + %d file(s) are still being downloaded. + %d Datei(en) wird/werden immer noch heruntergeladen. + forward confirmation reason + + + %d file(s) failed to download. + Bei %d Datei(en) ist das Herunterladen fehlgeschlagen. + forward confirmation reason + + + %d file(s) were deleted. + %d Datei(en) wurde(n) gelöscht. + forward confirmation reason + + + %d file(s) were not downloaded. + %d Datei(en) wurde(n) nicht heruntergeladen. + forward confirmation reason + %d hours %d Stunden time interval + + %d messages not forwarded + %d Nachrichten wurden nicht weitergeleitet + alert title + %d min %d min @@ -180,6 +190,11 @@ %d s time interval + + %d seconds(s) + %d Sekunde(n) + delete after time + %d skipped message(s) %d übersprungene Nachricht(en) @@ -250,11 +265,6 @@ %lld neue Sprachen für die Bedienoberfläche No comment provided by engineer. - - %lld second(s) - %lld Sekunde(n) - No comment provided by engineer. - %lld seconds %lld Sekunden @@ -305,11 +315,6 @@ %u übersprungene Nachrichten. No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) (Neu) @@ -320,19 +325,9 @@ (Dieses Gerät hat v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - **Kontakt hinzufügen**: Um einen neuen Einladungslink zu erstellen oder eine Verbindung über einen Link herzustellen, den Sie erhalten haben. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Neuen Kontakt hinzufügen**: Um einen Einmal-QR-Code oder -Link für Ihren Kontakt zu erzeugen. + + **Create 1-time link**: to create and share a new invitation link. + **Kontakt hinzufügen**: Um einen neuen Einladungslink zu erstellen. No comment provided by engineer. @@ -340,18 +335,19 @@ **Gruppe erstellen**: Um eine neue Gruppe zu erstellen. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **Mehr Privatsphäre**: Es wird alle 20 Minuten auf neue Nachrichten geprüft. Nur Ihr Geräte-Token wird dem SimpleX-Chat-Server mitgeteilt, aber nicht wie viele Kontakte Sie haben oder welche Nachrichten Sie empfangen. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **Beste Privatsphäre**: Es wird kein SimpleX-Chat-Benachrichtigungs-Server genutzt, Nachrichten werden in periodischen Abständen im Hintergrund geprüft (dies hängt davon ab, wie häufig Sie die App nutzen). No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Bitte beachten Sie**: Aus Sicherheitsgründen wird die Nachrichtenentschlüsselung Ihrer Verbindungen abgebrochen, wenn Sie die gleiche Datenbank auf zwei Geräten nutzen. No comment provided by engineer. @@ -359,11 +355,16 @@ **Bitte beachten Sie**: Das Passwort kann NICHT wiederhergestellt oder geändert werden, wenn Sie es vergessen haben oder verlieren. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Empfohlen**: Nur Ihr Geräte-Token und ihre Benachrichtigungen werden an den SimpleX-Chat-Benachrichtigungs-Server gesendet, aber weder der Nachrichteninhalt noch deren Größe oder von wem sie gesendet wurde. No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + **Link scannen / einfügen**: Um eine Verbindung über den Link herzustellen, den Sie erhalten haben. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **Warnung**: Sofortige Push-Benachrichtigungen erfordern die Eingabe eines Passworts, welches in Ihrem Schlüsselbund gespeichert ist. @@ -371,6 +372,7 @@ **Warning**: the archive will be removed. + **Warnung**: Das Archiv wird gelöscht. No comment provided by engineer. @@ -388,11 +390,6 @@ \*fett* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -429,11 +426,6 @@ - Nachrichtenverlauf bearbeiten No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 sek @@ -446,8 +438,9 @@ 1 day - täglich - time interval + Älter als ein Tag + delete after time +time interval 1 hour @@ -461,13 +454,30 @@ 1 month - monatlich - time interval + Älter als ein Monat + delete after time +time interval 1 week - wöchentlich - time interval + Älter als eine Woche + delete after time +time interval + + + 1 year + Älter als ein Jahr + delete after time + + + 1-time link + Einmal-Link + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + Ein Einmal-Link kann *nur mit einem Kontakt* genutzt werden - teilen Sie in nur persönlich oder über einen beliebigen Messenger. + No comment provided by engineer. 5 minutes @@ -484,11 +494,6 @@ 30 Sekunden No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -525,22 +530,17 @@ Abort - Abbrechen + Beenden No comment provided by engineer. Abort changing address - Wechsel der Empfängeradresse abbrechen + Wechsel der Empfängeradresse beenden No comment provided by engineer. Abort changing address? - Wechsel der Empfängeradresse abbrechen? - No comment provided by engineer. - - - About SimpleX - Über SimpleX + Wechsel der Empfängeradresse beenden? No comment provided by engineer. @@ -548,21 +548,27 @@ Über SimpleX Chat No comment provided by engineer. - - About SimpleX address - Über die SimpleX-Adresse + + About operators + Über die Betreiber No comment provided by engineer. - - Accent color - Akzentfarbe + + Accent + Akzent No comment provided by engineer. Accept Annehmen accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Nutzungsbedingungen akzeptieren + No comment provided by engineer. Accept connection request? @@ -577,21 +583,47 @@ Accept incognito Inkognito akzeptieren - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + Akzeptierte Nutzungsbedingungen + No comment provided by engineer. + + + Acknowledged + Bestätigt + No comment provided by engineer. + + + Acknowledgement errors + Fehler bei der Bestätigung + No comment provided by engineer. + + + Active + Aktiv + token status text + + + Active connections + Aktive Verbindungen + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Fügen Sie die Adresse Ihrem Profil hinzu, damit Ihre Kontakte sie mit anderen Personen teilen können. Es wird eine Profilaktualisierung an Ihre Kontakte gesendet. No comment provided by engineer. - - Add contact - Kontakt hinzufügen + + Add friends + Freunde aufnehmen No comment provided by engineer. - - Add preset servers - Füge voreingestellte Server hinzu + + Add list + Liste hinzufügen No comment provided by engineer. @@ -599,14 +631,19 @@ Profil hinzufügen No comment provided by engineer. - - Add servers by scanning QR codes. - Fügen Sie Server durch Scannen der QR Codes hinzu. + + Add server + Server hinzufügen No comment provided by engineer. - - Add server… - Füge Server hinzu… + + Add servers by scanning QR codes. + Server durch Scannen von QR Codes hinzufügen. + No comment provided by engineer. + + + Add team members + Team-Mitglieder aufnehmen No comment provided by engineer. @@ -614,11 +651,46 @@ Einem anderen Gerät hinzufügen No comment provided by engineer. + + Add to list + Zur Liste hinzufügen + No comment provided by engineer. + Add welcome message Begrüßungsmeldung hinzufügen No comment provided by engineer. + + Add your team members to the conversations. + Nehmen Sie Team-Mitglieder in Ihre Unterhaltungen auf. + No comment provided by engineer. + + + Added media & file servers + Medien- und Dateiserver hinzugefügt + No comment provided by engineer. + + + Added message servers + Nachrichtenserver hinzugefügt + No comment provided by engineer. + + + Additional accent + Erste Akzentfarbe + No comment provided by engineer. + + + Additional accent 2 + Zusätzlicher Akzent 2 + No comment provided by engineer. + + + Additional secondary + Zweite Akzentfarbe + No comment provided by engineer. + Address Adresse @@ -626,11 +698,22 @@ Address change will be aborted. Old receiving address will be used. - Der Wechsel der Empfängeradresse wird abgebrochen. Die bisherige Adresse wird weiter verwendet. + Der Wechsel der Empfängeradresse wird beendet. Die bisherige Adresse wird weiter verwendet. + No comment provided by engineer. + + + Address or 1-time link? + Adress- oder Einmal-Link? + No comment provided by engineer. + + + Address settings + Adress-Einstellungen No comment provided by engineer. Admins can block a member for all. + Administratoren können ein Gruppenmitglied für Alle blockieren. No comment provided by engineer. @@ -643,6 +726,16 @@ Erweiterte Netzwerkeinstellungen No comment provided by engineer. + + Advanced settings + Erweiterte Einstellungen + No comment provided by engineer. + + + All + Alle + No comment provided by engineer. + All app data is deleted. Werden die App-Daten komplett gelöscht. @@ -650,27 +743,42 @@ All chats and messages will be deleted - this cannot be undone! - Alle Chats und Nachrichten werden gelöscht! Dies kann nicht rückgängig gemacht werden! + Es werden alle Chats und Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + Alle Chats werden von der Liste %@ entfernt und danach wird die Liste gelöscht. + alert message + All data is erased when it is entered. Alle Daten werden gelöscht, sobald dieser eingegeben wird. No comment provided by engineer. + + All data is kept private on your device. + Alle Daten werden nur auf Ihrem Gerät gespeichert. + No comment provided by engineer. + All group members will remain connected. Alle Gruppenmitglieder bleiben verbunden. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + Alle Nachrichten und Dateien werden **Ende-zu-Ende verschlüsselt** versendet - in Direkt-Nachrichten mit Post-Quantum-Security. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! - Es werden alle Nachrichten gelöscht. Dieser Vorgang kann nicht rückgängig gemacht werden! + Es werden alle Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden! No comment provided by engineer. All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. - Alle Nachrichten werden gelöscht - dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht. + Es werden alle Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht. No comment provided by engineer. @@ -678,6 +786,21 @@ Von %@ werden alle neuen Nachrichten ausgeblendet! No comment provided by engineer. + + All profiles + Alle Profile + profile dropdown + + + All reports will be archived for you. + Alle Meldungen werden für Sie archiviert. + No comment provided by engineer. + + + All servers + Alle Server + No comment provided by engineer. + All your contacts will remain connected. Alle Ihre Kontakte bleiben verbunden. @@ -690,6 +813,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Alle Ihre Kontakte, Unterhaltungen und Dateien werden sicher verschlüsselt und in Daten-Paketen auf die konfigurierten XTFP-Relais hochgeladen. No comment provided by engineer. @@ -702,11 +826,21 @@ Erlauben Sie Anrufe nur dann, wenn es Ihr Kontakt ebenfalls erlaubt. No comment provided by engineer. + + Allow calls? + Anrufe erlauben? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Erlauben Sie verschwindende Nachrichten nur dann, wenn es Ihr Kontakt ebenfalls erlaubt. No comment provided by engineer. + + Allow downgrade + Herabstufung erlauben + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Erlauben Sie das unwiederbringliche Löschen von Nachrichten nur dann, wenn es Ihnen Ihr Kontakt ebenfalls erlaubt. (24 Stunden) @@ -732,11 +866,26 @@ Das Senden von verschwindenden Nachrichten erlauben. No comment provided by engineer. + + Allow sharing + Teilen erlauben + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Unwiederbringliches löschen von gesendeten Nachrichten erlauben. (24 Stunden) No comment provided by engineer. + + Allow to report messsages to moderators. + Melden von Nachrichten an Moderatoren erlauben. + No comment provided by engineer. + + + Allow to send SimpleX links. + Das Senden von SimpleX-Links erlauben. + No comment provided by engineer. + Allow to send files and media. Das Senden von Dateien und Medien erlauben. @@ -797,6 +946,11 @@ Sie sind bereits Mitglied der Gruppe! No comment provided by engineer. + + Always use private routing. + Sie nutzen immer privates Routing. + No comment provided by engineer. + Always use relay Über ein Relais verbinden @@ -807,11 +961,21 @@ Es wurde ein leeres Chat-Profil mit dem eingegebenen Namen erstellt und die App öffnet wie gewohnt. No comment provided by engineer. + + Another reason + Anderer Grund + report reason + Answer call Anruf annehmen No comment provided by engineer. + + Anybody can host servers. + Jeder kann seine eigenen Server aufsetzen. + No comment provided by engineer. + App build: %@ App Build: %@ @@ -819,6 +983,7 @@ App data migration + App-Daten-Migration No comment provided by engineer. @@ -826,6 +991,11 @@ Neue lokale Dateien (außer Video-Dateien) werden von der App verschlüsselt. No comment provided by engineer. + + App group: + App-Gruppe: + No comment provided by engineer. + App icon App-Icon @@ -841,6 +1011,11 @@ App-Zugangscode wurde durch den Selbstzerstörungs-Zugangscode ersetzt. No comment provided by engineer. + + App session + App-Sitzung + No comment provided by engineer. + App version App Version @@ -858,14 +1033,62 @@ Apply + Anwenden + No comment provided by engineer. + + + Apply to + Anwenden auf + No comment provided by engineer. + + + Archive + Archiv + No comment provided by engineer. + + + Archive %lld reports? + Archiviere %lld Meldungen? + No comment provided by engineer. + + + Archive all reports? + Alle Meldungen archivieren? No comment provided by engineer. Archive and upload + Archivieren und Hochladen + No comment provided by engineer. + + + Archive contacts to chat later. + Kontakte für spätere Chats archivieren. + No comment provided by engineer. + + + Archive report + Meldung archivieren + No comment provided by engineer. + + + Archive report? + Meldung archivieren? + No comment provided by engineer. + + + Archive reports + Meldungen archivieren + swipe action + + + Archived contacts + Archivierte Kontakte No comment provided by engineer. Archiving database + Datenbank wird archiviert No comment provided by engineer. @@ -928,11 +1151,21 @@ Bilder automatisch akzeptieren No comment provided by engineer. + + Auto-accept settings + Einstellungen automatisch akzeptieren + alert title + Back Zurück No comment provided by engineer. + + Background + Hintergrund-Farbe + No comment provided by engineer. + Bad desktop address Falsche Desktop-Adresse @@ -948,16 +1181,61 @@ Ungültiger Nachrichten-Hash No comment provided by engineer. + + Better calls + Verbesserte Anrufe + No comment provided by engineer. + Better groups Bessere Gruppen No comment provided by engineer. + + Better groups performance + Bessere Leistung von Gruppen + No comment provided by engineer. + + + Better message dates. + Verbesserte Nachrichten-Datumsinformation + No comment provided by engineer. + Better messages Verbesserungen bei Nachrichten No comment provided by engineer. + + Better networking + Kontrollieren Sie Ihr Netzwerk + No comment provided by engineer. + + + Better notifications + Verbesserte Benachrichtigungen + No comment provided by engineer. + + + Better privacy and security + Bessere(r) Security und Datenschutz + No comment provided by engineer. + + + Better security ✅ + Verbesserte Sicherheit ✅ + No comment provided by engineer. + + + Better user experience + Verbesserte Nutzer-Erfahrung + No comment provided by engineer. + + + Black + Schwarz + No comment provided by engineer. + Block Blockieren @@ -993,6 +1271,16 @@ wurde vom Administrator blockiert No comment provided by engineer. + + Blur for better privacy. + Für bessere Privatsphäre verpixeln. + No comment provided by engineer. + + + Blur media + Medium verpixeln + No comment provided by engineer. + Both you and your contact can add message reactions. Sowohl Sie, als auch Ihr Kontakt können Reaktionen auf Nachrichten geben. @@ -1023,11 +1311,35 @@ Bulgarisch, Finnisch, Thailändisch und Ukrainisch - Dank der Nutzer und [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + Geschäftliche Adresse + No comment provided by engineer. + + + Business chats + Geschäftliche Chats + No comment provided by engineer. + + + Businesses + Unternehmen + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). Per Chat-Profil (Voreinstellung) oder [per Verbindung](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + Durch die Nutzung von SimpleX Chat erklären Sie sich damit einverstanden: +- nur legale Inhalte in öffentlichen Gruppen zu versenden. +- andere Nutzer zu respektieren - kein Spam. + No comment provided by engineer. + Call already ended! Anruf ist bereits beendet! @@ -1038,11 +1350,26 @@ Anrufe No comment provided by engineer. + + Calls prohibited! + Anrufe nicht zugelassen! + No comment provided by engineer. + Camera not available Kamera nicht verfügbar No comment provided by engineer. + + Can't call contact + Kontakt kann nicht angerufen werden + No comment provided by engineer. + + + Can't call member + Mitglied kann nicht angerufen werden + No comment provided by engineer. + Can't invite contact! Kontakt kann nicht eingeladen werden! @@ -1053,13 +1380,20 @@ Kontakte können nicht eingeladen werden! No comment provided by engineer. + + Can't message member + Mitglied kann nicht benachrichtigt werden + No comment provided by engineer. + Cancel Abbrechen - No comment provided by engineer. + alert action +alert button Cancel migration + Migration abbrechen No comment provided by engineer. @@ -1067,9 +1401,24 @@ Die App kann nicht auf den Schlüsselbund zugreifen, um das Datenbank-Passwort zu speichern No comment provided by engineer. + + Cannot forward message + Die Nachricht kann nicht weitergeleitet werden + No comment provided by engineer. + Cannot receive file Datei kann nicht empfangen werden + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Kapazität überschritten - der Empfänger hat die zuvor gesendeten Nachrichten nicht empfangen. + snd error text + + + Cellular + Mobilfunknetz No comment provided by engineer. @@ -1077,6 +1426,16 @@ Ändern No comment provided by engineer. + + Change automatic message deletion? + Automatisches Löschen von Nachrichten ändern? + alert title + + + Change chat profiles + Chat-Profile wechseln + authentication reason + Change database passphrase? Datenbank-Passwort ändern? @@ -1121,11 +1480,26 @@ Change self-destruct passcode Selbstzerstörungs-Zugangscode ändern authentication reason - set passcode view +set passcode view - - Chat archive - Datenbank Archiv + + Chat + Chat + No comment provided by engineer. + + + Chat already exists + Chat besteht bereits + No comment provided by engineer. + + + Chat already exists! + Chat besteht bereits! + No comment provided by engineer. + + + Chat colors + Chat-Farben No comment provided by engineer. @@ -1143,6 +1517,11 @@ Chat-Datenbank gelöscht No comment provided by engineer. + + Chat database exported + Chat-Datenbank wurde exportiert + No comment provided by engineer. + Chat database imported Chat-Datenbank importiert @@ -1163,8 +1542,14 @@ Der Chat ist angehalten. Wenn Sie diese Datenbank bereits auf einem anderen Gerät genutzt haben, sollten Sie diese vor dem Starten des Chats wieder zurückspielen. No comment provided by engineer. + + Chat list + Chat-Liste + No comment provided by engineer. + Chat migrated! + Chat wurde migriert! No comment provided by engineer. @@ -1172,15 +1557,50 @@ Chat-Präferenzen No comment provided by engineer. + + Chat preferences were changed. + Die Chat-Präferenzen wurden geändert. + alert message + + + Chat profile + Benutzerprofil + No comment provided by engineer. + + + Chat theme + Chat-Design + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + Der Chat wird für alle Mitglieder gelöscht. Dies kann nicht rückgängig gemacht werden! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + Der Chat wird für Sie gelöscht. Dies kann nicht rückgängig gemacht werden! + No comment provided by engineer. + Chats Chats No comment provided by engineer. + + Check messages every 20 min. + Alle 20min Nachrichten überprüfen. + No comment provided by engineer. + + + Check messages when allowed. + Wenn es erlaubt ist, Nachrichten überprüfen. + No comment provided by engineer. + Check server address and try again. Überprüfen Sie die Serveradresse und versuchen Sie es nochmal. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1189,6 +1609,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + Wählen Sie auf dem neuen Gerät _Von einem anderen Gerät migrieren_ und scannen Sie den QR-Code. No comment provided by engineer. @@ -1201,24 +1622,49 @@ Aus dem Fotoalbum auswählen No comment provided by engineer. + + Chunks deleted + Daten-Pakete gelöscht + No comment provided by engineer. + + + Chunks downloaded + Daten-Pakete heruntergeladen + No comment provided by engineer. + + + Chunks uploaded + Daten-Pakete hochgeladen + No comment provided by engineer. + Clear - Löschen - No comment provided by engineer. + Entfernen + swipe action Clear conversation - Chatinhalte löschen + Chat-Inhalte entfernen No comment provided by engineer. Clear conversation? - Unterhaltung löschen? + Chat-Inhalte entfernen? + No comment provided by engineer. + + + Clear group? + Gruppe entfernen? + No comment provided by engineer. + + + Clear or delete group? + Gruppe entfernen oder löschen? No comment provided by engineer. Clear private notes? - Private Notizen löschen? + Private Notizen entfernen? No comment provided by engineer. @@ -1226,11 +1672,21 @@ Überprüfung zurücknehmen No comment provided by engineer. - - Colors - Farben + + Color chats with the new themes. + Farbige Chats mit neuen Designs. No comment provided by engineer. + + Color mode + Farbvariante + No comment provided by engineer. + + + Community guidelines violation + Verstoß gegen die Gemeinschaftsrichtlinien + report reason + Compare file Datei vergleichen @@ -1241,11 +1697,56 @@ Vergleichen Sie die Sicherheitscodes mit Ihren Kontakten. No comment provided by engineer. + + Completed + Abgeschlossen + No comment provided by engineer. + + + Conditions accepted on: %@. + Die Nutzungsbedingungen wurden akzeptiert am: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + Die Nutzungsbedingungen der/des Betreiber(s) werden akzeptiert: **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + Die Nutzungsbedingungen der/des folgenden Betreiber(s) wurden schon akzeptiert: **%@**. + No comment provided by engineer. + + + Conditions of use + Nutzungsbedingungen + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + Die Nutzungsbedingungen der/des Betreiber(s) werden akzeptiert: **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + Die Nutzungsbedingungen werden akzeptiert am: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + Die Nutzungsbedingungen der aktivierten Betreiber werden automatisch akzeptiert am: %@. + No comment provided by engineer. + Configure ICE servers ICE-Server konfigurieren No comment provided by engineer. + + Configure server operators + Server-Betreiber konfigurieren + No comment provided by engineer. + Confirm Bestätigen @@ -1256,13 +1757,24 @@ Zugangscode bestätigen No comment provided by engineer. + + Confirm contact deletion? + Löschen des Kontakts bestätigen? + No comment provided by engineer. + Confirm database upgrades Datenbank-Aktualisierungen bestätigen No comment provided by engineer. + + Confirm files from unknown servers. + Dateien von unbekannten Servern bestätigen. + No comment provided by engineer. + Confirm network settings + Bestätigen Sie die Netzwerkeinstellungen No comment provided by engineer. @@ -1277,12 +1789,19 @@ Confirm that you remember database passphrase to migrate it. + Bitte bestätigen Sie für die Migration, dass Sie sich an Ihr Datenbank-Passwort erinnern. No comment provided by engineer. Confirm upload + Hochladen bestätigen No comment provided by engineer. + + Confirmed + Bestätigt + token status text + Connect Verbinden @@ -1303,6 +1822,11 @@ Mit dem Desktop verbinden No comment provided by engineer. + + Connect to your friends faster. + Schneller mit Ihren Freunden verbinden. + No comment provided by engineer. + Connect to yourself? Mit Ihnen selbst verbinden? @@ -1342,16 +1866,31 @@ Das ist Ihr eigener Einmal-Link! Mit %@ verbinden No comment provided by engineer. + + Connected + Verbunden + No comment provided by engineer. + Connected desktop Verbundener Desktop No comment provided by engineer. + + Connected servers + Verbundene Server + No comment provided by engineer. + Connected to desktop Mit dem Desktop verbunden No comment provided by engineer. + + Connecting + Verbinden + No comment provided by engineer. + Connecting to server… Mit dem Server verbinden… @@ -1362,6 +1901,11 @@ Das ist Ihr eigener Einmal-Link! Mit dem Server verbinden… (Fehler: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + Verbinde mit Kontakt, bitte warten oder später erneut überprüfen! + No comment provided by engineer. + Connecting to desktop Mit dem Desktop verbinden @@ -1372,6 +1916,16 @@ Das ist Ihr eigener Einmal-Link! Verbindung No comment provided by engineer. + + Connection and servers status. + Verbindungs- und Server-Status. + No comment provided by engineer. + + + Connection blocked + Verbindung blockiert + No comment provided by engineer. + Connection error Verbindungsfehler @@ -1382,11 +1936,38 @@ Das ist Ihr eigener Einmal-Link! Verbindungsfehler (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + Die Verbindung wurde vom Server-Betreiber blockiert: +%@ + No comment provided by engineer. + + + Connection not ready. + Verbindung noch nicht bereit. + No comment provided by engineer. + + + Connection notifications + Verbindungsbenachrichtigungen + No comment provided by engineer. + Connection request sent! Verbindungsanfrage wurde gesendet! No comment provided by engineer. + + Connection requires encryption renegotiation. + Die Verbindung erfordert eine Neuverhandlung der Verschlüsselung. + No comment provided by engineer. + + + Connection security + Verbindungs-Sicherheit + No comment provided by engineer. + Connection terminated Verbindung beendet @@ -1397,6 +1978,16 @@ Das ist Ihr eigener Einmal-Link! Verbindungszeitüberschreitung No comment provided by engineer. + + Connection with desktop stopped + Die Verbindung mit dem Desktop wurde gestoppt + No comment provided by engineer. + + + Connections + Verbindungen + No comment provided by engineer. + Contact allows Der Kontakt erlaubt @@ -1407,6 +1998,11 @@ Das ist Ihr eigener Einmal-Link! Der Kontakt ist bereits vorhanden No comment provided by engineer. + + Contact deleted! + Kontakt gelöscht! + No comment provided by engineer. + Contact hidden: Kontakt verborgen: @@ -1417,9 +2013,9 @@ Das ist Ihr eigener Einmal-Link! Mit Ihrem Kontakt verbunden notification - - Contact is not connected yet! - Ihr Kontakt ist noch nicht verbunden! + + Contact is deleted. + Kontakt wurde gelöscht. No comment provided by engineer. @@ -1432,6 +2028,11 @@ Das ist Ihr eigener Einmal-Link! Kontakt-Präferenzen No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + Kontakt wird gelöscht. Dies kann nicht rückgängig gemacht werden! + No comment provided by engineer. + Contacts Kontakte @@ -1442,21 +2043,41 @@ Das ist Ihr eigener Einmal-Link! Ihre Kontakte können Nachrichten zum Löschen markieren. Sie können diese Nachrichten trotzdem anschauen. No comment provided by engineer. + + Content violates conditions of use + Inhalt verletzt Nutzungsbedingungen + blocking reason + Continue Weiter No comment provided by engineer. + + Conversation deleted! + Chat-Inhalte entfernt! + No comment provided by engineer. + Copy Kopieren - chat item action + No comment provided by engineer. + + + Copy error + Fehlermeldung kopieren + No comment provided by engineer. Core version: v%@ Core Version: v%@ No comment provided by engineer. + + Corner + Abrundung Ecken + No comment provided by engineer. + Correct name to %@? Richtiger Name für %@? @@ -1467,6 +2088,11 @@ Das ist Ihr eigener Einmal-Link! Erstellen No comment provided by engineer. + + Create 1-time link + Einmal-Link erstellen + No comment provided by engineer. + Create SimpleX address SimpleX-Adresse erstellen @@ -1477,11 +2103,6 @@ Das ist Ihr eigener Einmal-Link! Erstellen Sie eine Gruppe mit einem zufälligen Profil. No comment provided by engineer. - - Create an address to let people connect with you. - Erstellen Sie eine Adresse, damit sich Personen mit Ihnen verbinden können. - No comment provided by engineer. - Create file Datei erstellen @@ -1502,6 +2123,11 @@ Das ist Ihr eigener Einmal-Link! Link erzeugen No comment provided by engineer. + + Create list + Liste erstellen + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 Neues Profil in der [Desktop-App] erstellen (https://simplex.chat/downloads/). 💻 @@ -1527,6 +2153,11 @@ Das ist Ihr eigener Einmal-Link! Erstellen Sie Ihr Profil No comment provided by engineer. + + Created + Erstellt + No comment provided by engineer. + Created at Erstellt um @@ -1537,13 +2168,9 @@ Das ist Ihr eigener Einmal-Link! Erstellt um: %@ copied message info - - Created on %@ - Erstellt am %@ - No comment provided by engineer. - Creating archive link + Archiv-Link erzeugen No comment provided by engineer. @@ -1556,11 +2183,21 @@ Das ist Ihr eigener Einmal-Link! Aktueller Zugangscode No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + Der Text der aktuellen Nutzungsbedingungen konnte nicht geladen werden. Sie können die Nutzungsbedingungen unter diesem Link einsehen: + No comment provided by engineer. + Current passphrase… Aktuelles Passwort… No comment provided by engineer. + + Current profile + Aktuelles Profil + No comment provided by engineer. + Currently maximum supported file size is %@. Die derzeit maximal unterstützte Dateigröße beträgt %@. @@ -1571,11 +2208,26 @@ Das ist Ihr eigener Einmal-Link! Zeit anpassen No comment provided by engineer. + + Customizable message shape. + Anpassbares Format des Nachrichtenfelds + No comment provided by engineer. + + + Customize theme + Design anpassen + No comment provided by engineer. + Dark Dunkel No comment provided by engineer. + + Dark mode colors + Farben für die dunkle Variante + No comment provided by engineer. + Database ID Datenbank-ID @@ -1674,6 +2326,11 @@ Das ist Ihr eigener Einmal-Link! Die Datenbank wird beim nächsten Start der App migriert No comment provided by engineer. + + Debug delivery + Debugging-Zustellung + No comment provided by engineer. + Decentralized Dezentral @@ -1687,18 +2344,19 @@ Das ist Ihr eigener Einmal-Link! Delete Löschen - chat item action + alert action +swipe action + + + Delete %lld messages of members? + %lld Nachrichten der Mitglieder löschen? + No comment provided by engineer. Delete %lld messages? %lld Nachrichten löschen? No comment provided by engineer. - - Delete Contact - Kontakt löschen - No comment provided by engineer. - Delete address Adresse löschen @@ -1724,14 +2382,14 @@ Das ist Ihr eigener Einmal-Link! Kontakt löschen und benachrichtigen No comment provided by engineer. - - Delete archive - Archiv löschen + + Delete chat + Chat löschen No comment provided by engineer. - - Delete chat archive? - Chat Archiv löschen? + + Delete chat messages from your device. + Chat-Nachrichten von Ihrem Gerät löschen. No comment provided by engineer. @@ -1744,6 +2402,11 @@ Das ist Ihr eigener Einmal-Link! Chat-Profil löschen? No comment provided by engineer. + + Delete chat? + Chat löschen? + No comment provided by engineer. + Delete connection Verbindung löschen @@ -1754,11 +2417,9 @@ Das ist Ihr eigener Einmal-Link! Kontakt löschen No comment provided by engineer. - - Delete contact? -This cannot be undone! - Kontakt löschen? -Das kann nicht rückgängig gemacht werden! + + Delete contact? + Kontakt löschen? No comment provided by engineer. @@ -1768,6 +2429,7 @@ Das kann nicht rückgängig gemacht werden! Delete database from this device + Datenbank auf diesem Gerät löschen No comment provided by engineer. @@ -1820,6 +2482,11 @@ Das kann nicht rückgängig gemacht werden! Link löschen? No comment provided by engineer. + + Delete list? + Liste löschen? + alert title + Delete member message? Nachricht des Mitglieds löschen? @@ -1833,11 +2500,11 @@ Das kann nicht rückgängig gemacht werden! Delete messages Nachrichten löschen - No comment provided by engineer. + alert button Delete messages after - Löschen der Nachrichten + Nachrichten löschen No comment provided by engineer. @@ -1850,14 +2517,14 @@ Das kann nicht rückgängig gemacht werden! Alte Datenbank löschen? No comment provided by engineer. - - Delete pending connection - Ausstehende Verbindung löschen + + Delete or moderate up to 200 messages. + Bis zu 200 Nachrichten löschen oder moderieren No comment provided by engineer. Delete pending connection? - Die ausstehende Verbindung löschen? + Ausstehende Verbindung löschen? No comment provided by engineer. @@ -1870,11 +2537,31 @@ Das kann nicht rückgängig gemacht werden! Lösche Warteschlange server test step + + Delete report + Meldung löschen + No comment provided by engineer. + + + Delete up to 20 messages at once. + Löschen Sie bis zu 20 Nachrichten auf einmal. + No comment provided by engineer. + Delete user profile? Benutzerprofil löschen? No comment provided by engineer. + + Delete without notification + Ohne Benachrichtigung löschen + No comment provided by engineer. + + + Deleted + Gelöscht + No comment provided by engineer. + Deleted at Gelöscht um @@ -1885,6 +2572,16 @@ Das kann nicht rückgängig gemacht werden! Gelöscht um: %@ copied message info + + Deletion errors + Fehler beim Löschen + No comment provided by engineer. + + + Delivered even when Apple drops them. + Auslieferung, selbst wenn Apple sie löscht. + No comment provided by engineer. + Delivery Zustellung @@ -1920,11 +2617,41 @@ Das kann nicht rückgängig gemacht werden! Desktop-Geräte No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + Adresse des Zielservers von %@ ist nicht kompatibel mit den Einstellungen des Weiterleitungsservers %@. + No comment provided by engineer. + + + Destination server error: %@ + Zielserver-Fehler: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + Die Version des Zielservers %@ ist nicht kompatibel mit dem Weiterleitungsserver %@. + No comment provided by engineer. + + + Detailed statistics + Detaillierte Statistiken + No comment provided by engineer. + + + Details + Details + No comment provided by engineer. + Develop Entwicklung No comment provided by engineer. + + Developer options + Optionen für Entwickler + No comment provided by engineer. + Developer tools Entwicklertools @@ -1955,8 +2682,13 @@ Das kann nicht rückgängig gemacht werden! Direkte Nachrichten chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + In diesem Chat sind Direktnachrichten zwischen Mitgliedern nicht erlaubt. + No comment provided by engineer. + + + Direct messages between members are prohibited. In dieser Gruppe sind Direktnachrichten zwischen Mitgliedern nicht erlaubt. No comment provided by engineer. @@ -1970,11 +2702,26 @@ Das kann nicht rückgängig gemacht werden! SimpleX-Sperre deaktivieren authentication reason + + Disable automatic message deletion? + Automatisches Löschen von Nachrichten deaktivieren? + alert title + + + Disable delete messages + Löschen von Nachrichten deaktivieren + alert button + Disable for all Für Alle deaktivieren No comment provided by engineer. + + Disabled + Deaktiviert + No comment provided by engineer. + Disappearing message Verschwindende Nachricht @@ -1990,8 +2737,8 @@ Das kann nicht rückgängig gemacht werden! In diesem Chat sind verschwindende Nachrichten nicht erlaubt. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. In dieser Gruppe sind verschwindende Nachrichten nicht erlaubt. No comment provided by engineer. @@ -2025,9 +2772,19 @@ Das kann nicht rückgängig gemacht werden! Lokales Netzwerk durchsuchen No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + Nachrichten werden nicht direkt versendet, selbst wenn Ihr oder der Zielserver kein privates Routing unterstützt. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. - Nutzen Sie SimpleX nicht für Notrufe. + SimpleX NICHT für Notrufe nutzen. + No comment provided by engineer. + + + Do NOT use private routing. + Sie nutzen KEIN privates Routing. No comment provided by engineer. @@ -2040,6 +2797,16 @@ Das kann nicht rückgängig gemacht werden! Den Nachrichtenverlauf nicht an neue Mitglieder senden. No comment provided by engineer. + + Do not use credentials with proxy. + Verwenden Sie keine Anmeldeinformationen mit einem Proxy. + No comment provided by engineer. + + + Documents: + Dokumente: + No comment provided by engineer. + Don't create address Keine Adresse erstellt @@ -2050,18 +2817,40 @@ Das kann nicht rückgängig gemacht werden! Nicht aktivieren No comment provided by engineer. + + Don't miss important messages. + Verpassen Sie keine wichtigen Nachrichten. + No comment provided by engineer. + Don't show again Nicht nochmals anzeigen No comment provided by engineer. + + Done + Fertig + No comment provided by engineer. + Downgrade and open chat Datenbank herabstufen und den Chat öffnen No comment provided by engineer. + + Download + Herunterladen + alert button +chat item action + + + Download errors + Fehler beim Herunterladen + No comment provided by engineer. + Download failed + Herunterladen fehlgeschlagen No comment provided by engineer. @@ -2069,12 +2858,29 @@ Das kann nicht rückgängig gemacht werden! Datei herunterladen server test step + + Download files + Dateien herunterladen + alert action + + + Downloaded + Heruntergeladen + No comment provided by engineer. + + + Downloaded files + Heruntergeladene Dateien + No comment provided by engineer. + Downloading archive + Archiv wird heruntergeladen No comment provided by engineer. Downloading link details + Link-Details werden heruntergeladen No comment provided by engineer. @@ -2087,6 +2893,11 @@ Das kann nicht rückgängig gemacht werden! Dauer No comment provided by engineer. + + E2E encrypted notifications. + E2E-verschlüsselte Benachrichtigungen. + No comment provided by engineer. + Edit Bearbeiten @@ -2107,6 +2918,11 @@ Das kann nicht rückgängig gemacht werden! Aktivieren (vorgenommene Einstellungen bleiben erhalten) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + Für einen besseren Metadatenschutz Flux in den Netzwerk- und Servereinstellungen aktivieren. + No comment provided by engineer. + Enable SimpleX Lock SimpleX-Sperre aktivieren @@ -2120,7 +2936,7 @@ Das kann nicht rückgängig gemacht werden! Enable automatic message deletion? Automatisches Löschen von Nachrichten aktivieren? - No comment provided by engineer. + alert title Enable camera access @@ -2134,6 +2950,7 @@ Das kann nicht rückgängig gemacht werden! Enable in direct chats (BETA)! + Kann in direkten Chats aktiviert werden (BETA)! No comment provided by engineer. @@ -2166,6 +2983,16 @@ Das kann nicht rückgängig gemacht werden! Selbstzerstörungs-Zugangscode aktivieren set passcode view + + Enabled + Aktiviert + No comment provided by engineer. + + + Enabled for + Aktiviert für + No comment provided by engineer. + Encrypt Verschlüsseln @@ -2236,6 +3063,11 @@ Das kann nicht rückgängig gemacht werden! Neuverhandlung der Verschlüsselung fehlgeschlagen. No comment provided by engineer. + + Encryption renegotiation in progress. + Die Neuverhandlung der Verschlüsselung läuft. + No comment provided by engineer. + Enter Passcode Zugangscode eingeben @@ -2253,6 +3085,7 @@ Das kann nicht rückgängig gemacht werden! Enter passphrase + Passwort eingeben No comment provided by engineer. @@ -2297,33 +3130,39 @@ Das kann nicht rückgängig gemacht werden! Error aborting address change - Fehler beim Abbrechen des Adresswechsels + Fehler beim Beenden des Adresswechsels No comment provided by engineer. + + Error accepting conditions + Fehler beim Akzeptieren der Nutzungsbedingungen + alert title + Error accepting contact request Fehler beim Annehmen der Kontaktanfrage No comment provided by engineer. - - Error accessing database file - Fehler beim Zugriff auf die Datenbankdatei - No comment provided by engineer. - Error adding member(s) Fehler beim Hinzufügen von Mitgliedern No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + Fehler beim Hinzufügen des Servers + alert title Error changing address Fehler beim Wechseln der Empfängeradresse No comment provided by engineer. + + Error changing connection profile + Fehler beim Wechseln des Verbindungs-Profils + No comment provided by engineer. + Error changing role Fehler beim Ändern der Rolle @@ -2334,6 +3173,21 @@ Das kann nicht rückgängig gemacht werden! Fehler beim Ändern der Einstellung No comment provided by engineer. + + Error changing to incognito! + Fehler beim Wechseln zum Inkognito-Profil! + No comment provided by engineer. + + + Error checking token status + Fehler beim Überprüfen des Token-Status + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + Fehler beim Verbinden mit dem Weiterleitungsserver %@. Bitte versuchen Sie es später erneut. + No comment provided by engineer. + Error creating address Fehler beim Erstellen der Adresse @@ -2349,6 +3203,11 @@ Das kann nicht rückgängig gemacht werden! Fehler beim Erzeugen des Gruppen-Links No comment provided by engineer. + + Error creating list + Fehler beim Erstellen der Liste + alert title + Error creating member contact Fehler beim Anlegen eines Mitglied-Kontaktes @@ -2364,6 +3223,11 @@ Das kann nicht rückgängig gemacht werden! Fehler beim Erstellen des Profils! No comment provided by engineer. + + Error creating report + Fehler beim Erstellen der Meldung + No comment provided by engineer. + Error decrypting file Fehler beim Entschlüsseln der Datei @@ -2384,11 +3248,6 @@ Das kann nicht rückgängig gemacht werden! Fehler beim Löschen der Verbindung No comment provided by engineer. - - Error deleting contact - Fehler beim Löschen des Kontakts - No comment provided by engineer. - Error deleting database Fehler beim Löschen der Datenbank @@ -2411,6 +3270,7 @@ Das kann nicht rückgängig gemacht werden! Error downloading the archive + Fehler beim Herunterladen des Archivs No comment provided by engineer. @@ -2433,6 +3293,11 @@ Das kann nicht rückgängig gemacht werden! Fehler beim Exportieren der Chat-Datenbank No comment provided by engineer. + + Error exporting theme: %@ + Fehler beim Exportieren des Designs: %@ + No comment provided by engineer. + Error importing chat database Fehler beim Importieren der Chat-Datenbank @@ -2443,9 +3308,14 @@ Das kann nicht rückgängig gemacht werden! Fehler beim Beitritt zur Gruppe No comment provided by engineer. - - Error loading %@ servers - Fehler beim Laden von %@ Servern + + Error loading servers + Fehler beim Laden der Server + alert title + + + Error migrating settings + Fehler beim Migrieren der Einstellungen No comment provided by engineer. @@ -2455,17 +3325,37 @@ Das kann nicht rückgängig gemacht werden! Error receiving file - Fehler beim Empfangen der Datei + Fehler beim Herunterladen der Datei + alert title + + + Error reconnecting server + Fehler beim Wiederherstellen der Verbindung zum Server No comment provided by engineer. + + Error reconnecting servers + Fehler beim Wiederherstellen der Verbindungen zu den Servern + No comment provided by engineer. + + + Error registering for notifications + Fehler beim Registrieren für Benachrichtigungen + alert title + Error removing member Fehler beim Entfernen des Mitglieds No comment provided by engineer. - - Error saving %@ servers - Fehler beim Speichern der %@-Server + + Error reordering lists + Fehler beim Umsortieren der Listen + alert title + + + Error resetting statistics + Fehler beim Zurücksetzen der Statistiken No comment provided by engineer. @@ -2473,6 +3363,11 @@ Das kann nicht rückgängig gemacht werden! Fehler beim Speichern der ICE-Server No comment provided by engineer. + + Error saving chat list + Fehler beim Speichern der Chat-Liste + alert title + Error saving group profile Fehler beim Speichern des Gruppenprofils @@ -2488,8 +3383,14 @@ Das kann nicht rückgängig gemacht werden! Fehler beim Speichern des Passworts in den Schlüsselbund No comment provided by engineer. + + Error saving servers + Fehler beim Speichern der Server + alert title + Error saving settings + Fehler beim Abspeichern der Einstellungen when migrating @@ -2532,16 +3433,26 @@ Das kann nicht rückgängig gemacht werden! Fehler beim Beenden des Chats No comment provided by engineer. + + Error switching profile + Fehler beim Wechseln des Profils + No comment provided by engineer. + Error switching profile! Fehler beim Umschalten des Profils! - No comment provided by engineer. + alertTitle Error synchronizing connection Fehler beim Synchronisieren der Verbindung No comment provided by engineer. + + Error testing server connection + Fehler beim Testen der Server-Verbindung + No comment provided by engineer. + Error updating group link Fehler beim Aktualisieren des Gruppen-Links @@ -2552,6 +3463,11 @@ Das kann nicht rückgängig gemacht werden! Fehler beim Aktualisieren der Nachricht No comment provided by engineer. + + Error updating server + Fehler beim Aktualisieren des Servers + alert title + Error updating settings Fehler beim Aktualisieren der Einstellungen @@ -2564,10 +3480,12 @@ Das kann nicht rückgängig gemacht werden! Error uploading the archive + Fehler beim Hochladen des Archivs No comment provided by engineer. Error verifying passphrase: + Fehler bei der Überprüfung des Passworts: No comment provided by engineer. @@ -2578,7 +3496,9 @@ Das kann nicht rückgängig gemacht werden! Error: %@ Fehler: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2590,6 +3510,16 @@ Das kann nicht rückgängig gemacht werden! Fehler: Keine Datenbankdatei No comment provided by engineer. + + Errors + Fehler + No comment provided by engineer. + + + Errors in servers configuration. + Fehler in der Server-Konfiguration. + servers error + Even when disabled in the conversation. Auch wenn sie im Chat deaktiviert sind. @@ -2605,6 +3535,11 @@ Das kann nicht rückgängig gemacht werden! Erweitern chat item action + + Expired + Abgelaufen + token status text + Export database Datenbank exportieren @@ -2615,6 +3550,11 @@ Das kann nicht rückgängig gemacht werden! Fehler beim Export: No comment provided by engineer. + + Export theme + Design exportieren + No comment provided by engineer. + Exported database archive. Exportiertes Datenbankarchiv. @@ -2622,6 +3562,7 @@ Das kann nicht rückgängig gemacht werden! Exported file doesn't exist + Die exportierte Datei ist nicht vorhanden No comment provided by engineer. @@ -2639,16 +3580,70 @@ Das kann nicht rückgängig gemacht werden! Schnell und ohne warten auf den Absender, bis er online ist! No comment provided by engineer. + + Faster deletion of groups. + Schnelleres löschen von Gruppen. + No comment provided by engineer. + Faster joining and more reliable messages. Schnellerer Gruppenbeitritt und zuverlässigere Nachrichtenzustellung. No comment provided by engineer. + + Faster sending messages. + Schnelleres versenden von Nachrichten. + No comment provided by engineer. + Favorite Favorit + swipe action + + + Favorites + Favoriten No comment provided by engineer. + + File error + Datei-Fehler + file error alert title + + + File errors: +%@ + Datei-Fehler: +%@ + alert message + + + File is blocked by server operator: +%@. + Datei wurde vom Server-Betreiber blockiert: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + Datei nicht gefunden - höchstwahrscheinlich wurde die Datei gelöscht oder der Transfer abgebrochen. + file error text + + + File server error: %@ + Datei-Server Fehler: %@ + file error text + + + File status + Datei-Status + No comment provided by engineer. + + + File status: %@ + Datei-Status: %@ + copied message info + File will be deleted from servers. Die Datei wird von den Servern gelöscht. @@ -2656,12 +3651,12 @@ Das kann nicht rückgängig gemacht werden! File will be received when your contact completes uploading it. - Die Datei wird empfangen, sobald das Hochladen durch ihren Kontakt abgeschlossen ist. + Die Datei wird heruntergeladen, sobald das Hochladen durch ihren Kontakt abgeschlossen ist. No comment provided by engineer. File will be received when your contact is online, please wait or check later! - Die Datei wird empfangen, sobald Ihr Kontakt online ist. Bitte warten oder schauen Sie später nochmal nach! + Die Datei wird heruntergeladen, sobald Ihr Kontakt online ist. Bitte warten oder schauen Sie später nochmal nach! No comment provided by engineer. @@ -2669,6 +3664,11 @@ Das kann nicht rückgängig gemacht werden! Datei: %@ No comment provided by engineer. + + Files + Dateien + No comment provided by engineer. + Files & media Dateien & Medien @@ -2679,11 +3679,16 @@ Das kann nicht rückgängig gemacht werden! Dateien und Medien chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. In dieser Gruppe sind Dateien und Medien nicht erlaubt. No comment provided by engineer. + + Files and media not allowed + Dateien und Medien sind nicht erlaubt + No comment provided by engineer. + Files and media prohibited! Dateien und Medien sind nicht erlaubt! @@ -2696,10 +3701,12 @@ Das kann nicht rückgängig gemacht werden! Finalize migration + Die Migration wird abgeschlossen No comment provided by engineer. Finalize migration on another device. + Die Migration auf dem anderen Gerät wird abgeschlossen. No comment provided by engineer. @@ -2742,11 +3749,115 @@ Das kann nicht rückgängig gemacht werden! Reparatur wird vom Gruppenmitglied nicht unterstützt No comment provided by engineer. + + For all moderators + Für alle Moderatoren + No comment provided by engineer. + + + For chat profile %@: + Für das Chat-Profil %@: + servers error + For console Für Konsole No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + Wenn Ihr Kontakt beispielsweise Nachrichten über einen SimpleX-Chatserver empfängt, wird Ihre App diese über einen der Server von Flux versenden. + No comment provided by engineer. + + + For me + Für mich + No comment provided by engineer. + + + For private routing + Für privates Routing + No comment provided by engineer. + + + For social media + Für soziale Medien + No comment provided by engineer. + + + Forward + Weiterleiten + chat item action + + + Forward %d message(s)? + %d Nachricht(en) weiterleiten? + alert title + + + Forward and save messages + Nachrichten weiterleiten und speichern + No comment provided by engineer. + + + Forward messages + Nachrichten weiterleiten + alert action + + + Forward messages without files? + Nachrichten ohne Dateien weiterleiten? + alert message + + + Forward up to 20 messages at once. + Bis zu 20 Nachrichten auf einmal weiterleiten + No comment provided by engineer. + + + Forwarded + Weitergeleitet + No comment provided by engineer. + + + Forwarded from + Weitergeleitet aus + No comment provided by engineer. + + + Forwarding %lld messages + %lld Nachricht(en) wird/werden weitergeleitet + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + Weiterleitungsserver %@ konnte sich nicht mit dem Zielserver %@ verbinden. Bitte versuchen Sie es später erneut. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + Adresse des Weiterleitungsservers ist nicht kompatibel mit den Netzwerkeinstellungen: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + Version des Weiterleitungsservers ist nicht kompatibel mit den Netzwerkeinstellungen: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + Weiterleitungsserver: %1$@ +Zielserver Fehler: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + Weiterleitungsserver: %1$@ +Fehler: %2$@ + snd error text + Found desktop Gefundener Desktop @@ -2767,11 +3878,6 @@ Das kann nicht rückgängig gemacht werden! Vollständiger Name (optional) No comment provided by engineer. - - Full name: - Vollständiger Name: - No comment provided by engineer. - Fully decentralized – visible only to members. Vollständig dezentralisiert – nur für Mitglieder sichtbar. @@ -2792,6 +3898,21 @@ Das kann nicht rückgängig gemacht werden! GIFs und Sticker No comment provided by engineer. + + Get notified when mentioned. + Bei Erwähnung benachrichtigt werden. + No comment provided by engineer. + + + Good afternoon! + Guten Nachmittag! + message preview + + + Good morning! + Guten Morgen! + message preview + Group Gruppe @@ -2847,36 +3968,6 @@ Das kann nicht rückgängig gemacht werden! Gruppen-Links No comment provided by engineer. - - Group members can add message reactions. - Gruppenmitglieder können eine Reaktion auf Nachrichten geben. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Gruppenmitglieder können gesendete Nachrichten unwiederbringlich löschen. (24 Stunden) - No comment provided by engineer. - - - Group members can send direct messages. - Gruppenmitglieder können Direktnachrichten versenden. - No comment provided by engineer. - - - Group members can send disappearing messages. - Gruppenmitglieder können verschwindende Nachrichten senden. - No comment provided by engineer. - - - Group members can send files and media. - Gruppenmitglieder können Dateien und Medien senden. - No comment provided by engineer. - - - Group members can send voice messages. - Gruppenmitglieder können Sprachnachrichten versenden. - No comment provided by engineer. - Group message: Grppennachricht: @@ -2909,12 +4000,17 @@ Das kann nicht rückgängig gemacht werden! Group will be deleted for all members - this cannot be undone! - Die Gruppe wird für alle Mitglieder gelöscht - dies kann nicht rückgängig gemacht werden! + Die Gruppe wird für alle Mitglieder gelöscht. Dies kann nicht rückgängig gemacht werden! No comment provided by engineer. Group will be deleted for you - this cannot be undone! - Die Gruppe wird für Sie gelöscht - dies kann nicht rückgängig gemacht werden! + Die Gruppe wird nur bei Ihnen gelöscht. Dies kann nicht rückgängig gemacht werden! + No comment provided by engineer. + + + Groups + Gruppen No comment provided by engineer. @@ -2922,6 +4018,11 @@ Das kann nicht rückgängig gemacht werden! Hilfe No comment provided by engineer. + + Help admins moderating their groups. + Helfen Sie Administratoren bei der Moderation ihrer Gruppen. + No comment provided by engineer. + Hidden Verborgen @@ -2972,10 +4073,20 @@ Das kann nicht rückgängig gemacht werden! Wie SimpleX funktioniert No comment provided by engineer. + + How it affects privacy + Wie es die Privatsphäre beeinflusst + No comment provided by engineer. + + + How it helps privacy + Wie es die Privatsphäre schützt + No comment provided by engineer. + How it works Wie es funktioniert - No comment provided by engineer. + alert button How to @@ -2994,6 +4105,7 @@ Das kann nicht rückgängig gemacht werden! Hungarian interface + Ungarische Bedienoberfläche No comment provided by engineer. @@ -3001,6 +4113,11 @@ Das kann nicht rückgängig gemacht werden! ICE-Server (einer pro Zeile) No comment provided by engineer. + + IP address + IP-Adresse + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Falls Sie sich nicht persönlich treffen können, zeigen Sie den QR-Code in einem Videoanruf oder teilen Sie den Link. @@ -3028,12 +4145,12 @@ Das kann nicht rückgängig gemacht werden! Image will be received when your contact completes uploading it. - Das Bild wird empfangen, sobald das Hochladen durch ihren Kontakt abgeschlossen ist. + Das Bild wird heruntergeladen, sobald das Hochladen durch ihren Kontakt abgeschlossen ist. No comment provided by engineer. Image will be received when your contact is online, please wait or check later! - Das Bild wird empfangen, sobald Ihr Kontakt online ist. Bitte warten oder schauen Sie später nochmal nach! + Das Bild wird heruntergeladen, sobald Ihr Kontakt online ist. Bitte warten oder schauen Sie später nochmal nach! No comment provided by engineer. @@ -3041,8 +4158,8 @@ Das kann nicht rückgängig gemacht werden! Sofort No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Immun gegen Spam und Missbrauch No comment provided by engineer. @@ -3063,10 +4180,24 @@ Das kann nicht rückgängig gemacht werden! Import failed + Import ist fehlgeschlagen + No comment provided by engineer. + + + Import theme + Design importieren No comment provided by engineer. Importing archive + Archiv wird importiert + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Verbesserte Nachrichten-Auslieferung und verringerter Datenverbrauch. +Weitere Verbesserungen sind bald verfügbar! No comment provided by engineer. @@ -3086,6 +4217,7 @@ Das kann nicht rückgängig gemacht werden! In order to continue, chat should be stopped. + Um fortzufahren, sollte der Chat beendet werden. No comment provided by engineer. @@ -3093,6 +4225,21 @@ Das kann nicht rückgängig gemacht werden! Als Antwort auf No comment provided by engineer. + + In-call sounds + Klingeltöne + No comment provided by engineer. + + + Inappropriate content + Unangemessener Inhalt + report reason + + + Inappropriate profile + Unangemessenes Profil + report reason + Incognito Inkognito @@ -3130,12 +4277,12 @@ Das kann nicht rückgängig gemacht werden! Incompatible database version - Inkompatible Datenbank-Version + Datenbank-Version nicht kompatibel No comment provided by engineer. Incompatible version - Inkompatible Version + Version nicht kompatibel No comment provided by engineer. @@ -3163,6 +4310,11 @@ Das kann nicht rückgängig gemacht werden! Installieren Sie [SimpleX Chat als Terminalanwendung](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Sofort + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3170,16 +4322,41 @@ Das kann nicht rückgängig gemacht werden! No comment provided by engineer. - - Instantly - Sofort - No comment provided by engineer. - Interface Schnittstelle No comment provided by engineer. + + Interface colors + Interface-Farben + No comment provided by engineer. + + + Invalid + Ungültig + token status text + + + Invalid (bad token) + Ungültig (falsches Token) + token status text + + + Invalid (expired) + Ungültig (abgelaufen) + token status text + + + Invalid (unregistered) + Ungültig (nicht registriert) + token status text + + + Invalid (wrong topic) + Ungültig (falsches Thema) + token status text + Invalid QR code Ungültiger QR-Code @@ -3202,6 +4379,7 @@ Das kann nicht rückgängig gemacht werden! Invalid migration confirmation + Migrations-Bestätigung ungültig No comment provided by engineer. @@ -3217,7 +4395,7 @@ Das kann nicht rückgängig gemacht werden! Invalid server address! Ungültige Serveradresse! - No comment provided by engineer. + alert title Invalid status @@ -3239,6 +4417,11 @@ Das kann nicht rückgängig gemacht werden! Mitglieder einladen No comment provided by engineer. + + Invite to chat + Zum Chat einladen + No comment provided by engineer. + Invite to group In Gruppe einladen @@ -3254,8 +4437,8 @@ Das kann nicht rückgängig gemacht werden! In diesem Chat ist das unwiederbringliche Löschen von Nachrichten nicht erlaubt. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. In dieser Gruppe ist das unwiederbringliche Löschen von Nachrichten nicht erlaubt. No comment provided by engineer. @@ -3280,6 +4463,11 @@ Das kann nicht rückgängig gemacht werden! 3. Die Verbindung wurde kompromittiert. No comment provided by engineer. + + It protects your IP address and connections. + Ihre IP-Adresse und Verbindungen werden geschützt. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Es sieht so aus, als ob Sie bereits über diesen Link verbunden sind. Wenn das nicht der Fall ist, gab es einen Fehler (%@). @@ -3298,7 +4486,7 @@ Das kann nicht rückgängig gemacht werden! Join Beitreten - No comment provided by engineer. + swipe action Join group @@ -3340,6 +4528,11 @@ Das ist Ihr Link für die Gruppe %@! Keep Behalten + alert action + + + Keep conversation + Chat-Inhalte beibehalten No comment provided by engineer. @@ -3350,7 +4543,7 @@ Das ist Ihr Link für die Gruppe %@! Keep unused invitation? Nicht genutzte Einladung behalten? - No comment provided by engineer. + alert title Keep your connections @@ -3385,6 +4578,16 @@ Das ist Ihr Link für die Gruppe %@! Leave Verlassen + swipe action + + + Leave chat + Chat verlassen + No comment provided by engineer. + + + Leave chat? + Chat verlassen? No comment provided by engineer. @@ -3427,6 +4630,21 @@ Das ist Ihr Link für die Gruppe %@! Verknüpfte Desktops No comment provided by engineer. + + List + Liste + swipe action + + + List name and emoji should be different for all lists. + Der Listenname und das Emoji sollen für alle Listen unterschiedlich sein. + No comment provided by engineer. + + + List name... + Listenname... + No comment provided by engineer. + Live message! Live Nachricht! @@ -3437,11 +4655,6 @@ Das ist Ihr Link für die Gruppe %@! Live Nachrichten No comment provided by engineer. - - Local - Lokal - No comment provided by engineer. - Local name Lokaler Name @@ -3462,11 +4675,6 @@ Das ist Ihr Link für die Gruppe %@! Sperr-Modus No comment provided by engineer. - - Make a private connection - Stellen Sie eine private Verbindung her - No comment provided by engineer. - Make one message disappear Eine verschwindende Nachricht verfassen @@ -3477,21 +4685,11 @@ Das ist Ihr Link für die Gruppe %@! Privates Profil erzeugen! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Stellen Sie sicher, dass die %@-Server-Adressen das richtige Format haben, zeilenweise getrennt und nicht doppelt vorhanden sind (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Stellen Sie sicher, dass die WebRTC ICE-Server Adressen das richtige Format haben, zeilenweise getrennt und nicht doppelt vorhanden sind. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Viele Menschen haben gefragt: *Wie kann SimpleX Nachrichten zustellen, wenn es keine Benutzerkennungen gibt?* - No comment provided by engineer. - Mark deleted for everyone Für Alle als gelöscht markieren @@ -3517,11 +4715,36 @@ Das ist Ihr Link für die Gruppe %@! Max. 30 Sekunden, sofort erhalten. No comment provided by engineer. + + Media & file servers + Medien- und Datei-Server + No comment provided by engineer. + + + Medium + Medium + blur media + Member Mitglied No comment provided by engineer. + + Member inactive + Mitglied inaktiv + item status text + + + Member reports + Mitglieder-Meldungen + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + Die Rolle des Mitglieds wird auf "%@" geändert. Alle Chat-Mitglieder werden darüber informiert. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. Die Mitgliederrolle wird auf "%@" geändert. Alle Mitglieder der Gruppe werden benachrichtigt. @@ -3532,9 +4755,64 @@ Das ist Ihr Link für die Gruppe %@! Die Mitgliederrolle wird auf "%@" geändert. Das Mitglied wird eine neue Einladung erhalten. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + Das Mitglied wird aus dem Chat entfernt. Dies kann nicht rückgängig gemacht werden! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! - Das Mitglied wird aus der Gruppe entfernt - dies kann nicht rückgängig gemacht werden! + Das Mitglied wird aus der Gruppe entfernt. Dies kann nicht rückgängig gemacht werden! + No comment provided by engineer. + + + Members can add message reactions. + Gruppenmitglieder können eine Reaktion auf Nachrichten geben. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + Gruppenmitglieder können gesendete Nachrichten unwiederbringlich löschen. (24 Stunden) + No comment provided by engineer. + + + Members can report messsages to moderators. + Mitglieder können Nachrichten an Moderatoren melden. + No comment provided by engineer. + + + Members can send SimpleX links. + Gruppenmitglieder können SimpleX-Links versenden. + No comment provided by engineer. + + + Members can send direct messages. + Gruppenmitglieder können Direktnachrichten versenden. + No comment provided by engineer. + + + Members can send disappearing messages. + Gruppenmitglieder können verschwindende Nachrichten versenden. + No comment provided by engineer. + + + Members can send files and media. + Gruppenmitglieder können Dateien und Medien versenden. + No comment provided by engineer. + + + Members can send voice messages. + Gruppenmitglieder können Sprachnachrichten versenden. + No comment provided by engineer. + + + Mention members 👋 + Erwähnung von Mitgliedern 👋 + No comment provided by engineer. + + + Menus + Menüs No comment provided by engineer. @@ -3547,11 +4825,31 @@ Das ist Ihr Link für die Gruppe %@! Empfangsbestätigungen für Nachrichten! No comment provided by engineer. + + Message delivery warning + Warnung bei der Nachrichtenzustellung + item status text + Message draft Nachrichtenentwurf No comment provided by engineer. + + Message forwarded + Nachricht weitergeleitet + item status text + + + Message may be delivered later if member becomes active. + Die Nachricht kann später zugestellt werden, wenn das Mitglied aktiv wird. + item status description + + + Message queue info + Nachrichten-Warteschlangen-Information + No comment provided by engineer. + Message reactions Reaktionen auf Nachrichten @@ -3562,11 +4860,41 @@ Das ist Ihr Link für die Gruppe %@! In diesem Chat sind Reaktionen auf Nachrichten nicht erlaubt. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. In dieser Gruppe sind Reaktionen auf Nachrichten nicht erlaubt. No comment provided by engineer. + + Message reception + Nachrichtenempfang + No comment provided by engineer. + + + Message servers + Nachrichten-Server + No comment provided by engineer. + + + Message shape + Nachrichten-Form + No comment provided by engineer. + + + Message source remains private. + Die Nachrichtenquelle bleibt privat. + No comment provided by engineer. + + + Message status + Nachrichten-Status + No comment provided by engineer. + + + Message status: %@ + Nachrichten-Status: %@ + copied message info + Message text Nachrichtentext @@ -3574,6 +4902,7 @@ Das ist Ihr Link für die Gruppe %@! Message too large + Die Nachricht ist zu lang No comment provided by engineer. @@ -3591,36 +4920,64 @@ Das ist Ihr Link für die Gruppe %@! Die Nachrichten von %@ werden angezeigt! No comment provided by engineer. + + Messages in this chat will never be deleted. + Nachrichten in diesem Chat werden nie gelöscht. + alert message + + + Messages received + Empfangene Nachrichten + No comment provided by engineer. + + + Messages sent + Gesendete Nachrichten + No comment provided by engineer. + + + Messages were deleted after you selected them. + Die Nachrichten wurden gelöscht, nachdem Sie sie ausgewählt hatten. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + Nachrichten, Dateien und Anrufe sind durch **Ende-zu-Ende-Verschlüsselung** mit Perfect Forward Secrecy, Abstreitbarkeit und Wiederherstellung nach einer Kompromittierung geschützt. No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + Nachrichten, Dateien und Anrufe sind durch **Quantum-resistente E2E-Verschlüsselung** mit Perfect Forward Secrecy, Abstreitbarkeit und Wiederherstellung nach einer Kompromittierung geschützt. No comment provided by engineer. Migrate device + Gerät migrieren No comment provided by engineer. Migrate from another device + Von einem anderen Gerät migrieren No comment provided by engineer. Migrate here + Hierher migrieren No comment provided by engineer. Migrate to another device + Auf ein anderes Gerät migrieren No comment provided by engineer. Migrate to another device via QR code. + Daten können über einen QR-Code auf ein anderes Gerät migriert werden. No comment provided by engineer. Migrating + Migrieren No comment provided by engineer. @@ -3630,6 +4987,7 @@ Das ist Ihr Link für die Gruppe %@! Migration complete + Migration abgeschlossen No comment provided by engineer. @@ -3647,9 +5005,9 @@ Das ist Ihr Link für die Gruppe %@! Die Migration wurde abgeschlossen No comment provided by engineer. - - Migrations: %@ - Migrationen: %@ + + Migrations: + Migrationen: No comment provided by engineer. @@ -3667,21 +5025,31 @@ Das ist Ihr Link für die Gruppe %@! Moderiert um: %@ copied message info + + More + Mehr + swipe action + More improvements are coming soon! Weitere Verbesserungen sind bald verfügbar! No comment provided by engineer. + + More reliable network connection. + Zuverlässigere Netzwerkverbindung. + No comment provided by engineer. + + + More reliable notifications + Zuverlässigere Benachrichtigungen + No comment provided by engineer. + Most likely this connection is deleted. Wahrscheinlich ist diese Verbindung gelöscht worden. item status description - - Most likely this contact has deleted the connection with you. - Dieser Kontakt hat sehr wahrscheinlich die Verbindung mit Ihnen gelöscht. - No comment provided by engineer. - Multiple chat profiles Mehrere Chat-Profile @@ -3690,7 +5058,12 @@ Das ist Ihr Link für die Gruppe %@! Mute Stummschalten - No comment provided by engineer. + notification label action + + + Mute all + Alle stummschalten + notification label action Muted when inactive! @@ -3700,13 +5073,38 @@ Das ist Ihr Link für die Gruppe %@! Name Name - No comment provided by engineer. + swipe action Network & servers Netzwerk & Server No comment provided by engineer. + + Network connection + Netzwerkverbindung + No comment provided by engineer. + + + Network decentralization + Dezentralisiertes Netzwerk + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + Netzwerk-Fehler - die Nachricht ist nach vielen Sende-Versuchen abgelaufen. + snd error text + + + Network management + Netzwerk-Verwaltung + No comment provided by engineer. + + + Network operator + Netzwerk-Betreiber + No comment provided by engineer. + Network settings Netzwerkeinstellungen @@ -3717,16 +5115,36 @@ Das ist Ihr Link für die Gruppe %@! Netzwerkstatus No comment provided by engineer. + + New + Neu + token status text + New Passcode Neuer Zugangscode No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Jedes Mal wenn Sie die App starten, werden neue SOCKS-Anmeldeinformationen genutzt + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Für jeden Server werden neue SOCKS-Anmeldeinformationen genutzt + No comment provided by engineer. + New chat Neuer Chat No comment provided by engineer. + + New chat experience 🎉 + Neue Chat-Erfahrung 🎉 + No comment provided by engineer. + New contact request Neue Kontaktanfrage @@ -3737,11 +5155,6 @@ Das ist Ihr Link für die Gruppe %@! Neuer Kontakt: notification - - New database archive - Neues Datenbankarchiv - No comment provided by engineer. - New desktop app! Neue Desktop-App! @@ -3752,11 +5165,21 @@ Das ist Ihr Link für die Gruppe %@! Neuer Anzeigename No comment provided by engineer. + + New events + Neue Ereignisse + notification + New in %@ Neu in %@ No comment provided by engineer. + + New media options + Neue Medien-Optionen + No comment provided by engineer. + New member role Neue Mitgliedsrolle @@ -3772,6 +5195,11 @@ Das ist Ihr Link für die Gruppe %@! Neues Passwort… No comment provided by engineer. + + New server + Neuer Server + No comment provided by engineer. + No Nein @@ -3782,6 +5210,21 @@ Das ist Ihr Link für die Gruppe %@! Kein App-Passwort Authentication unavailable + + No chats + Keine Chats + No comment provided by engineer. + + + No chats found + Keine Chats gefunden + No comment provided by engineer. + + + No chats in list %@ + Keine Chats in der Liste %@ + No comment provided by engineer. + No contacts selected Keine Kontakte ausgewählt @@ -3802,6 +5245,11 @@ Das ist Ihr Link für die Gruppe %@! Kein Geräte-Token! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + Bisher keine direkte Verbindung. Nachricht wird von einem Admin weitergeleitet. + item status description + No filtered chats Keine gefilterten Chats @@ -3817,14 +5265,89 @@ Das ist Ihr Link für die Gruppe %@! Kein Nachrichtenverlauf No comment provided by engineer. + + No info, try to reload + Keine Information - es wird versucht neu zu laden + No comment provided by engineer. + + + No media & file servers. + Keine Medien- und Dateiserver. + servers error + + + No message + Keine Nachricht + No comment provided by engineer. + + + No message servers. + Keine Nachrichten-Server. + servers error + + + No network connection + Keine Netzwerkverbindung + No comment provided by engineer. + + + No permission to record speech + Keine Genehmigung für Sprach-Aufnahmen + No comment provided by engineer. + + + No permission to record video + Keine Genehmigung für Video-Aufnahmen + No comment provided by engineer. + No permission to record voice message Keine Berechtigung für das Aufnehmen von Sprachnachrichten No comment provided by engineer. + + No push server + Lokal + No comment provided by engineer. + No received or sent files - Keine empfangenen oder gesendeten Dateien + Keine herunter- oder hochgeladenen Dateien + No comment provided by engineer. + + + No servers for private message routing. + Keine Server für privates Nachrichten-Routing. + servers error + + + No servers to receive files. + Keine Server für das Herunterladen von Dateien. + servers error + + + No servers to receive messages. + Keine Server für den Empfang von Nachrichten. + servers error + + + No servers to send files. + Keine Server für das Versenden von Dateien. + servers error + + + No token! + Kein Token! + alert title + + + No unread chats + Keine ungelesenen Chats + No comment provided by engineer. + + + No user identifiers. + Keine Benutzerkennungen. No comment provided by engineer. @@ -3832,6 +5355,21 @@ Das ist Ihr Link für die Gruppe %@! Nicht kompatibel! No comment provided by engineer. + + Notes + Anmerkungen + No comment provided by engineer. + + + Nothing selected + Nichts ausgewählt + No comment provided by engineer. + + + Nothing to forward! + Es gibt nichts zum Weiterleiten! + alert title + Notifications Benachrichtigungen @@ -3842,6 +5380,21 @@ Das ist Ihr Link für die Gruppe %@! Benachrichtigungen sind deaktiviert! No comment provided by engineer. + + Notifications error + Benachrichtigungs-Fehler + alert title + + + Notifications privacy + Datenschutz für Benachrichtigungen + No comment provided by engineer. + + + Notifications status + Benachrichtigungs-Status + alert title + Now admins can: - delete members' messages. @@ -3859,36 +5412,35 @@ Das ist Ihr Link für die Gruppe %@! Off Aus - No comment provided by engineer. + blur media Ok Ok - No comment provided by engineer. + alert button Old database Alte Datenbank No comment provided by engineer. - - Old database archive - Altes Datenbankarchiv - No comment provided by engineer. - One-time invitation link Einmal-Einladungslink No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Für die Verbindung werden Onion-Hosts benötigt. Dies erfordert die Aktivierung eines VPNs. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Für diese Verbindung werden Onion-Hosts benötigt. +Dies erfordert die Aktivierung eines VPNs. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Onion-Hosts werden verwendet, sobald sie verfügbar sind. Dies erfordert die Aktivierung eines VPNs. + + Onion hosts will be used when available. +Requires compatible VPN. + Wenn Onion-Hosts verfügbar sind, werden sie verwendet. +Dies erfordert die Aktivierung eines VPNs. No comment provided by engineer. @@ -3896,11 +5448,21 @@ Das ist Ihr Link für die Gruppe %@! Onion-Hosts werden nicht verwendet. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + Nur Chat-Eigentümer können die Präferenzen ändern. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. Nur die Endgeräte speichern die Benutzerprofile, Kontakte, Gruppen und Nachrichten, welche über eine **2-Schichten Ende-zu-Ende-Verschlüsselung** gesendet werden. No comment provided by engineer. + + Only delete conversation + Nur die Chat-Inhalte löschen + No comment provided by engineer. + Only group owners can change group preferences. Gruppen-Präferenzen können nur von Gruppen-Eigentümern geändert werden. @@ -3916,6 +5478,16 @@ Das ist Ihr Link für die Gruppe %@! Sprachnachrichten können nur von Gruppen-Eigentümern aktiviert werden. No comment provided by engineer. + + Only sender and moderators see it + Nur Absender und Moderatoren sehen es + No comment provided by engineer. + + + Only you and moderators see it + Nur Sie und Moderatoren sehen es + No comment provided by engineer. + Only you can add message reactions. Nur Sie können Reaktionen auf Nachrichten geben. @@ -3969,13 +5541,18 @@ Das ist Ihr Link für die Gruppe %@! Open Öffnen - No comment provided by engineer. + alert action Open Settings Geräte-Einstellungen öffnen No comment provided by engineer. + + Open changes + Änderungen öffnen + No comment provided by engineer. + Open chat Chat öffnen @@ -3986,32 +5563,48 @@ Das ist Ihr Link für die Gruppe %@! Chat-Konsole öffnen authentication reason + + Open conditions + Nutzungsbedingungen öffnen + No comment provided by engineer. + Open group Gruppe öffnen No comment provided by engineer. + + Open link? + alert title + Open migration to another device + Migration auf ein anderes Gerät öffnen authentication reason - - Open user profiles - Benutzerprofile öffnen - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Open-Source-Protokoll und -Code – Jede Person kann ihre eigenen Server aufsetzen und nutzen. - No comment provided by engineer. - Opening app… App wird geöffnet… No comment provided by engineer. + + Operator + Betreiber + No comment provided by engineer. + + + Operator server + Betreiber-Server + alert title + + + Or import archive file + Oder importieren Sie eine Archiv-Datei + No comment provided by engineer. + Or paste archive link + Oder fügen Sie den Archiv-Link ein No comment provided by engineer. @@ -4021,6 +5614,7 @@ Das ist Ihr Link für die Gruppe %@! Or securely share this file link + Oder teilen Sie diesen Datei-Link sicher No comment provided by engineer. @@ -4028,6 +5622,28 @@ Das ist Ihr Link für die Gruppe %@! Oder diesen QR-Code anzeigen No comment provided by engineer. + + Or to share privately + Oder zum privaten Teilen + No comment provided by engineer. + + + Organize chats into lists + Chats in Listen verwalten + No comment provided by engineer. + + + Other + Andere + No comment provided by engineer. + + + Other file errors: +%@ + Andere(r) Datei-Fehler: +%@ + alert message + PING count PING-Zähler @@ -4063,6 +5679,11 @@ Das ist Ihr Link für die Gruppe %@! Zugangscode eingestellt! No comment provided by engineer. + + Password + Passwort + No comment provided by engineer. + Password to show Passwort anzeigen @@ -4093,13 +5714,13 @@ Das ist Ihr Link für die Gruppe %@! Fügen Sie den erhaltenen Link ein No comment provided by engineer. - - People can connect to you only via the links you share. - Verbindungen mit Kontakten sind nur über Links möglich, die Sie oder Ihre Kontakte untereinander teilen. + + Pending + Ausstehend No comment provided by engineer. - - Periodically + + Periodic Periodisch No comment provided by engineer. @@ -4110,6 +5731,17 @@ Das ist Ihr Link für die Gruppe %@! Picture-in-picture calls + Bild-in-Bild-Anrufe + No comment provided by engineer. + + + Play from the chat list. + Direkt aus der Chat-Liste abspielen. + No comment provided by engineer. + + + Please ask your contact to enable calls. + Bitten Sie Ihren Kontakt darum, Anrufe zu aktivieren. No comment provided by engineer. @@ -4117,6 +5749,13 @@ Das ist Ihr Link für die Gruppe %@! Bitten Sie Ihren Kontakt darum, das Senden von Sprachnachrichten zu aktivieren. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + Bitte überprüfen Sie, ob sich das Mobiltelefon und die Desktop-App im gleichen lokalen Netzwerk befinden, und die Desktop-Firewall die Verbindung erlaubt. +Bitte teilen Sie weitere mögliche Probleme den Entwicklern mit. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Überprüfen Sie bitte, ob Sie den richtigen Link genutzt haben oder bitten Sie Ihren Kontakt nochmal darum, Ihnen einen Link zuzusenden. @@ -4134,6 +5773,7 @@ Das ist Ihr Link für die Gruppe %@! Please confirm that network settings are correct for this device. + Bitte bestätigen Sie, dass die Netzwerkeinstellungen auf diesem Gerät richtig sind. No comment provided by engineer. @@ -4183,60 +5823,121 @@ Fehler: %@ Bitte bewahren Sie das Passwort sicher auf, Sie können es NICHT mehr ändern, wenn Sie es vergessen haben oder verlieren. No comment provided by engineer. + + Please try to disable and re-enable notfications. + Bitte versuchen Sie, die Benachrichtigungen zu deaktivieren und wieder zu aktivieren. + token info + + + Please wait for token activation to complete. + Bitte warten Sie, bis die Token-Aktivierung abgeschlossen ist. + token info + + + Please wait for token to be registered. + Bitte warten Sie auf die Registrierung des Tokens. + token info + Polish interface Polnische Bedienoberfläche No comment provided by engineer. + + Port + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Der Fingerabdruck des Zertifikats in der Serveradresse ist wahrscheinlich ungültig server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Den letzten Nachrichtenentwurf, auch mit seinen Anhängen, aufbewahren. No comment provided by engineer. - - Preset server - Voreingestellter Server - No comment provided by engineer. - Preset server address Voreingestellte Serveradresse No comment provided by engineer. + + Preset servers + Voreingestellte Server + No comment provided by engineer. + Preview Vorschau No comment provided by engineer. + + Previously connected servers + Bisher verbundene Server + No comment provided by engineer. + Privacy & security Datenschutz & Sicherheit No comment provided by engineer. + + Privacy for your customers. + Schutz der Privatsphäre Ihrer Kunden. + No comment provided by engineer. + + + Privacy policy and conditions of use. + Datenschutz- und Nutzungsbedingungen. + No comment provided by engineer. + Privacy redefined Datenschutz neu definiert No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + Private Chats, Gruppen und Ihre Kontakte sind für Server-Betreiber nicht zugänglich. + No comment provided by engineer. + Private filenames Neutrale Dateinamen No comment provided by engineer. + + Private media file names. + Medien mit anonymisierten Dateinamen. + No comment provided by engineer. + + + Private message routing + Privates Nachrichten-Routing + No comment provided by engineer. + + + Private message routing 🚀 + Privates Nachrichten-Routing 🚀 + No comment provided by engineer. + Private notes Private Notizen name of notes to self + + Private routing + Privates Routing + No comment provided by engineer. + + + Private routing error + Fehler beim privaten Routing + No comment provided by engineer. + Profile and server connections Profil und Serververbindungen @@ -4247,14 +5948,9 @@ Fehler: %@ Profilbild No comment provided by engineer. - - Profile name - Profilname - No comment provided by engineer. - - - Profile name: - Profilname: + + Profile images + Profil-Bilder No comment provided by engineer. @@ -4262,10 +5958,15 @@ Fehler: %@ Passwort für Profil No comment provided by engineer. + + Profile theme + Profil-Design + No comment provided by engineer. + Profile update will be sent to your contacts. Profil-Aktualisierung wird an Ihre Kontakte gesendet. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4287,6 +5988,16 @@ Fehler: %@ Reaktionen auf Nachrichten nicht erlauben. No comment provided by engineer. + + Prohibit reporting messages to moderators. + Melden von Nachrichten an Moderatoren nicht erlauben. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + Das Senden von SimpleX-Links nicht erlauben. + No comment provided by engineer. + Prohibit sending direct messages to members. Das Senden von Direktnachrichten an Gruppenmitglieder nicht erlauben. @@ -4294,7 +6005,7 @@ Fehler: %@ Prohibit sending disappearing messages. - Das Senden von verschwindenden Nachrichten verbieten. + Das Senden von verschwindenden Nachrichten nicht erlauben. No comment provided by engineer. @@ -4307,11 +6018,23 @@ Fehler: %@ Das Senden von Sprachnachrichten nicht erlauben. No comment provided by engineer. + + Protect IP address + IP-Adresse schützen + No comment provided by engineer. + Protect app screen App-Bildschirm schützen No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + Schützen Sie Ihre IP-Adresse vor den Nachrichten-Relais, die Ihre Kontakte ausgewählt haben. +Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. + No comment provided by engineer. + Protect your chat profiles with a password! Ihre Chat-Profile mit einem Passwort schützen! @@ -4327,6 +6050,21 @@ Fehler: %@ Protokollzeitüberschreitung pro kB No comment provided by engineer. + + Proxied + Proxied + No comment provided by engineer. + + + Proxied servers + Proxy-Server + No comment provided by engineer. + + + Proxy requires password + Der Proxy benötigt ein Passwort + No comment provided by engineer. + Push notifications Push-Benachrichtigungen @@ -4334,10 +6072,12 @@ Fehler: %@ Push server + Push-Server No comment provided by engineer. Quantum resistant encryption + Quantum-resistente Verschlüsselung No comment provided by engineer. @@ -4345,6 +6085,11 @@ Fehler: %@ Bewerten Sie die App No comment provided by engineer. + + Reachable chat toolbar + Chat-Symbolleiste unten + No comment provided by engineer. + React… Reagiere… @@ -4353,33 +6098,28 @@ Fehler: %@ Read Gelesen - No comment provided by engineer. + swipe action Read more Mehr erfahren No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Mehr dazu in der [Benutzeranleitung](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address) lesen. - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). Lesen Sie mehr dazu im [Benutzerhandbuch](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Mehr dazu in der [Benutzeranleitung](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses) lesen. + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). Mehr dazu in der [Benutzeranleitung](https://simplex.chat/docs/guide/readme.html#connect-to-friends) lesen. No comment provided by engineer. - - Read more in our GitHub repository. - Erfahren Sie in unserem GitHub-Repository mehr dazu. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). Erfahren Sie in unserem [GitHub-Repository](https://github.com/simplex-chat/simplex-chat#readme) mehr dazu. @@ -4390,6 +6130,11 @@ Fehler: %@ Bestätigungen sind deaktiviert No comment provided by engineer. + + Receive errors + Fehler beim Empfang + No comment provided by engineer. + Received at Empfangen um @@ -4410,6 +6155,21 @@ Fehler: %@ Empfangene Nachricht message info title + + Received messages + Empfangene Nachrichten + No comment provided by engineer. + + + Received reply + Empfangene Antwort + No comment provided by engineer. + + + Received total + Summe aller empfangenen Nachrichten + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. Die Empfängeradresse wird auf einen anderen Server geändert. Der Adresswechsel wird abgeschlossen, wenn der Absender wieder online ist. @@ -4417,7 +6177,7 @@ Fehler: %@ Receiving file will be stopped. - Der Empfang der Datei wird beendet. + Das Herunterladen der Datei wird beendet. No comment provided by engineer. @@ -4430,16 +6190,46 @@ Fehler: %@ Aktueller Nachrichtenverlauf und verbesserter [Gruppenverzeichnis-Bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + Empfänger können nicht sehen, von wem die Nachricht stammt. + No comment provided by engineer. + Recipients see updates as you type them. Die Empfänger sehen Nachrichtenaktualisierungen, während Sie sie eingeben. No comment provided by engineer. + + Reconnect + Neu verbinden + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Alle verbundenen Server werden neu verbunden, um die Zustellung der Nachricht zu erzwingen. Dies verursacht zusätzlichen Datenverkehr. No comment provided by engineer. + + Reconnect all servers + Alle Server neu verbinden + No comment provided by engineer. + + + Reconnect all servers? + Alle Server neu verbinden? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + Um die Auslieferung von Nachrichten zu erzwingen, wird der Server neu verbunden. Dafür wird weiterer Datenverkehr benötigt. + No comment provided by engineer. + + + Reconnect server? + Server neu verbinden? + No comment provided by engineer. + Reconnect servers? Die Server neu verbinden? @@ -4460,10 +6250,26 @@ Fehler: %@ Reduzierter Batterieverbrauch No comment provided by engineer. + + Register + Registrieren + No comment provided by engineer. + + + Register notification token? + Benachrichtigungs-Token registrieren? + token info + + + Registered + Registriert + token status text + Reject Ablehnen - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4490,6 +6296,16 @@ Fehler: %@ Entfernen No comment provided by engineer. + + Remove archive? + Archiv entfernen? + No comment provided by engineer. + + + Remove image + Bild entfernen + No comment provided by engineer. + Remove member Mitglied entfernen @@ -4527,10 +6343,12 @@ Fehler: %@ Repeat download + Herunterladen wiederholen No comment provided by engineer. Repeat import + Import wiederholen No comment provided by engineer. @@ -4540,6 +6358,7 @@ Fehler: %@ Repeat upload + Hochladen wiederholen No comment provided by engineer. @@ -4547,6 +6366,56 @@ Fehler: %@ Antwort chat item action + + Report + Melden + chat item action + + + Report content: only group moderators will see it. + Inhalt melden: Nur Gruppenmoderatoren werden es sehen. + report reason + + + Report member profile: only group moderators will see it. + Mitgliederprofil melden: Nur Gruppenmoderatoren werden es sehen. + report reason + + + Report other: only group moderators will see it. + Anderes melden: Nur Gruppenmoderatoren werden es sehen. + report reason + + + Report reason? + Grund der Meldung? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + Spam melden: Nur Gruppenmoderatoren werden es sehen. + report reason + + + Report violation: only group moderators will see it. + Verstoß melden: Nur Gruppenmoderatoren werden es sehen. + report reason + + + Report: %@ + Meldung: %@ + report in notification + + + Reporting messages to moderators is prohibited. + Melden von Nachrichten an Moderatoren ist nicht erlaubt. + No comment provided by engineer. + + + Reports + Meldungen + No comment provided by engineer. + Required Erforderlich @@ -4557,16 +6426,41 @@ Fehler: %@ Zurücksetzen No comment provided by engineer. + + Reset all hints + Alle Hinweise zurücksetzen + No comment provided by engineer. + + + Reset all statistics + Alle Statistiken zurücksetzen + No comment provided by engineer. + + + Reset all statistics? + Alle Statistiken zurücksetzen? + No comment provided by engineer. + Reset colors Farben zurücksetzen No comment provided by engineer. + + Reset to app theme + Auf das App-Design zurücksetzen + No comment provided by engineer. + Reset to defaults Auf Voreinstellungen zurücksetzen No comment provided by engineer. + + Reset to user theme + Auf das Benutzer-spezifische Design zurücksetzen + No comment provided by engineer. + Restart the app to create a new chat profile Um ein neues Chat-Profil zu erstellen, starten Sie die App neu @@ -4607,9 +6501,9 @@ Fehler: %@ Aufdecken chat item action - - Revert - Zurückkehren + + Review conditions + Nutzungsbedingungen einsehen No comment provided by engineer. @@ -4637,55 +6531,67 @@ Fehler: %@ Chat starten No comment provided by engineer. - - SMP servers + + SMP server SMP-Server No comment provided by engineer. + + SOCKS proxy + SOCKS-Proxy + No comment provided by engineer. + + + Safely receive files + Dateien sicher herunterladen + No comment provided by engineer. + Safer groups + Sicherere Gruppen No comment provided by engineer. Save Speichern - chat item action + alert button +chat item action Save (and notify contacts) Speichern (und Kontakte benachrichtigen) - No comment provided by engineer. + alert button Save and notify contact Speichern und Kontakt benachrichtigen - No comment provided by engineer. + alert button Save and notify group members Speichern und Gruppenmitglieder benachrichtigen No comment provided by engineer. + + Save and reconnect + Speichern und neu verbinden + No comment provided by engineer. + Save and update group profile Gruppen-Profil sichern und aktualisieren No comment provided by engineer. - - Save archive - Archiv speichern - No comment provided by engineer. - - - Save auto-accept settings - Einstellungen von "Automatisch akzeptieren" speichern - No comment provided by engineer. - Save group profile Gruppenprofil speichern No comment provided by engineer. + + Save list + Liste speichern + No comment provided by engineer. + Save passphrase and open chat Passwort speichern und Chat öffnen @@ -4699,7 +6605,7 @@ Fehler: %@ Save preferences? Präferenzen speichern? - No comment provided by engineer. + alert title Save profile password @@ -4714,28 +6620,53 @@ Fehler: %@ Save servers? Alle Server speichern? - No comment provided by engineer. - - - Save settings? - Einstellungen speichern? - No comment provided by engineer. + alert title Save welcome message? Begrüßungsmeldung speichern? No comment provided by engineer. + + Save your profile? + Ihr Profil speichern? + alert title + + + Saved + Abgespeichert + No comment provided by engineer. + Saved WebRTC ICE servers will be removed Gespeicherte WebRTC ICE-Server werden entfernt No comment provided by engineer. + + Saved from + Abgespeichert von + No comment provided by engineer. + Saved message Gespeicherte Nachricht message info title + + Saving %lld messages + Es wird/werden %lld Nachricht(en) gesichert + No comment provided by engineer. + + + Scale + Skalieren + No comment provided by engineer. + + + Scan / Paste link + Link scannen / einfügen + No comment provided by engineer. + Scan QR code QR-Code scannen @@ -4773,7 +6704,12 @@ Fehler: %@ Search or paste SimpleX link - Suchen oder fügen Sie den SimpleX-Link ein + Suchen oder SimpleX-Link einfügen + No comment provided by engineer. + + + Secondary + Zweite Farbe No comment provided by engineer. @@ -4781,6 +6717,11 @@ Fehler: %@ Sichere Warteschlange server test step + + Secured + Abgesichert + No comment provided by engineer. + Security assessment Sicherheits-Gutachten @@ -4794,6 +6735,21 @@ Fehler: %@ Select Auswählen + chat item action + + + Select chat profile + Chat-Profil auswählen + No comment provided by engineer. + + + Selected %lld + %lld ausgewählt + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Diese Nachricht ist wegen der gewählten Chat-Einstellungen nicht erlaubt. No comment provided by engineer. @@ -4831,11 +6787,6 @@ Fehler: %@ Empfangsbestätigungen senden an No comment provided by engineer. - - Send direct message - Direktnachricht senden - No comment provided by engineer. - Send direct message to connect Eine Direktnachricht zum Verbinden senden @@ -4846,6 +6797,11 @@ Fehler: %@ Verschwindende Nachricht senden No comment provided by engineer. + + Send errors + Fehler beim Senden + No comment provided by engineer. + Send link previews Link-Vorschau senden @@ -4856,14 +6812,29 @@ Fehler: %@ Live Nachricht senden No comment provided by engineer. + + Send message to enable calls. + Nachricht senden, um Anrufe zu aktivieren. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + Nachrichten werden direkt versendet, wenn die IP-Adresse geschützt ist, und Ihr oder der Zielserver kein privates Routing unterstützt. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + Nachrichten werden direkt versendet, wenn Ihr oder der Zielserver kein privates Routing unterstützt. + No comment provided by engineer. + Send notifications Benachrichtigungen senden No comment provided by engineer. - - Send notifications: - Benachrichtigungen senden: + + Send private reports + Private Meldungen senden No comment provided by engineer. @@ -4889,7 +6860,7 @@ Fehler: %@ Sender cancelled file transfer. Der Absender hat die Dateiübertragung abgebrochen. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4946,6 +6917,11 @@ Fehler: %@ Gesendet um: %@ copied message info + + Sent directly + Direkt gesendet + No comment provided by engineer. + Sent file event Datei-Ereignis wurde gesendet @@ -4956,11 +6932,71 @@ Fehler: %@ Gesendete Nachricht message info title + + Sent messages + Gesendete Nachrichten + No comment provided by engineer. + Sent messages will be deleted after set time. Gesendete Nachrichten werden nach der eingestellten Zeit gelöscht. No comment provided by engineer. + + Sent reply + Gesendete Antwort + No comment provided by engineer. + + + Sent total + Summe aller gesendeten Nachrichten + No comment provided by engineer. + + + Sent via proxy + Über einen Proxy gesendet + No comment provided by engineer. + + + Server + Server + No comment provided by engineer. + + + Server added to operator %@. + Der Server wurde dem Betreiber %@ hinzugefügt. + alert message + + + Server address + Server-Adresse + No comment provided by engineer. + + + Server address is incompatible with network settings. + Die Server-Adresse ist nicht mit den Netzwerkeinstellungen kompatibel. + srv error text. + + + Server address is incompatible with network settings: %@. + Die Server-Adresse ist nicht mit den Netzwerkeinstellungen kompatibel: %@. + No comment provided by engineer. + + + Server operator changed. + Der Server-Betreiber wurde geändert. + alert title + + + Server operators + Server-Betreiber + No comment provided by engineer. + + + Server protocol changed. + Das Server-Protokoll wurde geändert. + alert title + Server requires authorization to create queues, check password Um Warteschlangen zu erzeugen benötigt der Server eine Authentifizierung. Bitte überprüfen Sie das Passwort @@ -4976,11 +7012,36 @@ Fehler: %@ Server Test ist fehlgeschlagen! No comment provided by engineer. + + Server type + Server-Typ + No comment provided by engineer. + + + Server version is incompatible with network settings. + Die Server-Version ist nicht mit den Netzwerkeinstellungen kompatibel. + srv error text + + + Server version is incompatible with your app: %@. + Die Server-Version ist nicht mit Ihrer App kompatibel: %@. + No comment provided by engineer. + Servers Server No comment provided by engineer. + + Servers info + Server-Informationen + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + Die Serverstatistiken werden zurückgesetzt. Dies kann nicht rückgängig gemacht werden! + No comment provided by engineer. + Session code Sitzungscode @@ -4991,11 +7052,21 @@ Fehler: %@ Einen Tag festlegen No comment provided by engineer. + + Set chat name… + Chat-Name festlegen… + No comment provided by engineer. + Set contact name… Kontaktname festlegen… No comment provided by engineer. + + Set default theme + Default-Design einstellen + No comment provided by engineer. + Set group preferences Gruppen-Präferenzen einstellen @@ -5006,6 +7077,11 @@ Fehler: %@ Anstelle der System-Authentifizierung festlegen. No comment provided by engineer. + + Set message expiration in chats. + Verfallsdatum von Nachrichten in Chats festlegen. + No comment provided by engineer. + Set passcode Zugangscode einstellen @@ -5013,6 +7089,7 @@ Fehler: %@ Set passphrase + Passwort festlegen No comment provided by engineer. @@ -5035,24 +7112,55 @@ Fehler: %@ Einstellungen No comment provided by engineer. + + Settings were changed. + Die Einstellungen wurden geändert. + alert message + + + Shape profile images + Form der Profil-Bilder + No comment provided by engineer. + Share Teilen - chat item action + alert action +chat item action Share 1-time link Einmal-Link teilen No comment provided by engineer. + + Share 1-time link with a friend + Den Einmal-Einladungslink mit einem Freund teilen + No comment provided by engineer. + + + Share SimpleX address on social media. + Die SimpleX-Adresse auf sozialen Medien teilen. + No comment provided by engineer. + Share address Adresse teilen No comment provided by engineer. + + Share address publicly + Die Adresse öffentlich teilen + No comment provided by engineer. + Share address with contacts? Die Adresse mit Kontakten teilen? + alert title + + + Share from other apps. + Aus anderen Apps heraus teilen. No comment provided by engineer. @@ -5060,18 +7168,34 @@ Fehler: %@ Link teilen No comment provided by engineer. + + Share profile + Profil teilen + No comment provided by engineer. + Share this 1-time invite link Teilen Sie diesen Einmal-Einladungslink No comment provided by engineer. + + Share to SimpleX + Mit SimpleX teilen + No comment provided by engineer. + Share with contacts Mit Kontakten teilen No comment provided by engineer. + + Short link + Verkürzter Link + No comment provided by engineer. + Show QR code + QR-Code anzeigen No comment provided by engineer. @@ -5089,21 +7213,46 @@ Fehler: %@ Letzte Nachrichten anzeigen No comment provided by engineer. + + Show message status + Nachrichtenstatus anzeigen + No comment provided by engineer. + + + Show percentage + Prozentualen Anteil anzeigen + No comment provided by engineer. + Show preview Vorschau anzeigen No comment provided by engineer. + + Show → on messages sent via private routing. + Bei Nachrichten, die über privates Routing versendet wurden, → anzeigen. + No comment provided by engineer. + Show: Anzeigen: No comment provided by engineer. + + SimpleX + SimpleX + No comment provided by engineer. + SimpleX Address SimpleX-Adresse No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + SimpleX-Chat und Flux haben vereinbart, die von Flux betriebenen Server in die App aufzunehmen. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. Die Sicherheit von SimpleX Chat wurde von Trail of Bits überprüft. @@ -5134,6 +7283,21 @@ Fehler: %@ SimpleX-Adresse No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + Die SimpleX-Adresse und Einmal-Links können sicher über beliebige Messenger geteilt werden. + No comment provided by engineer. + + + SimpleX address or 1-time link? + SimpleX-Adresse oder Einmal-Link? + No comment provided by engineer. + + + SimpleX channel link + SimpleX-Kanal-Link + simplex link type + SimpleX contact address SimpleX-Kontaktadressen-Link @@ -5152,6 +7316,16 @@ Fehler: %@ SimpleX links SimpleX-Links + chat feature + + + SimpleX links are prohibited. + In dieser Gruppe sind SimpleX-Links nicht erlaubt. + No comment provided by engineer. + + + SimpleX links not allowed + SimpleX-Links sind nicht erlaubt No comment provided by engineer. @@ -5159,11 +7333,21 @@ Fehler: %@ SimpleX-Einmal-Einladung simplex link type + + SimpleX protocols reviewed by Trail of Bits. + Die SimpleX-Protokolle wurden von Trail of Bits überprüft. + No comment provided by engineer. + Simplified incognito mode Vereinfachter Inkognito-Modus No comment provided by engineer. + + Size + Größe + No comment provided by engineer. + Skip Überspringen @@ -5179,16 +7363,54 @@ Fehler: %@ Kleine Gruppen (max. 20) No comment provided by engineer. + + Soft + Weich + blur media + + + Some app settings were not migrated. + Einige App-Einstellungen wurden nicht migriert. + No comment provided by engineer. + + + Some file(s) were not exported: + Einzelne Datei(en) wurde(n) nicht exportiert: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. Während des Imports sind einige nicht schwerwiegende Fehler aufgetreten - in der Chat-Konsole finden Sie weitere Einzelheiten. No comment provided by engineer. + + Some non-fatal errors occurred during import: + Während des Imports traten ein paar nicht schwerwiegende Fehler auf: + No comment provided by engineer. + + + Some servers failed the test: +%@ + Einige Server haben den Test nicht bestanden: +%@ + alert message + Somebody Jemand notification title + + Spam + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + Quadratisch, kreisförmig oder irgendetwas dazwischen. + No comment provided by engineer. + Start chat Starten Sie den Chat @@ -5204,6 +7426,16 @@ Fehler: %@ Starten Sie die Migration No comment provided by engineer. + + Starting from %@. + Beginnend mit %@. + No comment provided by engineer. + + + Statistics + Statistiken + No comment provided by engineer. + Stop Beenden @@ -5216,11 +7448,7 @@ Fehler: %@ Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - Chat beenden, um Datenbankaktionen zu erlauben + Chat beenden No comment provided by engineer. @@ -5235,43 +7463,79 @@ Fehler: %@ Stop file - Datei beenden + Herunterladen beenden cancel file action Stop receiving file? - Den Empfang der Datei beenden? + Das Herunterladen der Datei beenden? No comment provided by engineer. Stop sending file? - Das Senden der Datei beenden? + Das Hochladen der Datei beenden? No comment provided by engineer. Stop sharing Teilen beenden - No comment provided by engineer. + alert action Stop sharing address? Das Teilen der Adresse beenden? - No comment provided by engineer. + alert title Stopping chat + Chat wird beendet No comment provided by engineer. + + Storage + Ablage + No comment provided by engineer. + + + Strong + Hart + blur media + Submit Bestätigen No comment provided by engineer. + + Subscribed + Abonniert + No comment provided by engineer. + + + Subscription errors + Fehler beim Abonnieren + No comment provided by engineer. + + + Subscriptions ignored + Nicht beachtete Abonnements + No comment provided by engineer. + Support SimpleX Chat Unterstützung von SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Während des Anrufs zwischen Audio und Video wechseln + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Das Chat-Profil für Einmal-Einladungen wechseln + No comment provided by engineer. + System System @@ -5282,11 +7546,21 @@ Fehler: %@ System-Authentifizierung No comment provided by engineer. + + TCP connection + TCP-Verbindung + No comment provided by engineer. + TCP connection timeout Timeout der TCP-Verbindung No comment provided by engineer. + + TCP port for messaging + TCP-Port für Nachrichtenübermittlung + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5302,11 +7576,21 @@ Fehler: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + Sprechblasen-Format + No comment provided by engineer. + Take picture Machen Sie ein Foto No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + Tippen Sie im Menü auf SimpleX-Adresse erstellen, um sie später zu erstellen. + No comment provided by engineer. + Tap button Schaltfläche antippen @@ -5342,16 +7626,21 @@ Fehler: %@ Zum Scannen tippen No comment provided by engineer. - - Tap to start a new chat - Zum Starten eines neuen Chats tippen - No comment provided by engineer. + + Temporary file error + Temporärer Datei-Fehler + file error alert title Test failed at step %@. Der Test ist beim Schritt %@ fehlgeschlagen. server test failure + + Test notifications + Benachrichtigungen testen + No comment provided by engineer. + Test server Teste Server @@ -5365,7 +7654,7 @@ Fehler: %@ Tests failed! Tests sind fehlgeschlagen! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5382,11 +7671,6 @@ Fehler: %@ Dank der Nutzer - Tragen Sie per Weblate bei! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - Die erste Plattform ohne Benutzerkennungen – Privat per Design. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5399,6 +7683,16 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro Wenn sie Nachrichten oder Kontaktanfragen empfangen, kann Sie die App benachrichtigen - Um dies zu aktivieren, öffnen Sie bitte die Einstellungen. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + Durch Verwendung verschiedener Netzwerk-Betreiber für jede Unterhaltung schützt die App Ihre Privatsphäre. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + Die App wird eine Bestätigung bei Downloads von unbekannten Datei-Servern anfordern (außer bei .onion). + No comment provided by engineer. + The attempt to change database passphrase was not completed. Die Änderung des Datenbank-Passworts konnte nicht abgeschlossen werden. @@ -5409,6 +7703,11 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro Der von Ihnen gescannte Code ist kein SimpleX-Link-QR-Code. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + Diese Verbindung hat das Limit der nicht ausgelieferten Nachrichten erreicht. Ihr Kontakt ist möglicherweise offline. + No comment provided by engineer. + The connection you accepted will be cancelled! Die von Ihnen akzeptierte Verbindung wird abgebrochen! @@ -5429,6 +7728,11 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro Die Verschlüsselung funktioniert und ein neues Verschlüsselungsabkommen ist nicht erforderlich. Es kann zu Verbindungsfehlern kommen! No comment provided by engineer. + + The future of messaging + Die nächste Generation von privatem Messaging + No comment provided by engineer. + The hash of the previous message is different. Der Hash der vorherigen Nachricht unterscheidet sich. @@ -5444,9 +7748,14 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro Diese Nachricht wird für alle Mitglieder als moderiert gekennzeichnet. No comment provided by engineer. - - The next generation of private messaging - Die nächste Generation von privatem Messaging + + The messages will be deleted for all members. + Die Nachrichten werden für alle Gruppenmitglieder gelöscht. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. + Die Nachrichten werden für alle Mitglieder als moderiert gekennzeichnet werden. No comment provided by engineer. @@ -5454,9 +7763,14 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro Die alte Datenbank wurde während der Migration nicht entfernt. Sie kann gelöscht werden. No comment provided by engineer. - - The profile is only shared with your contacts. - Das Profil wird nur mit Ihren Kontakten geteilt. + + The same conditions will apply to operator **%@**. + Dieselben Nutzungsbedingungen gelten auch für den Betreiber **%@**. + No comment provided by engineer. + + + The second preset operator in the app! + Der zweite voreingestellte Netzwerk-Betreiber in der App! No comment provided by engineer. @@ -5471,7 +7785,12 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro The servers for new connections of your current chat profile **%@**. - Mögliche Server für neue Verbindungen von Ihrem aktuellen Chat-Profil **%@**. + Nachrichten-Server für neue Verbindungen über Ihr aktuelles Chat-Profil **%@**. + No comment provided by engineer. + + + The servers for new files of your current chat profile **%@**. + Medien- und Datei-Server für neue Daten über Ihr aktuelles Chat-Profil **%@**. No comment provided by engineer. @@ -5479,11 +7798,21 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro Der von Ihnen eingefügte Text ist kein SimpleX-Link. No comment provided by engineer. - - Theme + + The uploaded database archive will be permanently removed from the servers. + Das hochgeladene Datenbank-Archiv wird dauerhaft von den Servern entfernt. + No comment provided by engineer. + + + Themes Design No comment provided by engineer. + + These conditions will also apply for: **%@**. + Diese Nutzungsbedingungen gelten auch für: **%@**. + No comment provided by engineer. + These settings are for your current profile **%@**. Diese Einstellungen betreffen Ihr aktuelles Profil **%@**. @@ -5496,25 +7825,32 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain. - Diese Aktion kann nicht rückgängig gemacht werden! Alle empfangenen und gesendeten Dateien und Medien werden gelöscht. Bilder mit niedriger Auflösung bleiben erhalten. + Es werden alle herunter- und hochgeladenen Dateien und Medien gelöscht. Bilder mit niedriger Auflösung bleiben erhalten. Diese Aktion kann nicht rückgängig gemacht werden! No comment provided by engineer. This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes. - Diese Aktion kann nicht rückgängig gemacht werden! Alle empfangenen und gesendeten Nachrichten, die über den ausgewählten Zeitraum hinaus gehen, werden gelöscht. Dieser Vorgang kann mehrere Minuten dauern. + Es werden alle empfangenen und gesendeten Nachrichten, die über den ausgewählten Zeitraum hinaus gehen, gelöscht. Dieser Vorgang kann mehrere Minuten dauern. Diese Aktion kann nicht rückgängig gemacht werden! No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + Die älteren als die ausgewählten gesendeten und empfangenen Nachrichten in diesem Chat werden gelöscht. Diese Aktion kann nicht rückgängig gemacht werden! + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. - Diese Aktion kann nicht rückgängig gemacht werden! Ihr Profil und Ihre Kontakte, Nachrichten und Dateien gehen unwiderruflich verloren. + Ihr Profil und Ihre Kontakte, Nachrichten und Dateien gehen unwiderruflich verloren. Diese Aktion kann nicht rückgängig gemacht werden! No comment provided by engineer. This chat is protected by end-to-end encryption. + Dieser Chat ist durch Ende-zu-Ende-Verschlüsselung geschützt. E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + Dieser Chat ist durch Quantum-resistente Ende-zu-Ende-Verschlüsselung geschützt. E2EE info chat item @@ -5547,14 +7883,34 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro Das ist Ihr eigener Einmal-Link! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + Für diesen Link wird eine neuere App-Version benötigt. Bitte aktualisieren Sie die App oder bitten Sie Ihren Kontakt einen kompatiblen Link zu senden. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + Dieser Link wurde schon mit einem anderen Mobiltelefon genutzt. Bitte erstellen sie einen neuen Link in der Desktop-App. + No comment provided by engineer. + + + This message was deleted or not received yet. + Diese Nachricht wurde gelöscht oder bisher noch nicht empfangen. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. Diese Einstellung gilt für Nachrichten in Ihrem aktuellen Chat-Profil **%@**. No comment provided by engineer. + + Title + Bezeichnung + No comment provided by engineer. + To ask any questions and to receive updates: - Um Fragen zu stellen und Aktualisierungen zu erhalten: + Um Fragen zu stellen und aktuelle Informationen zu erhalten: No comment provided by engineer. @@ -5572,9 +7928,9 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro Um eine Verbindung mit einem neuen Kontakt zu erstellen No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Zum Schutz Ihrer Privatsphäre verwendet SimpleX an Stelle von Benutzerkennungen, die von allen anderen Plattformen verwendet werden, Kennungen für Nachrichtenwarteschlangen, die für jeden Ihrer Kontakte individuell sind. + + To protect against your link being replaced, you can compare contact security codes. + Zum Schutz vor dem Austausch Ihres Links können Sie die Sicherheitscodes Ihrer Kontakte vergleichen. No comment provided by engineer. @@ -5582,6 +7938,11 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro Bild- und Sprachdateinamen enthalten UTC, um Informationen zur Zeitzone zu schützen. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + Zum Schutz Ihrer IP-Adresse, wird für die Nachrichten-Auslieferung privates Routing über Ihre konfigurierten SMP-Server genutzt. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5589,6 +7950,26 @@ You will be prompted to complete authentication before this feature is enabled.< Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funktion aktiviert wird. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Zum Schutz Ihrer Privatsphäre verwendet SimpleX an Stelle von Benutzerkennungen, die von allen anderen Plattformen verwendet werden, Kennungen für Nachrichtenwarteschlangen, die für jeden Ihrer Kontakte individuell sind. + No comment provided by engineer. + + + To receive + Für den Empfang + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + Bitte erteilen Sie für Sprach-Aufnahmen die Genehmigung das Mikrofon zu nutzen. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Bitte erteilen Sie für Video-Aufnahmen die Genehmigung die Kamera zu nutzen. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Bitte erlauben Sie die Nutzung des Mikrofons, um Sprachnachrichten aufnehmen zu können. @@ -5599,26 +7980,61 @@ Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funkt Geben Sie ein vollständiges Passwort in das Suchfeld auf der Seite **Ihre Chat-Profile** ein, um Ihr verborgenes Profil zu sehen. No comment provided by engineer. + + To send + Für das Senden + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. Um sofortige Push-Benachrichtigungen zu unterstützen, muss die Chat-Datenbank migriert werden. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + Um die Server von **%@** zu nutzen, müssen Sie dessen Nutzungsbedingungen akzeptieren. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. Um die Ende-zu-Ende-Verschlüsselung mit Ihrem Kontakt zu überprüfen, müssen Sie den Sicherheitscode in Ihren Apps vergleichen oder scannen. No comment provided by engineer. + + Toggle chat list: + Chat-Liste umschalten: + No comment provided by engineer. + Toggle incognito when connecting. Inkognito beim Verbinden einschalten. No comment provided by engineer. + + Token status: %@. + Token-Status: %@. + token status + + + Toolbar opacity + Deckkraft der Symbolleiste + No comment provided by engineer. + + + Total + Summe aller Abonnements + No comment provided by engineer. + Transport isolation Transport-Isolation No comment provided by engineer. + + Transport sessions + Transport-Sitzungen + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Beim Versuch die Verbindung mit dem Server aufzunehmen, der für den Empfang von Nachrichten mit diesem Kontakt genutzt wird, ist ein Fehler aufgetreten (Fehler: %@). @@ -5674,10 +8090,10 @@ Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funkt Mitglied freigeben? No comment provided by engineer. - - Unexpected error: %@ - Unerwarteter Fehler: %@ - item status description + + Undelivered messages + Nicht ausgelieferte Nachrichten + No comment provided by engineer. Unexpected migration state @@ -5687,7 +8103,7 @@ Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funkt Unfav. Fav. entf. - No comment provided by engineer. + swipe action Unhide @@ -5724,6 +8140,11 @@ Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funkt Unbekannter Fehler No comment provided by engineer. + + Unknown servers! + Unbekannte Server! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. Aktivieren Sie den Modus "Bitte nicht stören", um Unterbrechungen zu vermeiden, es sei denn, Sie verwenden die iOS Anrufschnittstelle. @@ -5759,11 +8180,16 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Unmute Stummschaltung aufheben - No comment provided by engineer. + notification label action Unread Ungelesen + swipe action + + + Unsupported connection link + Verbindungs-Link wird nicht unterstützt No comment provided by engineer. @@ -5776,11 +8202,6 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Aktualisieren No comment provided by engineer. - - Update .onion hosts setting? - Einstellung für .onion-Hosts aktualisieren? - No comment provided by engineer. - Update database passphrase Datenbank-Passwort aktualisieren @@ -5791,9 +8212,14 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Netzwerkeinstellungen aktualisieren? No comment provided by engineer. - - Update transport isolation mode? - Transport-Isolations-Modus aktualisieren? + + Update settings? + Einstellungen aktualisieren? + No comment provided by engineer. + + + Updated conditions + Aktualisierte Nutzungsbedingungen No comment provided by engineer. @@ -5801,18 +8227,19 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Die Aktualisierung der Einstellungen wird den Client wieder mit allen Servern verbinden. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - Die Aktualisierung dieser Einstellung wird den Client wieder mit allen Servern verbinden. - No comment provided by engineer. - Upgrade and open chat Aktualisieren und den Chat öffnen No comment provided by engineer. + + Upload errors + Fehler beim Hochladen + No comment provided by engineer. + Upload failed + Hochladen fehlgeschlagen No comment provided by engineer. @@ -5820,8 +8247,24 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Datei hochladen server test step + + Uploaded + Hochgeladen + No comment provided by engineer. + + + Uploaded files + Hochgeladene Dateien + No comment provided by engineer. + Uploading archive + Archiv wird hochgeladen + No comment provided by engineer. + + + Use %@ + Verwende %@ No comment provided by engineer. @@ -5829,11 +8272,26 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Verwende .onion-Hosts No comment provided by engineer. + + Use SOCKS proxy + SOCKS-Proxy nutzen + No comment provided by engineer. + Use SimpleX Chat servers? Verwenden Sie SimpleX-Chat-Server? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + Solange kein Port konfiguriert ist, wird TCP-Port %@ genutzt. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + TCP-Port 443 nur für voreingestellte Server verwenden. + No comment provided by engineer. + Use chat Verwenden Sie Chat @@ -5841,7 +8299,17 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Use current profile - Nutzen Sie das aktuelle Profil + Aktuelles Profil nutzen + No comment provided by engineer. + + + Use for files + Für Dateien verwenden + No comment provided by engineer. + + + Use for messages + Für Nachrichten verwenden No comment provided by engineer. @@ -5861,7 +8329,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Use new incognito profile - Nutzen Sie das neue Inkognito-Profil + Neues Inkognito-Profil nutzen No comment provided by engineer. @@ -5869,23 +8337,54 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Nur lokale Benachrichtigungen nutzen? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + Sie nutzen privates Routing mit unbekannten Servern, wenn Ihre IP-Adresse nicht geschützt ist. + No comment provided by engineer. + + + Use private routing with unknown servers. + Sie nutzen privates Routing mit unbekannten Servern. + No comment provided by engineer. + Use server Server nutzen No comment provided by engineer. + + Use servers + Verwende Server + No comment provided by engineer. + + + Use short links (BETA) + Kurze Links verwenden (BETA) + No comment provided by engineer. + Use the app while in the call. + Die App kann während eines Anrufs genutzt werden. No comment provided by engineer. - - User profile - Benutzerprofil + + Use the app with one hand. + Die App mit einer Hand bedienen. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - Für die Nutzung von .onion-Hosts sind kompatible VPN-Anbieter erforderlich. + + Use web port + Web-Port nutzen + No comment provided by engineer. + + + User selection + Benutzer-Auswahl + No comment provided by engineer. + + + Username + Benutzername No comment provided by engineer. @@ -5915,10 +8414,12 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Verify database passphrase + Überprüfen Sie das Datenbank-Passwort No comment provided by engineer. Verify passphrase + Überprüfen Sie das Passwort No comment provided by engineer. @@ -5943,12 +8444,12 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Video will be received when your contact completes uploading it. - Das Video wird empfangen, sobald Ihr Kontakt das Hochladen beendet hat. + Das Video wird heruntergeladen, sobald Ihr Kontakt das Hochladen beendet hat. No comment provided by engineer. Video will be received when your contact is online, please wait or check later! - Das Video wird empfangen, wenn Ihr Kontakt online ist. Bitte warten oder überprüfen Sie es später! + Das Video wird heruntergeladen, sobald Ihr Kontakt online ist. Bitte warten oder überprüfen Sie es später! No comment provided by engineer. @@ -5956,11 +8457,21 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Videos und Dateien bis zu 1GB No comment provided by engineer. + + View conditions + Nutzungsbedingungen anschauen + No comment provided by engineer. + View security code Schauen Sie sich den Sicherheitscode an No comment provided by engineer. + + View updated conditions + Aktualisierte Nutzungsbedingungen anschauen + No comment provided by engineer. + Visible history Sichtbarer Nachrichtenverlauf @@ -5976,11 +8487,16 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s In diesem Chat sind Sprachnachrichten nicht erlaubt. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. In dieser Gruppe sind Sprachnachrichten nicht erlaubt. No comment provided by engineer. + + Voice messages not allowed + Sprachnachrichten sind nicht erlaubt + No comment provided by engineer. + Voice messages prohibited! Sprachnachrichten sind nicht erlaubt! @@ -6011,8 +8527,19 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Auf das Video warten No comment provided by engineer. + + Wallpaper accent + Wallpaper-Akzent + No comment provided by engineer. + + + Wallpaper background + Wallpaper-Hintergrund + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures + Warnung: Das Starten des Chats auf mehreren Geräten wird nicht unterstützt und wird zu Fehlern bei der Nachrichtenübermittlung führen No comment provided by engineer. @@ -6037,6 +8564,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Welcome message is too long + Die Begrüßungsmeldung ist zu lang No comment provided by engineer. @@ -6049,9 +8577,14 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Wenn verfügbar No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Wenn Personen eine Verbindung anfordern, können Sie diese annehmen oder ablehnen. + + When connecting audio and video calls. + Bei der Verbindung über Audio- und Video-Anrufe. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. + Wenn mehrere Netzwerk-Betreiber aktiviert sind, hat keiner von ihnen Metadaten, um zu erfahren, wer mit wem kommuniziert. No comment provided by engineer. @@ -6059,6 +8592,21 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Wenn Sie ein Inkognito-Profil mit Jemandem teilen, wird dieses Profil auch für die Gruppen verwendet, für die Sie von diesem Kontakt eingeladen werden. No comment provided by engineer. + + WiFi + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + Wird in direkten Chats automatisch aktiviert! + No comment provided by engineer. + + + Wired ethernet + Kabelgebundenes Netzwerk + No comment provided by engineer. + With encrypted files and media. Mit verschlüsselten Dateien und Medien. @@ -6074,28 +8622,44 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Mit reduziertem Akkuverbrauch. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für Datei-Server sichtbar sein. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für diese XFTP-Relais sichtbar sein: %@. + alert message + Wrong database passphrase Falsches Datenbank-Passwort No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + Falscher Schlüssel oder unbekannte Verbindung - höchstwahrscheinlich ist diese Verbindung gelöscht worden. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + Falscher Schlüssel oder unbekannte Daten-Paketadresse der Datei - höchstwahrscheinlich wurde die Datei gelöscht. + file error text + Wrong passphrase! Falsches Passwort! No comment provided by engineer. - - XFTP servers + + XFTP server XFTP-Server No comment provided by engineer. - - You - Profil - No comment provided by engineer. - You **must not** use the same database on two devices. + Sie dürfen die selbe Datenbank **nicht** auf zwei Geräten nutzen. No comment provided by engineer. @@ -6118,6 +8682,11 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Sie sind bereits mit %@ verbunden. No comment provided by engineer. + + You are already connected with %@. + Sie sind bereits mit %@ verbunden. + No comment provided by engineer. + You are already connecting to %@. Sie sind bereits mit %@ verbunden. @@ -6165,11 +8734,26 @@ Verbindungsanfrage wiederholen? Sie sind zu der Gruppe eingeladen No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + Sie sind nicht mit diesen Servern verbunden. Zur Auslieferung von Nachrichten an diese Server wird privates Routing genutzt. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. Sie können Anrufe ohne Geräte- und App-Authentifizierung vom Sperrbildschirm aus annehmen. No comment provided by engineer. + + You can change it in Appearance settings. + Kann von Ihnen in den Erscheinungsbild-Einstellungen geändert werden. + No comment provided by engineer. + + + You can configure servers via settings. + Sie können die Server über die Einstellungen konfigurieren. + No comment provided by engineer. + You can create it later Sie können dies später erstellen @@ -6187,6 +8771,7 @@ Verbindungsanfrage wiederholen? You can give another try. + Sie können es nochmal probieren. No comment provided by engineer. @@ -6199,11 +8784,21 @@ Verbindungsanfrage wiederholen? Sie können sie über Einstellungen für Ihre SimpleX-Kontakte sichtbar machen. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ Sie können nun Nachrichten an %@ versenden notification body + + You can send messages to %@ from Archived contacts. + Sie können aus den archivierten Kontakten heraus Nachrichten an %@ versenden. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + Sie können einen Verbindungsnamen festlegen, um sich zu merken, mit wem der Link geteilt wurde. + No comment provided by engineer. + You can set lock screen notification preview via settings. Über die Geräte-Einstellungen können Sie die Benachrichtigungsvorschau im Sperrbildschirm erlauben. @@ -6219,16 +8814,16 @@ Verbindungsanfrage wiederholen? Sie können diese Adresse mit Ihren Kontakten teilen, um sie mit **%@** verbinden zu lassen. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - Sie können Ihre Adresse als Link oder als QR-Code teilen – Jede Person kann sich darüber mit Ihnen verbinden. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app Sie können den Chat über die App-Einstellungen / Datenbank oder durch Neustart der App starten No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + Sie können in der Chat-Liste weiterhin die Unterhaltung mit %@ einsehen. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. Sie können die SimpleX-Sperre über die Einstellungen aktivieren. @@ -6242,23 +8837,23 @@ Verbindungsanfrage wiederholen? You can view invitation link again in connection details. Den Einladungslink können Sie in den Details der Verbindung nochmals sehen. - No comment provided by engineer. + alert message You can't send messages! Sie können keine Nachrichten versenden! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Sie können selbst festlegen, über welche Server Sie Ihre Nachrichten **empfangen** und an Ihre Kontakte **senden** wollen. - No comment provided by engineer. - You could not be verified; please try again. Sie konnten nicht überprüft werden; bitte versuchen Sie es erneut. No comment provided by engineer. + + You decide who can connect. + Sie entscheiden, wer sich mit Ihnen verbinden kann. + No comment provided by engineer. + You have already requested connection via this address! Sie haben über diese Adresse bereits eine Verbindung beantragt! @@ -6271,11 +8866,6 @@ Repeat connection request? Verbindungsanfrage wiederholen? No comment provided by engineer. - - You have no chats - Sie haben keine Chats - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. Sie müssen das Passwort jedes Mal eingeben, wenn die App startet. Es wird nicht auf dem Gerät gespeichert. @@ -6296,11 +8886,26 @@ Verbindungsanfrage wiederholen? Sie sind dieser Gruppe beigetreten. Sie werden mit dem einladenden Gruppenmitglied verbunden. No comment provided by engineer. + + You may migrate the exported database. + Sie können die exportierte Datenbank migrieren. + No comment provided by engineer. + + + You may save the exported archive. + Sie können das exportierte Archiv speichern. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. Sie dürfen die neueste Version Ihrer Chat-Datenbank NUR auf einem Gerät verwenden, andernfalls erhalten Sie möglicherweise keine Nachrichten mehr von einigen Ihrer Kontakte. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + Sie müssen Ihrem Kontakt Anrufe zu Ihnen erlauben, bevor Sie ihn selbst anrufen können. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. Sie müssen Ihrem Kontakt das Senden von Sprachnachrichten erlauben, um diese senden zu können. @@ -6316,6 +8921,11 @@ Verbindungsanfrage wiederholen? Sie haben eine Gruppeneinladung gesendet No comment provided by engineer. + + You should receive notifications. + Sie sollten Benachrichtigungen erhalten. + token info + You will be connected to group when the group host's device is online, please wait or check later! Sie werden mit der Gruppe verbunden, sobald das Endgerät des Gruppen-Hosts online ist. Bitte warten oder schauen Sie später nochmal nach! @@ -6351,6 +8961,11 @@ Verbindungsanfrage wiederholen? Sie können Anrufe und Benachrichtigungen auch von stummgeschalteten Profilen empfangen, solange diese aktiv sind. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + Sie werden von diesem Chat keine Nachrichten mehr erhalten. Der Nachrichtenverlauf bleibt erhalten. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Sie werden von dieser Gruppe keine Nachrichten mehr erhalten. Der Nachrichtenverlauf wird beibehalten. @@ -6371,31 +8986,16 @@ Verbindungsanfrage wiederholen? Sie verwenden ein Inkognito-Profil für diese Gruppe. Um zu verhindern, dass Sie Ihr Hauptprofil teilen, ist in diesem Fall das Einladen von Kontakten nicht erlaubt No comment provided by engineer. - - Your %@ servers - Ihre %@-Server - No comment provided by engineer. - Your ICE servers Ihre ICE-Server No comment provided by engineer. - - Your SMP servers - Ihre SMP-Server - No comment provided by engineer. - Your SimpleX address Ihre SimpleX-Adresse No comment provided by engineer. - - Your XFTP servers - Ihre XFTP-Server - No comment provided by engineer. - Your calls Anrufe @@ -6411,16 +9011,19 @@ Verbindungsanfrage wiederholen? Ihre Chat-Datenbank ist nicht verschlüsselt. Bitte legen Sie ein Passwort fest, um sie zu schützen. No comment provided by engineer. + + Your chat preferences + Ihre Chat-Präferenzen + alert title + Your chat profiles Ihre Chat-Profile No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Damit die Verbindung hergestellt werden kann, muss Ihr Kontakt online sein. -Sie können diese Verbindung abbrechen und den Kontakt entfernen (und es später nochmals mit einem neuen Link versuchen). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Ihre Verbindung wurde auf %@ verschoben. Während Sie auf das Profil weitergeleitet wurden trat aber ein unerwarteter Fehler auf. No comment provided by engineer. @@ -6435,7 +9038,12 @@ Sie können diese Verbindung abbrechen und den Kontakt entfernen (und es später Your contacts will remain connected. - Ihre Kontakte bleiben verbunden. + Ihre Kontakte bleiben weiterhin verbunden. + No comment provided by engineer. + + + Your credentials may be sent unencrypted. + Ihre Anmeldeinformationen können unverschlüsselt versendet werden. No comment provided by engineer. @@ -6455,7 +9063,7 @@ Sie können diese Verbindung abbrechen und den Kontakt entfernen (und es später Your privacy - Ihre Privatsphäre + Privatsphäre No comment provided by engineer. @@ -6468,33 +9076,36 @@ Sie können diese Verbindung abbrechen und den Kontakt entfernen (und es später Ihr Profil **%@** wird geteilt. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt. -SimpleX-Server können Ihr Profil nicht einsehen. + + Your profile is stored on your device and only shared with your contacts. + Das Profil wird nur mit Ihren Kontakten geteilt. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Ihr Profil, Ihre Kontakte und zugestellten Nachrichten werden auf Ihrem Gerät gespeichert. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt. SimpleX-Server können Ihr Profil nicht einsehen. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Ihr Profil wurde geändert. Wenn Sie es speichern, wird das aktualisierte Profil an alle Ihre Kontakte gesendet. + alert message + Your random profile Ihr Zufallsprofil No comment provided by engineer. - - Your server - Ihr Server - No comment provided by engineer. - Your server address Ihre Serveradresse No comment provided by engineer. + + Your servers + Ihre Server + No comment provided by engineer. + Your settings Einstellungen @@ -6535,11 +9146,21 @@ SimpleX-Server können Ihr Profil nicht einsehen. Anruf angenommen call status + + accepted invitation + Einladung angenommen + chat list item title + admin Admin member role + + admins + Administratoren + feature role + agreeing encryption for %@… Verschlüsselung von %@ zustimmen… @@ -6550,6 +9171,11 @@ SimpleX-Server können Ihr Profil nicht einsehen. Verschlüsselung zustimmen… chat item text + + all members + Alle Mitglieder + feature role + always Immer @@ -6560,6 +9186,16 @@ SimpleX-Server können Ihr Profil nicht einsehen. und %lld weitere Ereignisse No comment provided by engineer. + + archived report + Archivierte Meldung + No comment provided by engineer. + + + attempts + Versuche + No comment provided by engineer. + audio call (not e2e encrypted) Audioanruf (nicht E2E verschlüsselt) @@ -6593,13 +9229,19 @@ SimpleX-Server können Ihr Profil nicht einsehen. blocked by admin wurde vom Administrator blockiert - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold fett No comment provided by engineer. + + call + Anrufen + No comment provided by engineer. + call error Fehler bei Anruf @@ -6703,7 +9345,7 @@ SimpleX-Server können Ihr Profil nicht einsehen. connecting… Verbinde… - chat list item title + No comment provided by engineer. connection established @@ -6750,10 +9392,16 @@ SimpleX-Server können Ihr Profil nicht einsehen. Tage time unit + + decryption errors + Entschlüsselungs-Fehler + No comment provided by engineer. + default (%@) - Voreinstellung (%@) - pref value + Default (%@) + delete after time +pref value default (no) @@ -6800,6 +9448,11 @@ SimpleX-Server können Ihr Profil nicht einsehen. Doppelte Nachricht integrity error chat item + + duplicates + Duplikate + No comment provided by engineer. + e2e encrypted E2E-verschlüsselt @@ -6875,9 +9528,14 @@ SimpleX-Server können Ihr Profil nicht einsehen. Fehler No comment provided by engineer. - - event happened - event happened + + expired + Abgelaufen + No comment provided by engineer. + + + forwarded + weitergeleitet No comment provided by engineer. @@ -6905,6 +9563,11 @@ SimpleX-Server können Ihr Profil nicht einsehen. Für die sichere Speicherung des Passworts nach dem Neustart der App und dem Wechsel des Passworts wird der iOS Schlüsselbund verwendet - dies erlaubt den Empfang von Push-Benachrichtigungen. No comment provided by engineer. + + inactive + Inaktiv + No comment provided by engineer. + incognito via contact address link Inkognito über einen Kontaktadressen-Link @@ -6945,6 +9608,11 @@ SimpleX-Server können Ihr Profil nicht einsehen. Einladung zur Gruppe %@ group name + + invite + Einladen + No comment provided by engineer. + invited eingeladen @@ -7000,6 +9668,11 @@ SimpleX-Server können Ihr Profil nicht einsehen. ist der Gruppe beigetreten rcv group event chat item + + message + Nachricht + No comment provided by engineer. + message received Nachricht empfangen @@ -7025,6 +9698,11 @@ SimpleX-Server können Ihr Profil nicht einsehen. Von %@ moderiert marked deleted chat item preview text + + moderator + Moderator + member role + months Monate @@ -7033,7 +9711,7 @@ SimpleX-Server können Ihr Profil nicht einsehen. never nie - No comment provided by engineer. + delete after time new message @@ -7064,8 +9742,8 @@ SimpleX-Server können Ihr Profil nicht einsehen. off Aus enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7082,18 +9760,44 @@ SimpleX-Server können Ihr Profil nicht einsehen. Ein group pref value + + other + Andere + No comment provided by engineer. + + + other errors + Andere Fehler + No comment provided by engineer. + owner Eigentümer member role + + owners + Eigentümer + feature role + peer-to-peer Peer-to-Peer No comment provided by engineer. + + pending + ausstehend + No comment provided by engineer. + + + pending approval + ausstehende Genehmigung + No comment provided by engineer. + quantum resistant e2e encryption + Quantum-resistente E2E-Verschlüsselung chat item text @@ -7106,6 +9810,11 @@ SimpleX-Server können Ihr Profil nicht einsehen. Bestätigung erhalten… No comment provided by engineer. + + rejected + abgelehnt + No comment provided by engineer. + rejected call Abgelehnter Anruf @@ -7136,6 +9845,26 @@ SimpleX-Server können Ihr Profil nicht einsehen. hat Sie aus der Gruppe entfernt rcv group event chat item + + requested to connect + Zur Verbindung aufgefordert + chat list item title + + + saved + abgespeichert + No comment provided by engineer. + + + saved from %@ + abgespeichert von %@ + No comment provided by engineer. + + + search + Suchen + No comment provided by engineer. + sec sek @@ -7161,6 +9890,15 @@ SimpleX-Server können Ihr Profil nicht einsehen. Direktnachricht senden No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + Server-Warteschlangen-Information: %1$@ + +Zuletzt empfangene Nachricht: %2$@ + queue info + set new contact address Es wurde eine neue Kontaktadresse festgelegt @@ -7173,6 +9911,7 @@ SimpleX-Server können Ihr Profil nicht einsehen. standard end-to-end encryption + Standard-Ende-zu-Ende-Verschlüsselung chat item text @@ -7200,11 +9939,21 @@ SimpleX-Server können Ihr Profil nicht einsehen. Unbekannt connection info + + unknown servers + Unbekannte Relais + No comment provided by engineer. + unknown status unbekannter Gruppenmitglieds-Status No comment provided by engineer. + + unprotected + Ungeschützt + No comment provided by engineer. + updated group profile Aktualisiertes Gruppenprofil @@ -7245,6 +9994,11 @@ SimpleX-Server können Ihr Profil nicht einsehen. über Relais No comment provided by engineer. + + video + Video + No comment provided by engineer. + video call (not e2e encrypted) Videoanruf (nicht E2E verschlüsselt) @@ -7270,11 +10024,21 @@ SimpleX-Server können Ihr Profil nicht einsehen. Wochen time unit + + when IP hidden + Wenn die IP-Adresse versteckt ist + No comment provided by engineer. + yes Ja pref value + + you + Profil + No comment provided by engineer. + you are invited to group Sie sind zu der Gruppe eingeladen @@ -7349,7 +10113,7 @@ SimpleX-Server können Ihr Profil nicht einsehen.
- +
@@ -7359,7 +10123,7 @@ SimpleX-Server können Ihr Profil nicht einsehen. SimpleX needs camera access to scan QR codes to connect to other users and for video calls. - SimpleX benötigt Zugriff auf die Kamera, um QR Codes für die Verbindung mit anderen Nutzern zu scannen und Videoanrufe durchzuführen. + SimpleX benötigt Zugriff auf die Kamera, um QR Codes für die Verbindung mit anderen Benutzern zu scannen und Videoanrufe durchzuführen. Privacy - Camera Usage Description @@ -7379,14 +10143,14 @@ SimpleX-Server können Ihr Profil nicht einsehen. SimpleX needs access to Photo Library for saving captured and received media - SimpleX benötigt Zugriff auf das Fotoalbum, um selbst gemachte oder empfangene Bilder zu speichern + SimpleX benötigt Zugriff auf das Fotoalbum, um selbst gemachte oder heruntergeladene Bilder zu speichern Privacy - Photo Library Additions Usage Description
- +
@@ -7401,9 +10165,255 @@ SimpleX-Server können Ihr Profil nicht einsehen. Copyright © 2022 SimpleX Chat. All rights reserved. - Copyright © 2022 SimpleX Chat. All rights reserved. + Copyright © 2024 SimpleX Chat. All rights reserved. Copyright (human-readable)
+ +
+ +
+ + + %d new events + %d neue Ereignisse + notification body + + + From %d chat(s) + Von %d Chat(s) + notification body + + + From: %@ + Von: %@ + notification body + + + New events + Neue Ereignisse + notification + + + New messages + Neue Nachrichten + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright © 2025 SimpleX Chat. Alle Rechte vorbehalten. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + Die App ist gesperrt! + No comment provided by engineer. + + + Cancel + Abbrechen + No comment provided by engineer. + + + Cannot access keychain to save database password + Es ist nicht möglich, auf den Schlüsselbund zuzugreifen, um das Datenbankpasswort zu speichern + No comment provided by engineer. + + + Cannot forward message + Nachricht kann nicht weitergeleitet werden + No comment provided by engineer. + + + Comment + Kommentieren + No comment provided by engineer. + + + Currently maximum supported file size is %@. + Die maximal erlaubte Dateigröße beträgt aktuell %@. + No comment provided by engineer. + + + Database downgrade required + Datenbank-Herunterstufung ist erforderlich + No comment provided by engineer. + + + Database encrypted! + Datenbank ist verschlüsselt! + No comment provided by engineer. + + + Database error + Datenbankfehler + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + Das Datenbank-Passwort unterscheidet sich vom im Schlüsselbund gespeicherten. + No comment provided by engineer. + + + Database passphrase is required to open chat. + Um den Chat zu öffnen, ist ein Datenbank-Passwort ist erforderlich. + No comment provided by engineer. + + + Database upgrade required + Datenbank-Aktualisierung erforderlich + No comment provided by engineer. + + + Error preparing file + Fehler beim Vorbereiten der Datei + No comment provided by engineer. + + + Error preparing message + Fehler beim Vorbereiten der Nachricht + No comment provided by engineer. + + + Error: %@ + Fehler: %@ + No comment provided by engineer. + + + File error + Datei-Fehler + No comment provided by engineer. + + + Incompatible database version + Datenbank-Version nicht kompatibel + No comment provided by engineer. + + + Invalid migration confirmation + Migrations-Bestätigung ungültig + No comment provided by engineer. + + + Keychain error + Schlüsselbund-Fehler + No comment provided by engineer. + + + Large file! + Große Datei! + No comment provided by engineer. + + + No active profile + Kein aktives Profil + No comment provided by engineer. + + + Ok + OK + No comment provided by engineer. + + + Open the app to downgrade the database. + Öffnen Sie die App, um die Datenbank herunterzustufen. + No comment provided by engineer. + + + Open the app to upgrade the database. + Öffnen Sie die App, um die Datenbank zu aktualisieren. + No comment provided by engineer. + + + Passphrase + Passwort + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Bitte erstellen Sie ein Profil in der SimpleX-App + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Diese Nachricht ist wegen der gewählten Chat-Einstellungen nicht erlaubt. + No comment provided by engineer. + + + Sending a message takes longer than expected. + Das Senden einer Nachricht dauert länger als erwartet. + No comment provided by engineer. + + + Sending message… + Nachricht wird gesendet… + No comment provided by engineer. + + + Share + Teilen + No comment provided by engineer. + + + Slow network? + Langsames Netzwerk? + No comment provided by engineer. + + + Unknown database error: %@ + Unbekannter Datenbankfehler: %@ + No comment provided by engineer. + + + Unsupported format + Nicht unterstütztes Format + No comment provided by engineer. + + + Wait + Warten + No comment provided by engineer. + + + Wrong database passphrase + Falsches Datenbank-Passwort + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + Sie können das Teilen in den Einstellungen zu Datenschutz & Sicherheit / SimpleX-Sperre erlauben. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/de.xcloc/contents.json b/apps/ios/SimpleX Localizations/de.xcloc/contents.json index 11924b71f5..e8d71cf38c 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/de.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "de", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/el.xcloc/Localized Contents/el.xliff b/apps/ios/SimpleX Localizations/el.xcloc/Localized Contents/el.xliff index 18051ae350..fc1846942c 100644 --- a/apps/ios/SimpleX Localizations/el.xcloc/Localized Contents/el.xliff +++ b/apps/ios/SimpleX Localizations/el.xcloc/Localized Contents/el.xliff @@ -186,20 +186,16 @@ Available in v5.1 ) No comment provided by engineer.
- - **Add new contact**: to create your one-time QR Code or link for your contact. - No comment provided by engineer. - **Create link / QR code** for your contact to use. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. No comment provided by engineer. @@ -210,8 +206,8 @@ Available in v5.1 **Please note**: you will NOT be able to recover or change passphrase if you lose it. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. No comment provided by engineer. @@ -336,8 +332,8 @@ Available in v5.1 Add servers by scanning QR codes. No comment provided by engineer. - - Add server… + + Add server No comment provided by engineer. @@ -1128,8 +1124,8 @@ Available in v5.1 Direct messages chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited. No comment provided by engineer. @@ -1144,8 +1140,8 @@ Available in v5.1 Disappearing messages are prohibited in this chat. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. No comment provided by engineer. @@ -1580,16 +1576,16 @@ Available in v5.1 Group members can irreversibly delete sent messages. No comment provided by engineer. - - Group members can send direct messages. + + Members can send direct messages. No comment provided by engineer. - - Group members can send disappearing messages. + + Members can send disappearing messages. No comment provided by engineer. - - Group members can send voice messages. + + Members can send voice messages. No comment provided by engineer. @@ -1708,8 +1704,8 @@ Available in v5.1 Immediately No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam No comment provided by engineer. @@ -1821,8 +1817,8 @@ Available in v5.1 Irreversible message deletion is prohibited in this chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. No comment provided by engineer. @@ -2016,8 +2012,8 @@ Available in v5.1 Migration is completed No comment provided by engineer. - - Migrations: %@ + + Migrations: No comment provided by engineer. @@ -2174,8 +2170,8 @@ Available in v5.1 Onion hosts will not be used. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only client devices store user profiles, contacts, groups, and messages. No comment provided by engineer. @@ -2234,8 +2230,8 @@ Available in v5.1 Open user profiles authentication reason - - Open-source protocol and code – anybody can run the servers. + + Anybody can host servers. No comment provided by engineer. @@ -2290,8 +2286,8 @@ Available in v5.1 Paste the link you received into the box below to connect with your contact. No comment provided by engineer. - - People can connect to you only via the links you share. + + You decide who can connect. No comment provided by engineer. @@ -2994,8 +2990,8 @@ Available in v5.1 Thanks to the users – contribute via Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. + + No user identifiers. No comment provided by engineer. @@ -3039,16 +3035,16 @@ It can happen because of some bug or when the connection is compromised.The message will be marked as moderated for all members. No comment provided by engineer. - - The next generation of private messaging + + The future of messaging No comment provided by engineer. The old database was not removed during the migration, it can be deleted. No comment provided by engineer. - - The profile is only shared with your contacts. + + Your profile is stored on your device and only shared with your contacts. No comment provided by engineer. @@ -3111,8 +3107,8 @@ It can happen because of some bug or when the connection is compromised.To make a new connection No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. No comment provided by engineer. @@ -3337,8 +3333,8 @@ To connect, please ask your contact to create another connection link and check Voice messages are prohibited in this chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. No comment provided by engineer. @@ -3478,10 +3474,6 @@ SimpleX Lock must be enabled. You can't send messages! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - You could not be verified; please try again. No comment provided by engineer. @@ -4200,7 +4192,7 @@ SimpleX servers cannot see your profile. ## In reply to - ## Ως απαντηση σε + ## Ως απάντηση σε copied message info diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff index 39ee4e2a9b..fd71e0dee6 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (can be copied) @@ -127,6 +100,11 @@ %@ is verified No comment provided by engineer. + + %@ server + %@ server + No comment provided by engineer. + %@ servers %@ servers @@ -142,6 +120,11 @@ %@ wants to connect! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ and %lld members @@ -162,11 +145,36 @@ %d days time interval + + %d file(s) are still being downloaded. + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d hours time interval + + %d messages not forwarded + %d messages not forwarded + alert title + %d min %d min @@ -182,6 +190,11 @@ %d sec time interval + + %d seconds(s) + %d seconds(s) + delete after time + %d skipped message(s) %d skipped message(s) @@ -252,11 +265,6 @@ %lld new interface languages No comment provided by engineer. - - %lld second(s) - %lld second(s) - No comment provided by engineer. - %lld seconds %lld seconds @@ -307,11 +315,6 @@ %u messages skipped. No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) (new) @@ -322,19 +325,9 @@ (this device v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - **Add contact**: to create a new invitation link, or connect via a link you received. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Add new contact**: to create your one-time QR Code or link for your contact. + + **Create 1-time link**: to create and share a new invitation link. + **Create 1-time link**: to create and share a new invitation link. No comment provided by engineer. @@ -342,14 +335,14 @@ **Create group**: to create a new group. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. No comment provided by engineer. @@ -362,9 +355,14 @@ **Please note**: you will NOT be able to recover or change passphrase if you lose it. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. + No comment provided by engineer. + + + **Scan / Paste link**: to connect via a link you received. + **Scan / Paste link**: to connect via a link you received. No comment provided by engineer. @@ -392,11 +390,6 @@ \*bold* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -433,11 +426,6 @@ - editing history. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 sec @@ -451,7 +439,8 @@ 1 day 1 day - time interval + delete after time +time interval 1 hour @@ -466,12 +455,29 @@ 1 month 1 month - time interval + delete after time +time interval 1 week 1 week - time interval + delete after time +time interval + + + 1 year + 1 year + delete after time + + + 1-time link + 1-time link + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + 1-time link can be used *with one contact only* - share in person or via any messenger. + No comment provided by engineer. 5 minutes @@ -488,11 +494,6 @@ 30 seconds No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -542,31 +543,32 @@ Abort changing address? No comment provided by engineer. - - About SimpleX - About SimpleX - No comment provided by engineer. - About SimpleX Chat About SimpleX Chat No comment provided by engineer. - - About SimpleX address - About SimpleX address + + About operators + About operators No comment provided by engineer. - - Accent color - Accent color + + Accent + Accent No comment provided by engineer. Accept Accept accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Accept conditions + No comment provided by engineer. Accept connection request? @@ -581,21 +583,47 @@ Accept incognito Accept incognito - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + Accepted conditions + No comment provided by engineer. + + + Acknowledged + Acknowledged + No comment provided by engineer. + + + Acknowledgement errors + Acknowledgement errors + No comment provided by engineer. + + + Active + Active + token status text + + + Active connections + Active connections + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. No comment provided by engineer. - - Add contact - Add contact + + Add friends + Add friends No comment provided by engineer. - - Add preset servers - Add preset servers + + Add list + Add list No comment provided by engineer. @@ -603,14 +631,19 @@ Add profile No comment provided by engineer. + + Add server + Add server + No comment provided by engineer. + Add servers by scanning QR codes. Add servers by scanning QR codes. No comment provided by engineer. - - Add server… - Add server… + + Add team members + Add team members No comment provided by engineer. @@ -618,11 +651,46 @@ Add to another device No comment provided by engineer. + + Add to list + Add to list + No comment provided by engineer. + Add welcome message Add welcome message No comment provided by engineer. + + Add your team members to the conversations. + Add your team members to the conversations. + No comment provided by engineer. + + + Added media & file servers + Added media & file servers + No comment provided by engineer. + + + Added message servers + Added message servers + No comment provided by engineer. + + + Additional accent + Additional accent + No comment provided by engineer. + + + Additional accent 2 + Additional accent 2 + No comment provided by engineer. + + + Additional secondary + Additional secondary + No comment provided by engineer. + Address Address @@ -633,6 +701,16 @@ Address change will be aborted. Old receiving address will be used. No comment provided by engineer. + + Address or 1-time link? + Address or 1-time link? + No comment provided by engineer. + + + Address settings + Address settings + No comment provided by engineer. + Admins can block a member for all. Admins can block a member for all. @@ -648,6 +726,16 @@ Advanced network settings No comment provided by engineer. + + Advanced settings + Advanced settings + No comment provided by engineer. + + + All + All + No comment provided by engineer. + All app data is deleted. All app data is deleted. @@ -658,16 +746,31 @@ All chats and messages will be deleted - this cannot be undone! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + All chats will be removed from the list %@, and the list deleted. + alert message + All data is erased when it is entered. All data is erased when it is entered. No comment provided by engineer. + + All data is kept private on your device. + All data is kept private on your device. + No comment provided by engineer. + All group members will remain connected. All group members will remain connected. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! All messages will be deleted - this cannot be undone! @@ -683,6 +786,21 @@ All new messages from %@ will be hidden! No comment provided by engineer. + + All profiles + All profiles + profile dropdown + + + All reports will be archived for you. + All reports will be archived for you. + No comment provided by engineer. + + + All servers + All servers + No comment provided by engineer. + All your contacts will remain connected. All your contacts will remain connected. @@ -708,11 +826,21 @@ Allow calls only if your contact allows them. No comment provided by engineer. + + Allow calls? + Allow calls? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Allow disappearing messages only if your contact allows it to you. No comment provided by engineer. + + Allow downgrade + Allow downgrade + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Allow irreversible message deletion only if your contact allows it to you. (24 hours) @@ -738,11 +866,26 @@ Allow sending disappearing messages. No comment provided by engineer. + + Allow sharing + Allow sharing + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Allow to irreversibly delete sent messages. (24 hours) No comment provided by engineer. + + Allow to report messsages to moderators. + Allow to report messsages to moderators. + No comment provided by engineer. + + + Allow to send SimpleX links. + Allow to send SimpleX links. + No comment provided by engineer. + Allow to send files and media. Allow to send files and media. @@ -803,6 +946,11 @@ Already joining the group! No comment provided by engineer. + + Always use private routing. + Always use private routing. + No comment provided by engineer. + Always use relay Always use relay @@ -813,11 +961,21 @@ An empty chat profile with the provided name is created, and the app opens as usual. No comment provided by engineer. + + Another reason + Another reason + report reason + Answer call Answer call No comment provided by engineer. + + Anybody can host servers. + Anybody can host servers. + No comment provided by engineer. + App build: %@ App build: %@ @@ -833,6 +991,11 @@ App encrypts new local files (except videos). No comment provided by engineer. + + App group: + App group: + No comment provided by engineer. + App icon App icon @@ -848,6 +1011,11 @@ App passcode is replaced with self-destruct passcode. No comment provided by engineer. + + App session + App session + No comment provided by engineer. + App version App version @@ -868,11 +1036,56 @@ Apply No comment provided by engineer. + + Apply to + Apply to + No comment provided by engineer. + + + Archive + Archive + No comment provided by engineer. + + + Archive %lld reports? + Archive %lld reports? + No comment provided by engineer. + + + Archive all reports? + Archive all reports? + No comment provided by engineer. + Archive and upload Archive and upload No comment provided by engineer. + + Archive contacts to chat later. + Archive contacts to chat later. + No comment provided by engineer. + + + Archive report + Archive report + No comment provided by engineer. + + + Archive report? + Archive report? + No comment provided by engineer. + + + Archive reports + Archive reports + swipe action + + + Archived contacts + Archived contacts + No comment provided by engineer. + Archiving database Archiving database @@ -938,11 +1151,21 @@ Auto-accept images No comment provided by engineer. + + Auto-accept settings + Auto-accept settings + alert title + Back Back No comment provided by engineer. + + Background + Background + No comment provided by engineer. + Bad desktop address Bad desktop address @@ -958,16 +1181,61 @@ Bad message hash No comment provided by engineer. + + Better calls + Better calls + No comment provided by engineer. + Better groups Better groups No comment provided by engineer. + + Better groups performance + Better groups performance + No comment provided by engineer. + + + Better message dates. + Better message dates. + No comment provided by engineer. + Better messages Better messages No comment provided by engineer. + + Better networking + Better networking + No comment provided by engineer. + + + Better notifications + Better notifications + No comment provided by engineer. + + + Better privacy and security + Better privacy and security + No comment provided by engineer. + + + Better security ✅ + Better security ✅ + No comment provided by engineer. + + + Better user experience + Better user experience + No comment provided by engineer. + + + Black + Black + No comment provided by engineer. + Block Block @@ -1003,6 +1271,16 @@ Blocked by admin No comment provided by engineer. + + Blur for better privacy. + Blur for better privacy. + No comment provided by engineer. + + + Blur media + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Both you and your contact can add message reactions. @@ -1033,11 +1311,35 @@ Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + Business address + No comment provided by engineer. + + + Business chats + Business chats + No comment provided by engineer. + + + Businesses + Businesses + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + No comment provided by engineer. + Call already ended! Call already ended! @@ -1048,11 +1350,26 @@ Calls No comment provided by engineer. + + Calls prohibited! + Calls prohibited! + No comment provided by engineer. + Camera not available Camera not available No comment provided by engineer. + + Can't call contact + Can't call contact + No comment provided by engineer. + + + Can't call member + Can't call member + No comment provided by engineer. + Can't invite contact! Can't invite contact! @@ -1063,10 +1380,16 @@ Can't invite contacts! No comment provided by engineer. + + Can't message member + Can't message member + No comment provided by engineer. + Cancel Cancel - No comment provided by engineer. + alert action +alert button Cancel migration @@ -1078,9 +1401,24 @@ Cannot access keychain to save database password No comment provided by engineer. + + Cannot forward message + Cannot forward message + No comment provided by engineer. + Cannot receive file Cannot receive file + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Capacity exceeded - recipient did not receive previously sent messages. + snd error text + + + Cellular + Cellular No comment provided by engineer. @@ -1088,6 +1426,16 @@ Change No comment provided by engineer. + + Change automatic message deletion? + Change automatic message deletion? + alert title + + + Change chat profiles + Change chat profiles + authentication reason + Change database passphrase? Change database passphrase? @@ -1132,11 +1480,26 @@ Change self-destruct passcode Change self-destruct passcode authentication reason - set passcode view +set passcode view - - Chat archive - Chat archive + + Chat + Chat + No comment provided by engineer. + + + Chat already exists + Chat already exists + No comment provided by engineer. + + + Chat already exists! + Chat already exists! + No comment provided by engineer. + + + Chat colors + Chat colors No comment provided by engineer. @@ -1154,6 +1517,11 @@ Chat database deleted No comment provided by engineer. + + Chat database exported + Chat database exported + No comment provided by engineer. + Chat database imported Chat database imported @@ -1174,6 +1542,11 @@ Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat. No comment provided by engineer. + + Chat list + Chat list + No comment provided by engineer. + Chat migrated! Chat migrated! @@ -1184,15 +1557,50 @@ Chat preferences No comment provided by engineer. + + Chat preferences were changed. + Chat preferences were changed. + alert message + + + Chat profile + Chat profile + No comment provided by engineer. + + + Chat theme + Chat theme + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + Chat will be deleted for all members - this cannot be undone! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + Chat will be deleted for you - this cannot be undone! + No comment provided by engineer. + Chats Chats No comment provided by engineer. + + Check messages every 20 min. + Check messages every 20 min. + No comment provided by engineer. + + + Check messages when allowed. + Check messages when allowed. + No comment provided by engineer. + Check server address and try again. Check server address and try again. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1214,10 +1622,25 @@ Choose from library No comment provided by engineer. + + Chunks deleted + Chunks deleted + No comment provided by engineer. + + + Chunks downloaded + Chunks downloaded + No comment provided by engineer. + + + Chunks uploaded + Chunks uploaded + No comment provided by engineer. + Clear Clear - No comment provided by engineer. + swipe action Clear conversation @@ -1229,6 +1652,16 @@ Clear conversation? No comment provided by engineer. + + Clear group? + Clear group? + No comment provided by engineer. + + + Clear or delete group? + Clear or delete group? + No comment provided by engineer. + Clear private notes? Clear private notes? @@ -1239,11 +1672,21 @@ Clear verification No comment provided by engineer. - - Colors - Colors + + Color chats with the new themes. + Color chats with the new themes. No comment provided by engineer. + + Color mode + Color mode + No comment provided by engineer. + + + Community guidelines violation + Community guidelines violation + report reason + Compare file Compare file @@ -1254,11 +1697,56 @@ Compare security codes with your contacts. No comment provided by engineer. + + Completed + Completed + No comment provided by engineer. + + + Conditions accepted on: %@. + Conditions accepted on: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + Conditions are accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + Conditions are already accepted for these operator(s): **%@**. + No comment provided by engineer. + + + Conditions of use + Conditions of use + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + Conditions will be accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + Conditions will be accepted on: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + Conditions will be automatically accepted for enabled operators on: %@. + No comment provided by engineer. + Configure ICE servers Configure ICE servers No comment provided by engineer. + + Configure server operators + Configure server operators + No comment provided by engineer. + Confirm Confirm @@ -1269,11 +1757,21 @@ Confirm Passcode No comment provided by engineer. + + Confirm contact deletion? + Confirm contact deletion? + No comment provided by engineer. + Confirm database upgrades Confirm database upgrades No comment provided by engineer. + + Confirm files from unknown servers. + Confirm files from unknown servers. + No comment provided by engineer. + Confirm network settings Confirm network settings @@ -1299,6 +1797,11 @@ Confirm upload No comment provided by engineer. + + Confirmed + Confirmed + token status text + Connect Connect @@ -1319,6 +1822,11 @@ Connect to desktop No comment provided by engineer. + + Connect to your friends faster. + Connect to your friends faster. + No comment provided by engineer. + Connect to yourself? Connect to yourself? @@ -1358,16 +1866,31 @@ This is your own one-time link! Connect with %@ No comment provided by engineer. + + Connected + Connected + No comment provided by engineer. + Connected desktop Connected desktop No comment provided by engineer. + + Connected servers + Connected servers + No comment provided by engineer. + Connected to desktop Connected to desktop No comment provided by engineer. + + Connecting + Connecting + No comment provided by engineer. + Connecting to server… Connecting to server… @@ -1378,6 +1901,11 @@ This is your own one-time link! Connecting to server… (error: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + Connecting to contact, please wait or check later! + No comment provided by engineer. + Connecting to desktop Connecting to desktop @@ -1388,6 +1916,16 @@ This is your own one-time link! Connection No comment provided by engineer. + + Connection and servers status. + Connection and servers status. + No comment provided by engineer. + + + Connection blocked + Connection blocked + No comment provided by engineer. + Connection error Connection error @@ -1398,11 +1936,38 @@ This is your own one-time link! Connection error (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + Connection is blocked by server operator: +%@ + No comment provided by engineer. + + + Connection not ready. + Connection not ready. + No comment provided by engineer. + + + Connection notifications + Connection notifications + No comment provided by engineer. + Connection request sent! Connection request sent! No comment provided by engineer. + + Connection requires encryption renegotiation. + Connection requires encryption renegotiation. + No comment provided by engineer. + + + Connection security + Connection security + No comment provided by engineer. + Connection terminated Connection terminated @@ -1413,6 +1978,16 @@ This is your own one-time link! Connection timeout No comment provided by engineer. + + Connection with desktop stopped + Connection with desktop stopped + No comment provided by engineer. + + + Connections + Connections + No comment provided by engineer. + Contact allows Contact allows @@ -1423,6 +1998,11 @@ This is your own one-time link! Contact already exists No comment provided by engineer. + + Contact deleted! + Contact deleted! + No comment provided by engineer. + Contact hidden: Contact hidden: @@ -1433,9 +2013,9 @@ This is your own one-time link! Contact is connected notification - - Contact is not connected yet! - Contact is not connected yet! + + Contact is deleted. + Contact is deleted. No comment provided by engineer. @@ -1448,6 +2028,11 @@ This is your own one-time link! Contact preferences No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + Contact will be deleted - this cannot be undone! + No comment provided by engineer. + Contacts Contacts @@ -1458,21 +2043,41 @@ This is your own one-time link! Contacts can mark messages for deletion; you will be able to view them. No comment provided by engineer. + + Content violates conditions of use + Content violates conditions of use + blocking reason + Continue Continue No comment provided by engineer. + + Conversation deleted! + Conversation deleted! + No comment provided by engineer. + Copy Copy - chat item action + No comment provided by engineer. + + + Copy error + Copy error + No comment provided by engineer. Core version: v%@ Core version: v%@ No comment provided by engineer. + + Corner + Corner + No comment provided by engineer. + Correct name to %@? Correct name to %@? @@ -1483,6 +2088,11 @@ This is your own one-time link! Create No comment provided by engineer. + + Create 1-time link + Create 1-time link + No comment provided by engineer. + Create SimpleX address Create SimpleX address @@ -1493,11 +2103,6 @@ This is your own one-time link! Create a group using a random profile. No comment provided by engineer. - - Create an address to let people connect with you. - Create an address to let people connect with you. - No comment provided by engineer. - Create file Create file @@ -1518,6 +2123,11 @@ This is your own one-time link! Create link No comment provided by engineer. + + Create list + Create list + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 @@ -1543,6 +2153,11 @@ This is your own one-time link! Create your profile No comment provided by engineer. + + Created + Created + No comment provided by engineer. + Created at Created at @@ -1553,11 +2168,6 @@ This is your own one-time link! Created at: %@ copied message info - - Created on %@ - Created on %@ - No comment provided by engineer. - Creating archive link Creating archive link @@ -1573,11 +2183,21 @@ This is your own one-time link! Current Passcode No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + Current conditions text couldn't be loaded, you can review conditions via this link: + No comment provided by engineer. + Current passphrase… Current passphrase… No comment provided by engineer. + + Current profile + Current profile + No comment provided by engineer. + Currently maximum supported file size is %@. Currently maximum supported file size is %@. @@ -1588,11 +2208,26 @@ This is your own one-time link! Custom time No comment provided by engineer. + + Customizable message shape. + Customizable message shape. + No comment provided by engineer. + + + Customize theme + Customize theme + No comment provided by engineer. + Dark Dark No comment provided by engineer. + + Dark mode colors + Dark mode colors + No comment provided by engineer. + Database ID Database ID @@ -1691,6 +2326,11 @@ This is your own one-time link! Database will be migrated when the app restarts No comment provided by engineer. + + Debug delivery + Debug delivery + No comment provided by engineer. + Decentralized Decentralized @@ -1704,18 +2344,19 @@ This is your own one-time link! Delete Delete - chat item action + alert action +swipe action + + + Delete %lld messages of members? + Delete %lld messages of members? + No comment provided by engineer. Delete %lld messages? Delete %lld messages? No comment provided by engineer. - - Delete Contact - Delete Contact - No comment provided by engineer. - Delete address Delete address @@ -1741,14 +2382,14 @@ This is your own one-time link! Delete and notify contact No comment provided by engineer. - - Delete archive - Delete archive + + Delete chat + Delete chat No comment provided by engineer. - - Delete chat archive? - Delete chat archive? + + Delete chat messages from your device. + Delete chat messages from your device. No comment provided by engineer. @@ -1761,6 +2402,11 @@ This is your own one-time link! Delete chat profile? No comment provided by engineer. + + Delete chat? + Delete chat? + No comment provided by engineer. + Delete connection Delete connection @@ -1771,11 +2417,9 @@ This is your own one-time link! Delete contact No comment provided by engineer. - - Delete contact? -This cannot be undone! - Delete contact? -This cannot be undone! + + Delete contact? + Delete contact? No comment provided by engineer. @@ -1838,6 +2482,11 @@ This cannot be undone! Delete link? No comment provided by engineer. + + Delete list? + Delete list? + alert title + Delete member message? Delete member message? @@ -1851,7 +2500,7 @@ This cannot be undone! Delete messages Delete messages - No comment provided by engineer. + alert button Delete messages after @@ -1868,9 +2517,9 @@ This cannot be undone! Delete old database? No comment provided by engineer. - - Delete pending connection - Delete pending connection + + Delete or moderate up to 200 messages. + Delete or moderate up to 200 messages. No comment provided by engineer. @@ -1888,11 +2537,31 @@ This cannot be undone! Delete queue server test step + + Delete report + Delete report + No comment provided by engineer. + + + Delete up to 20 messages at once. + Delete up to 20 messages at once. + No comment provided by engineer. + Delete user profile? Delete user profile? No comment provided by engineer. + + Delete without notification + Delete without notification + No comment provided by engineer. + + + Deleted + Deleted + No comment provided by engineer. + Deleted at Deleted at @@ -1903,6 +2572,16 @@ This cannot be undone! Deleted at: %@ copied message info + + Deletion errors + Deletion errors + No comment provided by engineer. + + + Delivered even when Apple drops them. + Delivered even when Apple drops them. + No comment provided by engineer. + Delivery Delivery @@ -1938,11 +2617,41 @@ This cannot be undone! Desktop devices No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + + + Destination server error: %@ + Destination server error: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + + + Detailed statistics + Detailed statistics + No comment provided by engineer. + + + Details + Details + No comment provided by engineer. + Develop Develop No comment provided by engineer. + + Developer options + Developer options + No comment provided by engineer. + Developer tools Developer tools @@ -1973,9 +2682,14 @@ This cannot be undone! Direct messages chat feature - - Direct messages between members are prohibited in this group. - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + Direct messages between members are prohibited in this chat. + No comment provided by engineer. + + + Direct messages between members are prohibited. + Direct messages between members are prohibited. No comment provided by engineer. @@ -1988,11 +2702,26 @@ This cannot be undone! Disable SimpleX Lock authentication reason + + Disable automatic message deletion? + Disable automatic message deletion? + alert title + + + Disable delete messages + Disable delete messages + alert button + Disable for all Disable for all No comment provided by engineer. + + Disabled + Disabled + No comment provided by engineer. + Disappearing message Disappearing message @@ -2008,9 +2737,9 @@ This cannot be undone! Disappearing messages are prohibited in this chat. No comment provided by engineer. - - Disappearing messages are prohibited in this group. - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. + Disappearing messages are prohibited. No comment provided by engineer. @@ -2043,11 +2772,21 @@ This cannot be undone! Discover via local network No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + Do NOT send messages directly, even if your or destination server does not support private routing. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. Do NOT use SimpleX for emergency calls. No comment provided by engineer. + + Do NOT use private routing. + Do NOT use private routing. + No comment provided by engineer. + Do it later Do it later @@ -2058,6 +2797,16 @@ This cannot be undone! Do not send history to new members. No comment provided by engineer. + + Do not use credentials with proxy. + Do not use credentials with proxy. + No comment provided by engineer. + + + Documents: + Documents: + No comment provided by engineer. + Don't create address Don't create address @@ -2068,16 +2817,37 @@ This cannot be undone! Don't enable No comment provided by engineer. + + Don't miss important messages. + Don't miss important messages. + No comment provided by engineer. + Don't show again Don't show again No comment provided by engineer. + + Done + Done + No comment provided by engineer. + Downgrade and open chat Downgrade and open chat No comment provided by engineer. + + Download + Download + alert button +chat item action + + + Download errors + Download errors + No comment provided by engineer. + Download failed Download failed @@ -2088,6 +2858,21 @@ This cannot be undone! Download file server test step + + Download files + Download files + alert action + + + Downloaded + Downloaded + No comment provided by engineer. + + + Downloaded files + Downloaded files + No comment provided by engineer. + Downloading archive Downloading archive @@ -2108,6 +2893,11 @@ This cannot be undone! Duration No comment provided by engineer. + + E2E encrypted notifications. + E2E encrypted notifications. + No comment provided by engineer. + Edit Edit @@ -2128,6 +2918,11 @@ This cannot be undone! Enable (keep overrides) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + Enable Flux in Network & servers settings for better metadata privacy. + No comment provided by engineer. + Enable SimpleX Lock Enable SimpleX Lock @@ -2141,7 +2936,7 @@ This cannot be undone! Enable automatic message deletion? Enable automatic message deletion? - No comment provided by engineer. + alert title Enable camera access @@ -2188,6 +2983,16 @@ This cannot be undone! Enable self-destruct passcode set passcode view + + Enabled + Enabled + No comment provided by engineer. + + + Enabled for + Enabled for + No comment provided by engineer. + Encrypt Encrypt @@ -2258,6 +3063,11 @@ This cannot be undone! Encryption re-negotiation failed. No comment provided by engineer. + + Encryption renegotiation in progress. + Encryption renegotiation in progress. + No comment provided by engineer. + Enter Passcode Enter Passcode @@ -2323,31 +3133,36 @@ This cannot be undone! Error aborting address change No comment provided by engineer. + + Error accepting conditions + Error accepting conditions + alert title + Error accepting contact request Error accepting contact request No comment provided by engineer. - - Error accessing database file - Error accessing database file - No comment provided by engineer. - Error adding member(s) Error adding member(s) No comment provided by engineer. - - Error allowing contact PQ encryption - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + Error adding server + alert title Error changing address Error changing address No comment provided by engineer. + + Error changing connection profile + Error changing connection profile + No comment provided by engineer. + Error changing role Error changing role @@ -2358,6 +3173,21 @@ This cannot be undone! Error changing setting No comment provided by engineer. + + Error changing to incognito! + Error changing to incognito! + No comment provided by engineer. + + + Error checking token status + Error checking token status + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Error creating address @@ -2373,6 +3203,11 @@ This cannot be undone! Error creating group link No comment provided by engineer. + + Error creating list + Error creating list + alert title + Error creating member contact Error creating member contact @@ -2388,6 +3223,11 @@ This cannot be undone! Error creating profile! No comment provided by engineer. + + Error creating report + Error creating report + No comment provided by engineer. + Error decrypting file Error decrypting file @@ -2408,11 +3248,6 @@ This cannot be undone! Error deleting connection No comment provided by engineer. - - Error deleting contact - Error deleting contact - No comment provided by engineer. - Error deleting database Error deleting database @@ -2458,6 +3293,11 @@ This cannot be undone! Error exporting chat database No comment provided by engineer. + + Error exporting theme: %@ + Error exporting theme: %@ + No comment provided by engineer. + Error importing chat database Error importing chat database @@ -2468,9 +3308,14 @@ This cannot be undone! Error joining group No comment provided by engineer. - - Error loading %@ servers - Error loading %@ servers + + Error loading servers + Error loading servers + alert title + + + Error migrating settings + Error migrating settings No comment provided by engineer. @@ -2481,16 +3326,36 @@ This cannot be undone! Error receiving file Error receiving file + alert title + + + Error reconnecting server + Error reconnecting server No comment provided by engineer. + + Error reconnecting servers + Error reconnecting servers + No comment provided by engineer. + + + Error registering for notifications + Error registering for notifications + alert title + Error removing member Error removing member No comment provided by engineer. - - Error saving %@ servers - Error saving %@ servers + + Error reordering lists + Error reordering lists + alert title + + + Error resetting statistics + Error resetting statistics No comment provided by engineer. @@ -2498,6 +3363,11 @@ This cannot be undone! Error saving ICE servers No comment provided by engineer. + + Error saving chat list + Error saving chat list + alert title + Error saving group profile Error saving group profile @@ -2513,6 +3383,11 @@ This cannot be undone! Error saving passphrase to keychain No comment provided by engineer. + + Error saving servers + Error saving servers + alert title + Error saving settings Error saving settings @@ -2558,16 +3433,26 @@ This cannot be undone! Error stopping chat No comment provided by engineer. + + Error switching profile + Error switching profile + No comment provided by engineer. + Error switching profile! Error switching profile! - No comment provided by engineer. + alertTitle Error synchronizing connection Error synchronizing connection No comment provided by engineer. + + Error testing server connection + Error testing server connection + No comment provided by engineer. + Error updating group link Error updating group link @@ -2578,6 +3463,11 @@ This cannot be undone! Error updating message No comment provided by engineer. + + Error updating server + Error updating server + alert title + Error updating settings Error updating settings @@ -2606,7 +3496,9 @@ This cannot be undone! Error: %@ Error: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2618,6 +3510,16 @@ This cannot be undone! Error: no database file No comment provided by engineer. + + Errors + Errors + No comment provided by engineer. + + + Errors in servers configuration. + Errors in servers configuration. + servers error + Even when disabled in the conversation. Even when disabled in the conversation. @@ -2633,6 +3535,11 @@ This cannot be undone! Expand chat item action + + Expired + Expired + token status text + Export database Export database @@ -2643,6 +3550,11 @@ This cannot be undone! Export error: No comment provided by engineer. + + Export theme + Export theme + No comment provided by engineer. + Exported database archive. Exported database archive. @@ -2668,16 +3580,70 @@ This cannot be undone! Fast and no wait until the sender is online! No comment provided by engineer. + + Faster deletion of groups. + Faster deletion of groups. + No comment provided by engineer. + Faster joining and more reliable messages. Faster joining and more reliable messages. No comment provided by engineer. + + Faster sending messages. + Faster sending messages. + No comment provided by engineer. + Favorite Favorite + swipe action + + + Favorites + Favorites No comment provided by engineer. + + File error + File error + file error alert title + + + File errors: +%@ + File errors: +%@ + alert message + + + File is blocked by server operator: +%@. + File is blocked by server operator: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + File not found - most likely file was deleted or cancelled. + file error text + + + File server error: %@ + File server error: %@ + file error text + + + File status + File status + No comment provided by engineer. + + + File status: %@ + File status: %@ + copied message info + File will be deleted from servers. File will be deleted from servers. @@ -2698,6 +3664,11 @@ This cannot be undone! File: %@ No comment provided by engineer. + + Files + Files + No comment provided by engineer. + Files & media Files & media @@ -2708,9 +3679,14 @@ This cannot be undone! Files and media chat feature - - Files and media are prohibited in this group. - Files and media are prohibited in this group. + + Files and media are prohibited. + Files and media are prohibited. + No comment provided by engineer. + + + Files and media not allowed + Files and media not allowed No comment provided by engineer. @@ -2773,11 +3749,115 @@ This cannot be undone! Fix not supported by group member No comment provided by engineer. + + For all moderators + For all moderators + No comment provided by engineer. + + + For chat profile %@: + For chat profile %@: + servers error + For console For console No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + No comment provided by engineer. + + + For me + For me + No comment provided by engineer. + + + For private routing + For private routing + No comment provided by engineer. + + + For social media + For social media + No comment provided by engineer. + + + Forward + Forward + chat item action + + + Forward %d message(s)? + Forward %d message(s)? + alert title + + + Forward and save messages + Forward and save messages + No comment provided by engineer. + + + Forward messages + Forward messages + alert action + + + Forward messages without files? + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + Forward up to 20 messages at once. + No comment provided by engineer. + + + Forwarded + Forwarded + No comment provided by engineer. + + + Forwarded from + Forwarded from + No comment provided by engineer. + + + Forwarding %lld messages + Forwarding %lld messages + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + Forwarding server: %1$@ +Destination server error: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + Forwarding server: %1$@ +Error: %2$@ + snd error text + Found desktop Found desktop @@ -2798,11 +3878,6 @@ This cannot be undone! Full name (optional) No comment provided by engineer. - - Full name: - Full name: - No comment provided by engineer. - Fully decentralized – visible only to members. Fully decentralized – visible only to members. @@ -2823,6 +3898,21 @@ This cannot be undone! GIFs and stickers No comment provided by engineer. + + Get notified when mentioned. + Get notified when mentioned. + No comment provided by engineer. + + + Good afternoon! + Good afternoon! + message preview + + + Good morning! + Good morning! + message preview + Group Group @@ -2878,36 +3968,6 @@ This cannot be undone! Group links No comment provided by engineer. - - Group members can add message reactions. - Group members can add message reactions. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Group members can irreversibly delete sent messages. (24 hours) - No comment provided by engineer. - - - Group members can send direct messages. - Group members can send direct messages. - No comment provided by engineer. - - - Group members can send disappearing messages. - Group members can send disappearing messages. - No comment provided by engineer. - - - Group members can send files and media. - Group members can send files and media. - No comment provided by engineer. - - - Group members can send voice messages. - Group members can send voice messages. - No comment provided by engineer. - Group message: Group message: @@ -2948,11 +4008,21 @@ This cannot be undone! Group will be deleted for you - this cannot be undone! No comment provided by engineer. + + Groups + Groups + No comment provided by engineer. + Help Help No comment provided by engineer. + + Help admins moderating their groups. + Help admins moderating their groups. + No comment provided by engineer. + Hidden Hidden @@ -3003,10 +4073,20 @@ This cannot be undone! How SimpleX works No comment provided by engineer. + + How it affects privacy + How it affects privacy + No comment provided by engineer. + + + How it helps privacy + How it helps privacy + No comment provided by engineer. + How it works How it works - No comment provided by engineer. + alert button How to @@ -3033,6 +4113,11 @@ This cannot be undone! ICE servers (one per line) No comment provided by engineer. + + IP address + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. If you can't meet in person, show QR code in a video call, or share the link. @@ -3073,9 +4158,9 @@ This cannot be undone! Immediately No comment provided by engineer. - - Immune to spam and abuse - Immune to spam and abuse + + Immune to spam + Immune to spam No comment provided by engineer. @@ -3098,11 +4183,23 @@ This cannot be undone! Import failed No comment provided by engineer. + + Import theme + Import theme + No comment provided by engineer. + Importing archive Importing archive No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery Improved message delivery @@ -3128,6 +4225,21 @@ This cannot be undone! In reply to No comment provided by engineer. + + In-call sounds + In-call sounds + No comment provided by engineer. + + + Inappropriate content + Inappropriate content + report reason + + + Inappropriate profile + Inappropriate profile + report reason + Incognito Incognito @@ -3198,6 +4310,11 @@ This cannot be undone! Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Instant + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3205,16 +4322,41 @@ This cannot be undone! No comment provided by engineer. - - Instantly - Instantly - No comment provided by engineer. - Interface Interface No comment provided by engineer. + + Interface colors + Interface colors + No comment provided by engineer. + + + Invalid + Invalid + token status text + + + Invalid (bad token) + Invalid (bad token) + token status text + + + Invalid (expired) + Invalid (expired) + token status text + + + Invalid (unregistered) + Invalid (unregistered) + token status text + + + Invalid (wrong topic) + Invalid (wrong topic) + token status text + Invalid QR code Invalid QR code @@ -3253,7 +4395,7 @@ This cannot be undone! Invalid server address! Invalid server address! - No comment provided by engineer. + alert title Invalid status @@ -3275,6 +4417,11 @@ This cannot be undone! Invite members No comment provided by engineer. + + Invite to chat + Invite to chat + No comment provided by engineer. + Invite to group Invite to group @@ -3290,9 +4437,9 @@ This cannot be undone! Irreversible message deletion is prohibited in this chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. + Irreversible message deletion is prohibited. No comment provided by engineer. @@ -3316,6 +4463,11 @@ This cannot be undone! 3. The connection was compromised. No comment provided by engineer. + + It protects your IP address and connections. + It protects your IP address and connections. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). It seems like you are already connected via this link. If it is not the case, there was an error (%@). @@ -3334,7 +4486,7 @@ This cannot be undone! Join Join - No comment provided by engineer. + swipe action Join group @@ -3376,6 +4528,11 @@ This is your link for group %@! Keep Keep + alert action + + + Keep conversation + Keep conversation No comment provided by engineer. @@ -3386,7 +4543,7 @@ This is your link for group %@! Keep unused invitation? Keep unused invitation? - No comment provided by engineer. + alert title Keep your connections @@ -3421,6 +4578,16 @@ This is your link for group %@! Leave Leave + swipe action + + + Leave chat + Leave chat + No comment provided by engineer. + + + Leave chat? + Leave chat? No comment provided by engineer. @@ -3463,6 +4630,21 @@ This is your link for group %@! Linked desktops No comment provided by engineer. + + List + List + swipe action + + + List name and emoji should be different for all lists. + List name and emoji should be different for all lists. + No comment provided by engineer. + + + List name... + List name... + No comment provided by engineer. + Live message! Live message! @@ -3473,11 +4655,6 @@ This is your link for group %@! Live messages No comment provided by engineer. - - Local - Local - No comment provided by engineer. - Local name Local name @@ -3498,11 +4675,6 @@ This is your link for group %@! Lock mode No comment provided by engineer. - - Make a private connection - Make a private connection - No comment provided by engineer. - Make one message disappear Make one message disappear @@ -3513,21 +4685,11 @@ This is your link for group %@! Make profile private! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - No comment provided by engineer. - Mark deleted for everyone Mark deleted for everyone @@ -3553,11 +4715,36 @@ This is your link for group %@! Max 30 seconds, received instantly. No comment provided by engineer. + + Media & file servers + Media & file servers + No comment provided by engineer. + + + Medium + Medium + blur media + Member Member No comment provided by engineer. + + Member inactive + Member inactive + item status text + + + Member reports + Member reports + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + Member role will be changed to "%@". All chat members will be notified. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. Member role will be changed to "%@". All group members will be notified. @@ -3568,11 +4755,66 @@ This is your link for group %@! Member role will be changed to "%@". The member will receive a new invitation. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + Member will be removed from chat - this cannot be undone! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! Member will be removed from group - this cannot be undone! No comment provided by engineer. + + Members can add message reactions. + Members can add message reactions. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + Members can irreversibly delete sent messages. (24 hours) + No comment provided by engineer. + + + Members can report messsages to moderators. + Members can report messsages to moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + Members can send SimpleX links. + No comment provided by engineer. + + + Members can send direct messages. + Members can send direct messages. + No comment provided by engineer. + + + Members can send disappearing messages. + Members can send disappearing messages. + No comment provided by engineer. + + + Members can send files and media. + Members can send files and media. + No comment provided by engineer. + + + Members can send voice messages. + Members can send voice messages. + No comment provided by engineer. + + + Mention members 👋 + Mention members 👋 + No comment provided by engineer. + + + Menus + Menus + No comment provided by engineer. + Message delivery error Message delivery error @@ -3583,11 +4825,31 @@ This is your link for group %@! Message delivery receipts! No comment provided by engineer. + + Message delivery warning + Message delivery warning + item status text + Message draft Message draft No comment provided by engineer. + + Message forwarded + Message forwarded + item status text + + + Message may be delivered later if member becomes active. + Message may be delivered later if member becomes active. + item status description + + + Message queue info + Message queue info + No comment provided by engineer. + Message reactions Message reactions @@ -3598,11 +4860,41 @@ This is your link for group %@! Message reactions are prohibited in this chat. No comment provided by engineer. - - Message reactions are prohibited in this group. - Message reactions are prohibited in this group. + + Message reactions are prohibited. + Message reactions are prohibited. No comment provided by engineer. + + Message reception + Message reception + No comment provided by engineer. + + + Message servers + Message servers + No comment provided by engineer. + + + Message shape + Message shape + No comment provided by engineer. + + + Message source remains private. + Message source remains private. + No comment provided by engineer. + + + Message status + Message status + No comment provided by engineer. + + + Message status: %@ + Message status: %@ + copied message info + Message text Message text @@ -3628,6 +4920,26 @@ This is your link for group %@! Messages from %@ will be shown! No comment provided by engineer. + + Messages in this chat will never be deleted. + Messages in this chat will never be deleted. + alert message + + + Messages received + Messages received + No comment provided by engineer. + + + Messages sent + Messages sent + No comment provided by engineer. + + + Messages were deleted after you selected them. + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. @@ -3693,9 +5005,9 @@ This is your link for group %@! Migration is completed No comment provided by engineer. - - Migrations: %@ - Migrations: %@ + + Migrations: + Migrations: No comment provided by engineer. @@ -3713,21 +5025,31 @@ This is your link for group %@! Moderated at: %@ copied message info + + More + More + swipe action + More improvements are coming soon! More improvements are coming soon! No comment provided by engineer. + + More reliable network connection. + More reliable network connection. + No comment provided by engineer. + + + More reliable notifications + More reliable notifications + No comment provided by engineer. + Most likely this connection is deleted. Most likely this connection is deleted. item status description - - Most likely this contact has deleted the connection with you. - Most likely this contact has deleted the connection with you. - No comment provided by engineer. - Multiple chat profiles Multiple chat profiles @@ -3736,7 +5058,12 @@ This is your link for group %@! Mute Mute - No comment provided by engineer. + notification label action + + + Mute all + Mute all + notification label action Muted when inactive! @@ -3746,13 +5073,38 @@ This is your link for group %@! Name Name - No comment provided by engineer. + swipe action Network & servers Network & servers No comment provided by engineer. + + Network connection + Network connection + No comment provided by engineer. + + + Network decentralization + Network decentralization + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + Network issues - message expired after many attempts to send it. + snd error text + + + Network management + Network management + No comment provided by engineer. + + + Network operator + Network operator + No comment provided by engineer. + Network settings Network settings @@ -3763,16 +5115,36 @@ This is your link for group %@! Network status No comment provided by engineer. + + New + New + token status text + New Passcode New Passcode No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat New chat No comment provided by engineer. + + New chat experience 🎉 + New chat experience 🎉 + No comment provided by engineer. + New contact request New contact request @@ -3783,11 +5155,6 @@ This is your link for group %@! New contact: notification - - New database archive - New database archive - No comment provided by engineer. - New desktop app! New desktop app! @@ -3798,11 +5165,21 @@ This is your link for group %@! New display name No comment provided by engineer. + + New events + New events + notification + New in %@ New in %@ No comment provided by engineer. + + New media options + New media options + No comment provided by engineer. + New member role New member role @@ -3818,6 +5195,11 @@ This is your link for group %@! New passphrase… No comment provided by engineer. + + New server + New server + No comment provided by engineer. + No No @@ -3828,6 +5210,21 @@ This is your link for group %@! No app password Authentication unavailable + + No chats + No chats + No comment provided by engineer. + + + No chats found + No chats found + No comment provided by engineer. + + + No chats in list %@ + No chats in list %@ + No comment provided by engineer. + No contacts selected No contacts selected @@ -3848,6 +5245,11 @@ This is your link for group %@! No device token! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + No direct connection yet, message is forwarded by admin. + item status description + No filtered chats No filtered chats @@ -3863,21 +5265,111 @@ This is your link for group %@! No history No comment provided by engineer. + + No info, try to reload + No info, try to reload + No comment provided by engineer. + + + No media & file servers. + No media & file servers. + servers error + + + No message + No message + No comment provided by engineer. + + + No message servers. + No message servers. + servers error + + + No network connection + No network connection + No comment provided by engineer. + + + No permission to record speech + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No permission to record video + No comment provided by engineer. + No permission to record voice message No permission to record voice message No comment provided by engineer. + + No push server + No push server + No comment provided by engineer. + No received or sent files No received or sent files No comment provided by engineer. + + No servers for private message routing. + No servers for private message routing. + servers error + + + No servers to receive files. + No servers to receive files. + servers error + + + No servers to receive messages. + No servers to receive messages. + servers error + + + No servers to send files. + No servers to send files. + servers error + + + No token! + No token! + alert title + + + No unread chats + No unread chats + No comment provided by engineer. + + + No user identifiers. + No user identifiers. + No comment provided by engineer. + Not compatible! Not compatible! No comment provided by engineer. + + Notes + Notes + No comment provided by engineer. + + + Nothing selected + Nothing selected + No comment provided by engineer. + + + Nothing to forward! + Nothing to forward! + alert title + Notifications Notifications @@ -3888,6 +5380,21 @@ This is your link for group %@! Notifications are disabled! No comment provided by engineer. + + Notifications error + Notifications error + alert title + + + Notifications privacy + Notifications privacy + No comment provided by engineer. + + + Notifications status + Notifications status + alert title + Now admins can: - delete members' messages. @@ -3905,36 +5412,35 @@ This is your link for group %@! Off Off - No comment provided by engineer. + blur media Ok Ok - No comment provided by engineer. + alert button Old database Old database No comment provided by engineer. - - Old database archive - Old database archive - No comment provided by engineer. - One-time invitation link One-time invitation link No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Onion hosts will be required for connection. Requires enabling VPN. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Onion hosts will be **required** for connection. +Requires compatible VPN. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Onion hosts will be used when available. Requires enabling VPN. + + Onion hosts will be used when available. +Requires compatible VPN. + Onion hosts will be used when available. +Requires compatible VPN. No comment provided by engineer. @@ -3942,9 +5448,19 @@ This is your link for group %@! Onion hosts will not be used. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + Only chat owners can change preferences. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. + Only client devices store user profiles, contacts, groups, and messages. + No comment provided by engineer. + + + Only delete conversation + Only delete conversation No comment provided by engineer. @@ -3962,6 +5478,16 @@ This is your link for group %@! Only group owners can enable voice messages. No comment provided by engineer. + + Only sender and moderators see it + Only sender and moderators see it + No comment provided by engineer. + + + Only you and moderators see it + Only you and moderators see it + No comment provided by engineer. + Only you can add message reactions. Only you can add message reactions. @@ -4015,13 +5541,18 @@ This is your link for group %@! Open Open - No comment provided by engineer. + alert action Open Settings Open Settings No comment provided by engineer. + + Open changes + Open changes + No comment provided by engineer. + Open chat Open chat @@ -4032,31 +5563,46 @@ This is your link for group %@! Open chat console authentication reason + + Open conditions + Open conditions + No comment provided by engineer. + Open group Open group No comment provided by engineer. + + Open link? + Open link? + alert title + Open migration to another device Open migration to another device authentication reason - - Open user profiles - Open user profiles - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Open-source protocol and code – anybody can run the servers. - No comment provided by engineer. - Opening app… Opening app… No comment provided by engineer. + + Operator + Operator + No comment provided by engineer. + + + Operator server + Operator server + alert title + + + Or import archive file + Or import archive file + No comment provided by engineer. + Or paste archive link Or paste archive link @@ -4077,6 +5623,28 @@ This is your link for group %@! Or show this code No comment provided by engineer. + + Or to share privately + Or to share privately + No comment provided by engineer. + + + Organize chats into lists + Organize chats into lists + No comment provided by engineer. + + + Other + Other + No comment provided by engineer. + + + Other file errors: +%@ + Other file errors: +%@ + alert message + PING count PING count @@ -4112,6 +5680,11 @@ This is your link for group %@! Passcode set! No comment provided by engineer. + + Password + Password + No comment provided by engineer. + Password to show Password to show @@ -4142,14 +5715,14 @@ This is your link for group %@! Paste the link you received No comment provided by engineer. - - People can connect to you only via the links you share. - People can connect to you only via the links you share. + + Pending + Pending No comment provided by engineer. - - Periodically - Periodically + + Periodic + Periodic No comment provided by engineer. @@ -4162,11 +5735,28 @@ This is your link for group %@! Picture-in-picture calls No comment provided by engineer. + + Play from the chat list. + Play from the chat list. + No comment provided by engineer. + + + Please ask your contact to enable calls. + Please ask your contact to enable calls. + No comment provided by engineer. + Please ask your contact to enable sending voice messages. Please ask your contact to enable sending voice messages. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Please check that you used the correct link or ask your contact to send you another one. @@ -4234,61 +5824,121 @@ Error: %@ Please store passphrase securely, you will NOT be able to change it if you lose it. No comment provided by engineer. + + Please try to disable and re-enable notfications. + Please try to disable and re-enable notfications. + token info + + + Please wait for token activation to complete. + Please wait for token activation to complete. + token info + + + Please wait for token to be registered. + Please wait for token to be registered. + token info + Polish interface Polish interface No comment provided by engineer. + + Port + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Possibly, certificate fingerprint in server address is incorrect server test error - - Post-quantum E2EE - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Preserve the last message draft, with attachments. No comment provided by engineer. - - Preset server - Preset server - No comment provided by engineer. - Preset server address Preset server address No comment provided by engineer. + + Preset servers + Preset servers + No comment provided by engineer. + Preview Preview No comment provided by engineer. + + Previously connected servers + Previously connected servers + No comment provided by engineer. + Privacy & security Privacy & security No comment provided by engineer. + + Privacy for your customers. + Privacy for your customers. + No comment provided by engineer. + + + Privacy policy and conditions of use. + Privacy policy and conditions of use. + No comment provided by engineer. + Privacy redefined Privacy redefined No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + Private chats, groups and your contacts are not accessible to server operators. + No comment provided by engineer. + Private filenames Private filenames No comment provided by engineer. + + Private media file names. + Private media file names. + No comment provided by engineer. + + + Private message routing + Private message routing + No comment provided by engineer. + + + Private message routing 🚀 + Private message routing 🚀 + No comment provided by engineer. + Private notes Private notes name of notes to self + + Private routing + Private routing + No comment provided by engineer. + + + Private routing error + Private routing error + No comment provided by engineer. + Profile and server connections Profile and server connections @@ -4299,14 +5949,9 @@ Error: %@ Profile image No comment provided by engineer. - - Profile name - Profile name - No comment provided by engineer. - - - Profile name: - Profile name: + + Profile images + Profile images No comment provided by engineer. @@ -4314,10 +5959,15 @@ Error: %@ Profile password No comment provided by engineer. + + Profile theme + Profile theme + No comment provided by engineer. + Profile update will be sent to your contacts. Profile update will be sent to your contacts. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4339,6 +5989,16 @@ Error: %@ Prohibit messages reactions. No comment provided by engineer. + + Prohibit reporting messages to moderators. + Prohibit reporting messages to moderators. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + Prohibit sending SimpleX links. + No comment provided by engineer. + Prohibit sending direct messages to members. Prohibit sending direct messages to members. @@ -4359,11 +6019,23 @@ Error: %@ Prohibit sending voice messages. No comment provided by engineer. + + Protect IP address + Protect IP address + No comment provided by engineer. + Protect app screen Protect app screen No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + No comment provided by engineer. + Protect your chat profiles with a password! Protect your chat profiles with a password! @@ -4379,6 +6051,21 @@ Error: %@ Protocol timeout per KB No comment provided by engineer. + + Proxied + Proxied + No comment provided by engineer. + + + Proxied servers + Proxied servers + No comment provided by engineer. + + + Proxy requires password + Proxy requires password + No comment provided by engineer. + Push notifications Push notifications @@ -4399,6 +6086,11 @@ Error: %@ Rate the app No comment provided by engineer. + + Reachable chat toolbar + Reachable chat toolbar + No comment provided by engineer. + React… React… @@ -4407,33 +6099,28 @@ Error: %@ Read Read - No comment provided by engineer. + swipe action Read more Read more No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - - Read more in our GitHub repository. - Read more in our GitHub repository. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). @@ -4444,6 +6131,11 @@ Error: %@ Receipts are disabled No comment provided by engineer. + + Receive errors + Receive errors + No comment provided by engineer. + Received at Received at @@ -4464,6 +6156,21 @@ Error: %@ Received message message info title + + Received messages + Received messages + No comment provided by engineer. + + + Received reply + Received reply + No comment provided by engineer. + + + Received total + Received total + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. Receiving address will be changed to a different server. Address change will complete after sender comes online. @@ -4484,16 +6191,46 @@ Error: %@ Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + Recipient(s) can't see who this message is from. + No comment provided by engineer. + Recipients see updates as you type them. Recipients see updates as you type them. No comment provided by engineer. + + Reconnect + Reconnect + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Reconnect all connected servers to force message delivery. It uses additional traffic. No comment provided by engineer. + + Reconnect all servers + Reconnect all servers + No comment provided by engineer. + + + Reconnect all servers? + Reconnect all servers? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + Reconnect server to force message delivery. It uses additional traffic. + No comment provided by engineer. + + + Reconnect server? + Reconnect server? + No comment provided by engineer. + Reconnect servers? Reconnect servers? @@ -4514,10 +6251,26 @@ Error: %@ Reduced battery usage No comment provided by engineer. + + Register + Register + No comment provided by engineer. + + + Register notification token? + Register notification token? + token info + + + Registered + Registered + token status text + Reject Reject - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4544,6 +6297,16 @@ Error: %@ Remove No comment provided by engineer. + + Remove archive? + Remove archive? + No comment provided by engineer. + + + Remove image + Remove image + No comment provided by engineer. + Remove member Remove member @@ -4604,6 +6367,56 @@ Error: %@ Reply chat item action + + Report + Report + chat item action + + + Report content: only group moderators will see it. + Report content: only group moderators will see it. + report reason + + + Report member profile: only group moderators will see it. + Report member profile: only group moderators will see it. + report reason + + + Report other: only group moderators will see it. + Report other: only group moderators will see it. + report reason + + + Report reason? + Report reason? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + Report spam: only group moderators will see it. + report reason + + + Report violation: only group moderators will see it. + Report violation: only group moderators will see it. + report reason + + + Report: %@ + Report: %@ + report in notification + + + Reporting messages to moderators is prohibited. + Reporting messages to moderators is prohibited. + No comment provided by engineer. + + + Reports + Reports + No comment provided by engineer. + Required Required @@ -4614,16 +6427,41 @@ Error: %@ Reset No comment provided by engineer. + + Reset all hints + Reset all hints + No comment provided by engineer. + + + Reset all statistics + Reset all statistics + No comment provided by engineer. + + + Reset all statistics? + Reset all statistics? + No comment provided by engineer. + Reset colors Reset colors No comment provided by engineer. + + Reset to app theme + Reset to app theme + No comment provided by engineer. + Reset to defaults Reset to defaults No comment provided by engineer. + + Reset to user theme + Reset to user theme + No comment provided by engineer. + Restart the app to create a new chat profile Restart the app to create a new chat profile @@ -4664,9 +6502,9 @@ Error: %@ Reveal chat item action - - Revert - Revert + + Review conditions + Review conditions No comment provided by engineer. @@ -4694,9 +6532,19 @@ Error: %@ Run chat No comment provided by engineer. - - SMP servers - SMP servers + + SMP server + SMP server + No comment provided by engineer. + + + SOCKS proxy + SOCKS proxy + No comment provided by engineer. + + + Safely receive files + Safely receive files No comment provided by engineer. @@ -4707,43 +6555,44 @@ Error: %@ Save Save - chat item action + alert button +chat item action Save (and notify contacts) Save (and notify contacts) - No comment provided by engineer. + alert button Save and notify contact Save and notify contact - No comment provided by engineer. + alert button Save and notify group members Save and notify group members No comment provided by engineer. + + Save and reconnect + Save and reconnect + No comment provided by engineer. + Save and update group profile Save and update group profile No comment provided by engineer. - - Save archive - Save archive - No comment provided by engineer. - - - Save auto-accept settings - Save auto-accept settings - No comment provided by engineer. - Save group profile Save group profile No comment provided by engineer. + + Save list + Save list + No comment provided by engineer. + Save passphrase and open chat Save passphrase and open chat @@ -4757,7 +6606,7 @@ Error: %@ Save preferences? Save preferences? - No comment provided by engineer. + alert title Save profile password @@ -4772,28 +6621,53 @@ Error: %@ Save servers? Save servers? - No comment provided by engineer. - - - Save settings? - Save settings? - No comment provided by engineer. + alert title Save welcome message? Save welcome message? No comment provided by engineer. + + Save your profile? + Save your profile? + alert title + + + Saved + Saved + No comment provided by engineer. + Saved WebRTC ICE servers will be removed Saved WebRTC ICE servers will be removed No comment provided by engineer. + + Saved from + Saved from + No comment provided by engineer. + Saved message Saved message message info title + + Saving %lld messages + Saving %lld messages + No comment provided by engineer. + + + Scale + Scale + No comment provided by engineer. + + + Scan / Paste link + Scan / Paste link + No comment provided by engineer. + Scan QR code Scan QR code @@ -4834,11 +6708,21 @@ Error: %@ Search or paste SimpleX link No comment provided by engineer. + + Secondary + Secondary + No comment provided by engineer. + Secure queue Secure queue server test step + + Secured + Secured + No comment provided by engineer. + Security assessment Security assessment @@ -4852,6 +6736,21 @@ Error: %@ Select Select + chat item action + + + Select chat profile + Select chat profile + No comment provided by engineer. + + + Selected %lld + Selected %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Selected chat preferences prohibit this message. No comment provided by engineer. @@ -4889,11 +6788,6 @@ Error: %@ Send delivery receipts to No comment provided by engineer. - - Send direct message - Send direct message - No comment provided by engineer. - Send direct message to connect Send direct message to connect @@ -4904,6 +6798,11 @@ Error: %@ Send disappearing message No comment provided by engineer. + + Send errors + Send errors + No comment provided by engineer. + Send link previews Send link previews @@ -4914,14 +6813,29 @@ Error: %@ Send live message No comment provided by engineer. + + Send message to enable calls. + Send message to enable calls. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + Send messages directly when IP address is protected and your or destination server does not support private routing. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + Send messages directly when your or destination server does not support private routing. + No comment provided by engineer. + Send notifications Send notifications No comment provided by engineer. - - Send notifications: - Send notifications: + + Send private reports + Send private reports No comment provided by engineer. @@ -4947,7 +6861,7 @@ Error: %@ Sender cancelled file transfer. Sender cancelled file transfer. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5004,6 +6918,11 @@ Error: %@ Sent at: %@ copied message info + + Sent directly + Sent directly + No comment provided by engineer. + Sent file event Sent file event @@ -5014,11 +6933,71 @@ Error: %@ Sent message message info title + + Sent messages + Sent messages + No comment provided by engineer. + Sent messages will be deleted after set time. Sent messages will be deleted after set time. No comment provided by engineer. + + Sent reply + Sent reply + No comment provided by engineer. + + + Sent total + Sent total + No comment provided by engineer. + + + Sent via proxy + Sent via proxy + No comment provided by engineer. + + + Server + Server + No comment provided by engineer. + + + Server added to operator %@. + Server added to operator %@. + alert message + + + Server address + Server address + No comment provided by engineer. + + + Server address is incompatible with network settings. + Server address is incompatible with network settings. + srv error text. + + + Server address is incompatible with network settings: %@. + Server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Server operator changed. + Server operator changed. + alert title + + + Server operators + Server operators + No comment provided by engineer. + + + Server protocol changed. + Server protocol changed. + alert title + Server requires authorization to create queues, check password Server requires authorization to create queues, check password @@ -5034,11 +7013,36 @@ Error: %@ Server test failed! No comment provided by engineer. + + Server type + Server type + No comment provided by engineer. + + + Server version is incompatible with network settings. + Server version is incompatible with network settings. + srv error text + + + Server version is incompatible with your app: %@. + Server version is incompatible with your app: %@. + No comment provided by engineer. + Servers Servers No comment provided by engineer. + + Servers info + Servers info + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + Servers statistics will be reset - this cannot be undone! + No comment provided by engineer. + Session code Session code @@ -5049,11 +7053,21 @@ Error: %@ Set 1 day No comment provided by engineer. + + Set chat name… + Set chat name… + No comment provided by engineer. + Set contact name… Set contact name… No comment provided by engineer. + + Set default theme + Set default theme + No comment provided by engineer. + Set group preferences Set group preferences @@ -5064,6 +7078,11 @@ Error: %@ Set it instead of system authentication. No comment provided by engineer. + + Set message expiration in chats. + Set message expiration in chats. + No comment provided by engineer. + Set passcode Set passcode @@ -5094,24 +7113,55 @@ Error: %@ Settings No comment provided by engineer. + + Settings were changed. + Settings were changed. + alert message + + + Shape profile images + Shape profile images + No comment provided by engineer. + Share Share - chat item action + alert action +chat item action Share 1-time link Share 1-time link No comment provided by engineer. + + Share 1-time link with a friend + Share 1-time link with a friend + No comment provided by engineer. + + + Share SimpleX address on social media. + Share SimpleX address on social media. + No comment provided by engineer. + Share address Share address No comment provided by engineer. + + Share address publicly + Share address publicly + No comment provided by engineer. + Share address with contacts? Share address with contacts? + alert title + + + Share from other apps. + Share from other apps. No comment provided by engineer. @@ -5119,16 +7169,31 @@ Error: %@ Share link No comment provided by engineer. + + Share profile + Share profile + No comment provided by engineer. + Share this 1-time invite link Share this 1-time invite link No comment provided by engineer. + + Share to SimpleX + Share to SimpleX + No comment provided by engineer. + Share with contacts Share with contacts No comment provided by engineer. + + Short link + Short link + No comment provided by engineer. + Show QR code Show QR code @@ -5149,21 +7214,46 @@ Error: %@ Show last messages No comment provided by engineer. + + Show message status + Show message status + No comment provided by engineer. + + + Show percentage + Show percentage + No comment provided by engineer. + Show preview Show preview No comment provided by engineer. + + Show → on messages sent via private routing. + Show → on messages sent via private routing. + No comment provided by engineer. + Show: Show: No comment provided by engineer. + + SimpleX + SimpleX + No comment provided by engineer. + SimpleX Address SimpleX Address No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. SimpleX Chat security was audited by Trail of Bits. @@ -5194,6 +7284,21 @@ Error: %@ SimpleX address No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + SimpleX address and 1-time links are safe to share via any messenger. + No comment provided by engineer. + + + SimpleX address or 1-time link? + SimpleX address or 1-time link? + No comment provided by engineer. + + + SimpleX channel link + SimpleX channel link + simplex link type + SimpleX contact address SimpleX contact address @@ -5212,6 +7317,16 @@ Error: %@ SimpleX links SimpleX links + chat feature + + + SimpleX links are prohibited. + SimpleX links are prohibited. + No comment provided by engineer. + + + SimpleX links not allowed + SimpleX links not allowed No comment provided by engineer. @@ -5219,11 +7334,21 @@ Error: %@ SimpleX one-time invitation simplex link type + + SimpleX protocols reviewed by Trail of Bits. + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Simplified incognito mode No comment provided by engineer. + + Size + Size + No comment provided by engineer. + Skip Skip @@ -5239,16 +7364,54 @@ Error: %@ Small groups (max 20) No comment provided by engineer. + + Soft + Soft + blur media + + + Some app settings were not migrated. + Some app settings were not migrated. + No comment provided by engineer. + + + Some file(s) were not exported: + Some file(s) were not exported: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. Some non-fatal errors occurred during import - you may see Chat console for more details. No comment provided by engineer. + + Some non-fatal errors occurred during import: + Some non-fatal errors occurred during import: + No comment provided by engineer. + + + Some servers failed the test: +%@ + Some servers failed the test: +%@ + alert message + Somebody Somebody notification title + + Spam + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + Square, circle, or anything in between. + No comment provided by engineer. + Start chat Start chat @@ -5264,6 +7427,16 @@ Error: %@ Start migration No comment provided by engineer. + + Starting from %@. + Starting from %@. + No comment provided by engineer. + + + Statistics + Statistics + No comment provided by engineer. + Stop Stop @@ -5279,11 +7452,6 @@ Error: %@ Stop chat No comment provided by engineer. - - Stop chat to enable database actions - Stop chat to enable database actions - No comment provided by engineer. - Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. @@ -5312,28 +7480,63 @@ Error: %@ Stop sharing Stop sharing - No comment provided by engineer. + alert action Stop sharing address? Stop sharing address? - No comment provided by engineer. + alert title Stopping chat Stopping chat No comment provided by engineer. + + Storage + Storage + No comment provided by engineer. + + + Strong + Strong + blur media + Submit Submit No comment provided by engineer. + + Subscribed + Subscribed + No comment provided by engineer. + + + Subscription errors + Subscription errors + No comment provided by engineer. + + + Subscriptions ignored + Subscriptions ignored + No comment provided by engineer. + Support SimpleX Chat Support SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System System @@ -5344,11 +7547,21 @@ Error: %@ System authentication No comment provided by engineer. + + TCP connection + TCP connection + No comment provided by engineer. + TCP connection timeout TCP connection timeout No comment provided by engineer. + + TCP port for messaging + TCP port for messaging + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5364,11 +7577,21 @@ Error: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + Tail + No comment provided by engineer. + Take picture Take picture No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + Tap Create SimpleX address in the menu to create it later. + No comment provided by engineer. + Tap button Tap button @@ -5404,16 +7627,21 @@ Error: %@ Tap to scan No comment provided by engineer. - - Tap to start a new chat - Tap to start a new chat - No comment provided by engineer. + + Temporary file error + Temporary file error + file error alert title Test failed at step %@. Test failed at step %@. server test failure + + Test notifications + Test notifications + No comment provided by engineer. + Test server Test server @@ -5427,7 +7655,7 @@ Error: %@ Tests failed! Tests failed! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5444,11 +7672,6 @@ Error: %@ Thanks to the users – contribute via Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - The 1st platform without any user identifiers – private by design. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5461,6 +7684,16 @@ It can happen because of some bug or when the connection is compromised.The app can notify you when you receive messages or contact requests - please open settings to enable. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + The app protects your privacy by using different operators in each conversation. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + The app will ask to confirm downloads from unknown file servers (except .onion). + No comment provided by engineer. + The attempt to change database passphrase was not completed. The attempt to change database passphrase was not completed. @@ -5471,6 +7704,11 @@ It can happen because of some bug or when the connection is compromised.The code you scanned is not a SimpleX link QR code. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + The connection reached the limit of undelivered messages, your contact may be offline. + No comment provided by engineer. + The connection you accepted will be cancelled! The connection you accepted will be cancelled! @@ -5491,6 +7729,11 @@ It can happen because of some bug or when the connection is compromised.The encryption is working and the new encryption agreement is not required. It may result in connection errors! No comment provided by engineer. + + The future of messaging + The future of messaging + No comment provided by engineer. + The hash of the previous message is different. The hash of the previous message is different. @@ -5506,9 +7749,14 @@ It can happen because of some bug or when the connection is compromised.The message will be marked as moderated for all members. No comment provided by engineer. - - The next generation of private messaging - The next generation of private messaging + + The messages will be deleted for all members. + The messages will be deleted for all members. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. + The messages will be marked as moderated for all members. No comment provided by engineer. @@ -5516,9 +7764,14 @@ It can happen because of some bug or when the connection is compromised.The old database was not removed during the migration, it can be deleted. No comment provided by engineer. - - The profile is only shared with your contacts. - The profile is only shared with your contacts. + + The same conditions will apply to operator **%@**. + The same conditions will apply to operator **%@**. + No comment provided by engineer. + + + The second preset operator in the app! + The second preset operator in the app! No comment provided by engineer. @@ -5536,14 +7789,29 @@ It can happen because of some bug or when the connection is compromised.The servers for new connections of your current chat profile **%@**. No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + The servers for new files of your current chat profile **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. The text you pasted is not a SimpleX link. No comment provided by engineer. - - Theme - Theme + + The uploaded database archive will be permanently removed from the servers. + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + + + Themes + Themes + No comment provided by engineer. + + + These conditions will also apply for: **%@**. + These conditions will also apply for: **%@**. No comment provided by engineer. @@ -5566,6 +7834,11 @@ It can happen because of some bug or when the connection is compromised.This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. @@ -5611,11 +7884,31 @@ It can happen because of some bug or when the connection is compromised.This is your own one-time link! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + This link was used with another mobile device, please create a new link on the desktop. + No comment provided by engineer. + + + This message was deleted or not received yet. + This message was deleted or not received yet. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. This setting applies to messages in your current chat profile **%@**. No comment provided by engineer. + + Title + Title + No comment provided by engineer. + To ask any questions and to receive updates: To ask any questions and to receive updates: @@ -5636,9 +7929,9 @@ It can happen because of some bug or when the connection is compromised.To make a new connection No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + + To protect against your link being replaced, you can compare contact security codes. + To protect against your link being replaced, you can compare contact security codes. No comment provided by engineer. @@ -5646,6 +7939,11 @@ It can happen because of some bug or when the connection is compromised.To protect timezone, image/voice files use UTC. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + To protect your IP address, private routing uses your SMP servers to deliver messages. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5653,6 +7951,26 @@ You will be prompted to complete authentication before this feature is enabled.< You will be prompted to complete authentication before this feature is enabled. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + No comment provided by engineer. + + + To receive + To receive + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. To record voice message please grant permission to use Microphone. @@ -5663,26 +7981,61 @@ You will be prompted to complete authentication before this feature is enabled.< To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. No comment provided by engineer. + + To send + To send + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. To support instant push notifications the chat database has to be migrated. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + To use the servers of **%@**, accept conditions of use. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. To verify end-to-end encryption with your contact compare (or scan) the code on your devices. No comment provided by engineer. + + Toggle chat list: + Toggle chat list: + No comment provided by engineer. + Toggle incognito when connecting. Toggle incognito when connecting. No comment provided by engineer. + + Token status: %@. + Token status: %@. + token status + + + Toolbar opacity + Toolbar opacity + No comment provided by engineer. + + + Total + Total + No comment provided by engineer. + Transport isolation Transport isolation No comment provided by engineer. + + Transport sessions + Transport sessions + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Trying to connect to the server used to receive messages from this contact (error: %@). @@ -5738,10 +8091,10 @@ You will be prompted to complete authentication before this feature is enabled.< Unblock member? No comment provided by engineer. - - Unexpected error: %@ - Unexpected error: %@ - item status description + + Undelivered messages + Undelivered messages + No comment provided by engineer. Unexpected migration state @@ -5751,7 +8104,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unfav. Unfav. - No comment provided by engineer. + swipe action Unhide @@ -5788,6 +8141,11 @@ You will be prompted to complete authentication before this feature is enabled.< Unknown error No comment provided by engineer. + + Unknown servers! + Unknown servers! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -5823,11 +8181,16 @@ To connect, please ask your contact to create another connection link and check Unmute Unmute - No comment provided by engineer. + notification label action Unread Unread + swipe action + + + Unsupported connection link + Unsupported connection link No comment provided by engineer. @@ -5840,11 +8203,6 @@ To connect, please ask your contact to create another connection link and check Update No comment provided by engineer. - - Update .onion hosts setting? - Update .onion hosts setting? - No comment provided by engineer. - Update database passphrase Update database passphrase @@ -5855,9 +8213,14 @@ To connect, please ask your contact to create another connection link and check Update network settings? No comment provided by engineer. - - Update transport isolation mode? - Update transport isolation mode? + + Update settings? + Update settings? + No comment provided by engineer. + + + Updated conditions + Updated conditions No comment provided by engineer. @@ -5865,16 +8228,16 @@ To connect, please ask your contact to create another connection link and check Updating settings will re-connect the client to all servers. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - Updating this setting will re-connect the client to all servers. - No comment provided by engineer. - Upgrade and open chat Upgrade and open chat No comment provided by engineer. + + Upload errors + Upload errors + No comment provided by engineer. + Upload failed Upload failed @@ -5885,21 +8248,51 @@ To connect, please ask your contact to create another connection link and check Upload file server test step + + Uploaded + Uploaded + No comment provided by engineer. + + + Uploaded files + Uploaded files + No comment provided by engineer. + Uploading archive Uploading archive No comment provided by engineer. + + Use %@ + Use %@ + No comment provided by engineer. + Use .onion hosts Use .onion hosts No comment provided by engineer. + + Use SOCKS proxy + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? Use SimpleX Chat servers? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + Use TCP port %@ when no port is specified. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat Use chat @@ -5910,6 +8303,16 @@ To connect, please ask your contact to create another connection link and check Use current profile No comment provided by engineer. + + Use for files + Use for files + No comment provided by engineer. + + + Use for messages + Use for messages + No comment provided by engineer. + Use for new connections Use for new connections @@ -5935,24 +8338,54 @@ To connect, please ask your contact to create another connection link and check Use only local notifications? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + Use private routing with unknown servers when IP address is not protected. + No comment provided by engineer. + + + Use private routing with unknown servers. + Use private routing with unknown servers. + No comment provided by engineer. + Use server Use server No comment provided by engineer. + + Use servers + Use servers + No comment provided by engineer. + + + Use short links (BETA) + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. Use the app while in the call. No comment provided by engineer. - - User profile - User profile + + Use the app with one hand. + Use the app with one hand. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - Using .onion hosts requires compatible VPN provider. + + Use web port + Use web port + No comment provided by engineer. + + + User selection + User selection + No comment provided by engineer. + + + Username + Username No comment provided by engineer. @@ -6025,11 +8458,21 @@ To connect, please ask your contact to create another connection link and check Videos and files up to 1gb No comment provided by engineer. + + View conditions + View conditions + No comment provided by engineer. + View security code View security code No comment provided by engineer. + + View updated conditions + View updated conditions + No comment provided by engineer. + Visible history Visible history @@ -6045,9 +8488,14 @@ To connect, please ask your contact to create another connection link and check Voice messages are prohibited in this chat. No comment provided by engineer. - - Voice messages are prohibited in this group. - Voice messages are prohibited in this group. + + Voice messages are prohibited. + Voice messages are prohibited. + No comment provided by engineer. + + + Voice messages not allowed + Voice messages not allowed No comment provided by engineer. @@ -6080,6 +8528,16 @@ To connect, please ask your contact to create another connection link and check Waiting for video No comment provided by engineer. + + Wallpaper accent + Wallpaper accent + No comment provided by engineer. + + + Wallpaper background + Wallpaper background + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures Warning: starting chat on multiple devices is not supported and will cause message delivery failures @@ -6120,9 +8578,14 @@ To connect, please ask your contact to create another connection link and check When available No comment provided by engineer. - - When people request to connect, you can accept or reject it. - When people request to connect, you can accept or reject it. + + When connecting audio and video calls. + When connecting audio and video calls. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. No comment provided by engineer. @@ -6130,6 +8593,21 @@ To connect, please ask your contact to create another connection link and check When you share an incognito profile with somebody, this profile will be used for the groups they invite you to. No comment provided by engineer. + + WiFi + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + Will be enabled in direct chats! + No comment provided by engineer. + + + Wired ethernet + Wired ethernet + No comment provided by engineer. + With encrypted files and media. With encrypted files and media. @@ -6145,24 +8623,39 @@ To connect, please ask your contact to create another connection link and check With reduced battery usage. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + Without Tor or VPN, your IP address will be visible to file servers. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + alert message + Wrong database passphrase Wrong database passphrase No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + Wrong key or unknown connection - most likely this connection is deleted. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + Wrong key or unknown file chunk address - most likely file is deleted. + file error text + Wrong passphrase! Wrong passphrase! No comment provided by engineer. - - XFTP servers - XFTP servers - No comment provided by engineer. - - - You - You + + XFTP server + XFTP server No comment provided by engineer. @@ -6190,6 +8683,11 @@ To connect, please ask your contact to create another connection link and check You are already connected to %@. No comment provided by engineer. + + You are already connected with %@. + You are already connected with %@. + No comment provided by engineer. + You are already connecting to %@. You are already connecting to %@. @@ -6237,11 +8735,26 @@ Repeat join request? You are invited to group No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + You are not connected to these servers. Private routing is used to deliver messages to them. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. You can accept calls from lock screen, without device and app authentication. No comment provided by engineer. + + You can change it in Appearance settings. + You can change it in Appearance settings. + No comment provided by engineer. + + + You can configure servers via settings. + You can configure servers via settings. + No comment provided by engineer. + You can create it later You can create it later @@ -6272,11 +8785,21 @@ Repeat join request? You can make it visible to your SimpleX contacts via Settings. No comment provided by engineer. - - You can now send messages to %@ - You can now send messages to %@ + + You can now chat with %@ + You can now chat with %@ notification body + + You can send messages to %@ from Archived contacts. + You can send messages to %@ from Archived contacts. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + You can set connection name, to remember who the link was shared with. + No comment provided by engineer. + You can set lock screen notification preview via settings. You can set lock screen notification preview via settings. @@ -6292,16 +8815,16 @@ Repeat join request? You can share this address with your contacts to let them connect with **%@**. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - You can share your address as a link or QR code - anybody can connect to you. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app You can start chat via app Settings / Database or by restarting the app No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + You can still view conversation with %@ in the list of chats. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. You can turn on SimpleX Lock via Settings. @@ -6315,23 +8838,23 @@ Repeat join request? You can view invitation link again in connection details. You can view invitation link again in connection details. - No comment provided by engineer. + alert message You can't send messages! You can't send messages! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - You could not be verified; please try again. You could not be verified; please try again. No comment provided by engineer. + + You decide who can connect. + You decide who can connect. + No comment provided by engineer. + You have already requested connection via this address! You have already requested connection via this address! @@ -6344,11 +8867,6 @@ Repeat connection request? Repeat connection request? No comment provided by engineer. - - You have no chats - You have no chats - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. You have to enter passphrase every time the app starts - it is not stored on the device. @@ -6369,11 +8887,26 @@ Repeat connection request? You joined this group. Connecting to inviting group member. No comment provided by engineer. + + You may migrate the exported database. + You may migrate the exported database. + No comment provided by engineer. + + + You may save the exported archive. + You may save the exported archive. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + You need to allow your contact to call to be able to call them. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. You need to allow your contact to send voice messages to be able to send them. @@ -6389,6 +8922,11 @@ Repeat connection request? You sent group invitation No comment provided by engineer. + + You should receive notifications. + You should receive notifications. + token info + You will be connected to group when the group host's device is online, please wait or check later! You will be connected to group when the group host's device is online, please wait or check later! @@ -6424,6 +8962,11 @@ Repeat connection request? You will still receive calls and notifications from muted profiles when they are active. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + You will stop receiving messages from this chat. Chat history will be preserved. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. You will stop receiving messages from this group. Chat history will be preserved. @@ -6444,31 +8987,16 @@ Repeat connection request? You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed No comment provided by engineer. - - Your %@ servers - Your %@ servers - No comment provided by engineer. - Your ICE servers Your ICE servers No comment provided by engineer. - - Your SMP servers - Your SMP servers - No comment provided by engineer. - Your SimpleX address Your SimpleX address No comment provided by engineer. - - Your XFTP servers - Your XFTP servers - No comment provided by engineer. - Your calls Your calls @@ -6484,16 +9012,19 @@ Repeat connection request? Your chat database is not encrypted - set passphrase to encrypt it. No comment provided by engineer. + + Your chat preferences + Your chat preferences + alert title + Your chat profiles Your chat profiles No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. No comment provided by engineer. @@ -6511,6 +9042,11 @@ You can cancel this connection and remove the contact (and try later with a new Your contacts will remain connected. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Your current chat database will be DELETED and REPLACED with the imported one. @@ -6541,33 +9077,36 @@ You can cancel this connection and remove the contact (and try later with a new Your profile **%@** will be shared. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. + + Your profile is stored on your device and only shared with your contacts. + Your profile is stored on your device and only shared with your contacts. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Your profile, contacts and delivered messages are stored on your device. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your random profile Your random profile No comment provided by engineer. - - Your server - Your server - No comment provided by engineer. - Your server address Your server address No comment provided by engineer. + + Your servers + Your servers + No comment provided by engineer. + Your settings Your settings @@ -6608,11 +9147,21 @@ SimpleX servers cannot see your profile. accepted call call status + + accepted invitation + accepted invitation + chat list item title + admin admin member role + + admins + admins + feature role + agreeing encryption for %@… agreeing encryption for %@… @@ -6623,6 +9172,11 @@ SimpleX servers cannot see your profile. agreeing encryption… chat item text + + all members + all members + feature role + always always @@ -6633,6 +9187,16 @@ SimpleX servers cannot see your profile. and %lld other events No comment provided by engineer. + + archived report + archived report + No comment provided by engineer. + + + attempts + attempts + No comment provided by engineer. + audio call (not e2e encrypted) audio call (not e2e encrypted) @@ -6666,13 +9230,19 @@ SimpleX servers cannot see your profile. blocked by admin blocked by admin - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold bold No comment provided by engineer. + + call + call + No comment provided by engineer. + call error call error @@ -6776,7 +9346,7 @@ SimpleX servers cannot see your profile. connecting… connecting… - chat list item title + No comment provided by engineer. connection established @@ -6823,10 +9393,16 @@ SimpleX servers cannot see your profile. days time unit + + decryption errors + decryption errors + No comment provided by engineer. + default (%@) default (%@) - pref value + delete after time +pref value default (no) @@ -6873,6 +9449,11 @@ SimpleX servers cannot see your profile. duplicate message integrity error chat item + + duplicates + duplicates + No comment provided by engineer. + e2e encrypted e2e encrypted @@ -6948,9 +9529,14 @@ SimpleX servers cannot see your profile. error No comment provided by engineer. - - event happened - event happened + + expired + expired + No comment provided by engineer. + + + forwarded + forwarded No comment provided by engineer. @@ -6978,6 +9564,11 @@ SimpleX servers cannot see your profile. iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications. No comment provided by engineer. + + inactive + inactive + No comment provided by engineer. + incognito via contact address link incognito via contact address link @@ -7018,6 +9609,11 @@ SimpleX servers cannot see your profile. invitation to group %@ group name + + invite + invite + No comment provided by engineer. + invited invited @@ -7073,6 +9669,11 @@ SimpleX servers cannot see your profile. connected rcv group event chat item + + message + message + No comment provided by engineer. + message received message received @@ -7098,6 +9699,11 @@ SimpleX servers cannot see your profile. moderated by %@ marked deleted chat item preview text + + moderator + moderator + member role + months months @@ -7106,7 +9712,7 @@ SimpleX servers cannot see your profile. never never - No comment provided by engineer. + delete after time new message @@ -7137,8 +9743,8 @@ SimpleX servers cannot see your profile. off off enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7155,16 +9761,41 @@ SimpleX servers cannot see your profile. on group pref value + + other + other + No comment provided by engineer. + + + other errors + other errors + No comment provided by engineer. + owner owner member role + + owners + owners + feature role + peer-to-peer peer-to-peer No comment provided by engineer. + + pending + pending + No comment provided by engineer. + + + pending approval + pending approval + No comment provided by engineer. + quantum resistant e2e encryption quantum resistant e2e encryption @@ -7180,6 +9811,11 @@ SimpleX servers cannot see your profile. received confirmation… No comment provided by engineer. + + rejected + rejected + No comment provided by engineer. + rejected call rejected call @@ -7210,6 +9846,26 @@ SimpleX servers cannot see your profile. removed you rcv group event chat item + + requested to connect + requested to connect + chat list item title + + + saved + saved + No comment provided by engineer. + + + saved from %@ + saved from %@ + No comment provided by engineer. + + + search + search + No comment provided by engineer. + sec sec @@ -7235,6 +9891,15 @@ SimpleX servers cannot see your profile. send direct message No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + server queue info: %1$@ + +last received msg: %2$@ + queue info + set new contact address set new contact address @@ -7275,11 +9940,21 @@ SimpleX servers cannot see your profile. unknown connection info + + unknown servers + unknown servers + No comment provided by engineer. + unknown status unknown status No comment provided by engineer. + + unprotected + unprotected + No comment provided by engineer. + updated group profile updated group profile @@ -7320,6 +9995,11 @@ SimpleX servers cannot see your profile. via relay No comment provided by engineer. + + video + video + No comment provided by engineer. + video call (not e2e encrypted) video call (not e2e encrypted) @@ -7345,11 +10025,21 @@ SimpleX servers cannot see your profile. weeks time unit + + when IP hidden + when IP hidden + No comment provided by engineer. + yes yes pref value + + you + you + No comment provided by engineer. + you are invited to group you are invited to group @@ -7424,7 +10114,7 @@ SimpleX servers cannot see your profile.
- +
@@ -7461,7 +10151,7 @@ SimpleX servers cannot see your profile.
- +
@@ -7481,4 +10171,250 @@ SimpleX servers cannot see your profile.
+ +
+ +
+ + + %d new events + %d new events + notification body + + + From %d chat(s) + From %d chat(s) + notification body + + + From: %@ + From: %@ + notification body + + + New events + New events + notification + + + New messages + New messages + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + App is locked! + No comment provided by engineer. + + + Cancel + Cancel + No comment provided by engineer. + + + Cannot access keychain to save database password + Cannot access keychain to save database password + No comment provided by engineer. + + + Cannot forward message + Cannot forward message + No comment provided by engineer. + + + Comment + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + Database downgrade required + No comment provided by engineer. + + + Database encrypted! + Database encrypted! + No comment provided by engineer. + + + Database error + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database passphrase is required to open chat. + Database passphrase is required to open chat. + No comment provided by engineer. + + + Database upgrade required + Database upgrade required + No comment provided by engineer. + + + Error preparing file + Error preparing file + No comment provided by engineer. + + + Error preparing message + Error preparing message + No comment provided by engineer. + + + Error: %@ + Error: %@ + No comment provided by engineer. + + + File error + File error + No comment provided by engineer. + + + Incompatible database version + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + Invalid migration confirmation + No comment provided by engineer. + + + Keychain error + Keychain error + No comment provided by engineer. + + + Large file! + Large file! + No comment provided by engineer. + + + No active profile + No active profile + No comment provided by engineer. + + + Ok + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + Open the app to upgrade the database. + No comment provided by engineer. + + + Passphrase + Passphrase + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Selected chat preferences prohibit this message. + No comment provided by engineer. + + + Sending a message takes longer than expected. + Sending a message takes longer than expected. + No comment provided by engineer. + + + Sending message… + Sending message… + No comment provided by engineer. + + + Share + Share + No comment provided by engineer. + + + Slow network? + Slow network? + No comment provided by engineer. + + + Unknown database error: %@ + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + Unsupported format + No comment provided by engineer. + + + Wait + Wait + No comment provided by engineer. + + + Wrong database passphrase + Wrong database passphrase + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + You can allow sharing in Privacy & Security / SimpleX Lock settings. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/en.xcloc/contents.json b/apps/ios/SimpleX Localizations/en.xcloc/contents.json index 7d429820ee..ec2accf27e 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/en.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "en", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff index 9cc9583e2e..d39fb61249 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff +++ b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (puede copiarse) @@ -109,6 +82,7 @@ %@ downloaded + %@ descargado No comment provided by engineer. @@ -126,13 +100,19 @@ %@ está verificado No comment provided by engineer. + + %@ server + %@ servidor + No comment provided by engineer. + %@ servers - Servidores %@ + %@ servidores No comment provided by engineer. %@ uploaded + %@ subido No comment provided by engineer. @@ -140,6 +120,11 @@ ¡ %@ quiere contactar! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ y %lld miembro(s) más @@ -157,37 +142,67 @@ %d days - %d días + %d día(s) time interval + + %d file(s) are still being downloaded. + %d archivo(s) se está(n) descargando todavía. + forward confirmation reason + + + %d file(s) failed to download. + La descarga ha fallado para %d archivo(s). + forward confirmation reason + + + %d file(s) were deleted. + %d archivo(s) ha(n) sido eliminado(s). + forward confirmation reason + + + %d file(s) were not downloaded. + %d archivo(s) no se ha(n) descargado. + forward confirmation reason + %d hours - %d horas + %d hora(s) time interval + + %d messages not forwarded + %d mensaje(s) no enviado(s) + alert title + %d min - %d minutos + %d minuto(s) time interval %d months - %d meses + %d mes(es) time interval %d sec - %d segundos + %d segundo(s) time interval + + %d seconds(s) + %d segundos + delete after time + %d skipped message(s) - %d mensaje(s) saltado(s + %d mensaje(s) omitido(s) integrity error chat item %d weeks - %d semanas + %d semana(s) time interval @@ -250,11 +265,6 @@ %lld idiomas de interfaz nuevos No comment provided by engineer. - - %lld second(s) - %lld segundo(s) - No comment provided by engineer. - %lld seconds %lld segundos @@ -305,11 +315,6 @@ %u mensaje(s) omitido(s). No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) (nuevo) @@ -320,19 +325,9 @@ (este dispositivo v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - **Añadir contacto**: crea un enlace de invitación nuevo o usa un enlace recibido. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Añadir nuevo contacto**: para crear tu código QR o enlace de un uso para tu contacto. + + **Create 1-time link**: to create and share a new invitation link. + **Añadir contacto**: crea un enlace de invitación nuevo. No comment provided by engineer. @@ -340,30 +335,36 @@ **Crear grupo**: crea un grupo nuevo. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **Más privado**: comprueba los mensajes nuevos cada 20 minutos. El token del dispositivo se comparte con el servidor de SimpleX Chat, pero no cuántos contactos o mensajes tienes. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **Más privado**: no se usa el servidor de notificaciones de SimpleX Chat, los mensajes se comprueban periódicamente en segundo plano (dependiendo de la frecuencia con la que utilices la aplicación). No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Recuarda**: usar la misma base de datos en dos dispositivos hará que falle el descifrado de mensajes como protección de seguridad. No comment provided by engineer. **Please note**: you will NOT be able to recover or change passphrase if you lose it. - **Atención**: NO podrás recuperar o cambiar la contraseña si la pierdes. + **Atención**: Si la pierdes NO podrás recuperar o cambiar la contraseña. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Recomendado**: el token del dispositivo y las notificaciones se envían al servidor de notificaciones de SimpleX Chat, pero no el contenido del mensaje, su tamaño o su procedencia. No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + **Escanear / Pegar enlace**: para conectar mediante un enlace recibido. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **Advertencia**: Las notificaciones automáticas instantáneas requieren una contraseña guardada en Keychain. @@ -371,6 +372,7 @@ **Warning**: the archive will be removed. + **Atención**: el archivo será eliminado. No comment provided by engineer. @@ -388,11 +390,6 @@ \*bold* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -429,11 +426,6 @@ - historial de edición. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 seg @@ -447,7 +439,8 @@ 1 day un dia - time interval + delete after time +time interval 1 hour @@ -462,12 +455,29 @@ 1 month un mes - time interval + delete after time +time interval 1 week una semana - time interval + delete after time +time interval + + + 1 year + 1 año + delete after time + + + 1-time link + Enlace de un uso + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + Los enlaces de un uso pueden ser usados *solamente con un contacto* - compártelos en persona o mediante cualquier aplicación de mensajería. + No comment provided by engineer. 5 minutes @@ -484,11 +494,6 @@ 30 segundos No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -508,7 +513,7 @@ A new random profile will be shared. - Se compartirá un perfil nuevo aleatorio. + Compartirás un perfil nuevo aleatorio. No comment provided by engineer. @@ -520,7 +525,7 @@ A separate TCP connection will be used **for each contact and group member**. **Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail. Se usará una conexión TCP independiente **por cada contacto y miembro de grupo**. -**Atención**: si tienes muchas conexiones, tu consumo de batería y tráfico pueden ser sustancialmente mayores y algunas conexiones pueden fallar. +**Atención**: si tienes muchas conexiones, tu consumo de batería y tráfico pueden aumentar bastante y algunas conexiones pueden fallar. No comment provided by engineer. @@ -538,23 +543,18 @@ ¿Cancelar el cambio de servidor? No comment provided by engineer. - - About SimpleX - Acerca de SimpleX - No comment provided by engineer. - About SimpleX Chat Sobre SimpleX Chat No comment provided by engineer. - - About SimpleX address - Acerca de la dirección SimpleX + + About operators + Acerca de los operadores No comment provided by engineer. - - Accent color + + Accent Color No comment provided by engineer. @@ -562,7 +562,13 @@ Accept Aceptar accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Aceptar condiciones + No comment provided by engineer. Accept connection request? @@ -577,21 +583,47 @@ Accept incognito Aceptar incógnito - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + Condiciones aceptadas + No comment provided by engineer. + + + Acknowledged + Confirmaciones + No comment provided by engineer. + + + Acknowledgement errors + Errores de confirmación + No comment provided by engineer. + + + Active + Activo + token status text + + + Active connections + Conexiones activas + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Añade la dirección a tu perfil para que tus contactos puedan compartirla con otros. La actualización del perfil se enviará a tus contactos. No comment provided by engineer. - - Add contact - Añadir contacto + + Add friends + Añadir amigos No comment provided by engineer. - - Add preset servers - Añadir servidores predefinidos + + Add list + Añadir lista No comment provided by engineer. @@ -599,14 +631,19 @@ Añadir perfil No comment provided by engineer. + + Add server + Añadir servidor + No comment provided by engineer. + Add servers by scanning QR codes. Añadir servidores mediante el escaneo de códigos QR. No comment provided by engineer. - - Add server… - Añadir servidor… + + Add team members + Añadir miembros del equipo No comment provided by engineer. @@ -614,11 +651,46 @@ Añadir a otro dispositivo No comment provided by engineer. + + Add to list + Añadir a la lista + No comment provided by engineer. + Add welcome message Añadir mensaje de bienvenida No comment provided by engineer. + + Add your team members to the conversations. + Añade a miembros de tu equipo a las conversaciones. + No comment provided by engineer. + + + Added media & file servers + Servidores de archivos y multimedia añadidos + No comment provided by engineer. + + + Added message servers + Servidores de mensajes añadidos + No comment provided by engineer. + + + Additional accent + Acento adicional + No comment provided by engineer. + + + Additional accent 2 + Color adicional 2 + No comment provided by engineer. + + + Additional secondary + Secundario adicional + No comment provided by engineer. + Address Dirección @@ -629,8 +701,19 @@ El cambio de dirección se cancelará. Se usará la antigua dirección de recepción. No comment provided by engineer. + + Address or 1-time link? + ¿Dirección o enlace de un uso? + No comment provided by engineer. + + + Address settings + Configurar dirección + No comment provided by engineer. + Admins can block a member for all. + Los administradores pueden bloquear a un miembro para los demás. No comment provided by engineer. @@ -643,6 +726,16 @@ Configuración avanzada de red No comment provided by engineer. + + Advanced settings + Configuración avanzada + No comment provided by engineer. + + + All + Todo + No comment provided by engineer. + All app data is deleted. Todos los datos de la aplicación se eliminarán. @@ -650,27 +743,42 @@ All chats and messages will be deleted - this cannot be undone! - Se eliminarán todos los chats y mensajes. ¡No podrá deshacerse! + Se eliminarán todos los chats y mensajes. ¡No puede deshacerse! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + Todos los chats se quitarán de la lista %@ y esta será eliminada. + alert message + All data is erased when it is entered. Al introducirlo todos los datos son eliminados. No comment provided by engineer. + + All data is kept private on your device. + Todos los datos son privados y están en tu dispositivo. + No comment provided by engineer. + All group members will remain connected. Todos los miembros del grupo permanecerán conectados. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + Todos los mensajes y archivos son enviados **cifrados de extremo a extremo** y con seguridad de cifrado postcuántico en mensajes directos. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! - Todos los mensajes serán borrados. ¡No podrá deshacerse! + Todos los mensajes serán eliminados. ¡No puede deshacerse! No comment provided by engineer. All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. - Se eliminarán todos los mensajes SOLO para tí. ¡No podrá deshacerse! + Se eliminarán todos los mensajes SOLO para tí. ¡No puede deshacerse! No comment provided by engineer. @@ -678,6 +786,21 @@ ¡Los mensajes nuevos de %@ estarán ocultos! No comment provided by engineer. + + All profiles + Todos los perfiles + profile dropdown + + + All reports will be archived for you. + Todos los informes serán archivados para ti. + No comment provided by engineer. + + + All servers + Todos los servidores + No comment provided by engineer. + All your contacts will remain connected. Todos tus contactos permanecerán conectados. @@ -690,6 +813,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Todos tus contactos, conversaciones y archivos serán cifrados, divididos y subidos de forma segura a los servidores XFTP configurados. No comment provided by engineer. @@ -702,11 +826,21 @@ Se permiten las llamadas pero sólo si tu contacto también las permite. No comment provided by engineer. + + Allow calls? + ¿Permitir llamadas? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Se permiten los mensajes temporales pero sólo si tu contacto también los permite para tí. No comment provided by engineer. + + Allow downgrade + Permitir versión anterior + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Se permite la eliminación irreversible de mensajes pero sólo si tu contacto también la permite para tí. (24 horas) @@ -732,11 +866,26 @@ Permites el envío de mensajes temporales. No comment provided by engineer. + + Allow sharing + Permitir compartir + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Se permite la eliminación irreversible de mensajes. (24 horas) No comment provided by engineer. + + Allow to report messsages to moderators. + Permitir informar de mensajes a los moderadores. + No comment provided by engineer. + + + Allow to send SimpleX links. + Se permite enviar enlaces SimpleX. + No comment provided by engineer. + Allow to send files and media. Se permite enviar archivos y multimedia. @@ -797,6 +946,11 @@ ¡Ya en proceso de unirte al grupo! No comment provided by engineer. + + Always use private routing. + Usar siempre enrutamiento privado. + No comment provided by engineer. + Always use relay Usar siempre retransmisor @@ -807,11 +961,21 @@ Se creará un perfil vacío con el nombre proporcionado, y la aplicación se abrirá como de costumbre. No comment provided by engineer. + + Another reason + Otro motivo + report reason + Answer call Responder llamada No comment provided by engineer. + + Anybody can host servers. + Cualquiera puede alojar servidores. + No comment provided by engineer. + App build: %@ Compilación app: %@ @@ -819,6 +983,7 @@ App data migration + Migrar datos de la aplicación No comment provided by engineer. @@ -826,9 +991,14 @@ Cifrado de los nuevos archivos locales (excepto vídeos). No comment provided by engineer. + + App group: + Grupo app: + No comment provided by engineer. + App icon - Icono aplicación + Icono de la aplicación No comment provided by engineer. @@ -841,6 +1011,11 @@ El código de acceso será reemplazado por código de autodestrucción. No comment provided by engineer. + + App session + por sesión + No comment provided by engineer. + App version Versión de la aplicación @@ -858,14 +1033,62 @@ Apply + Aplicar + No comment provided by engineer. + + + Apply to + Aplicar a + No comment provided by engineer. + + + Archive + Archivar + No comment provided by engineer. + + + Archive %lld reports? + ¿Archivar %lld informes? + No comment provided by engineer. + + + Archive all reports? + ¿Archivar todos los informes? No comment provided by engineer. Archive and upload + Archivar y subir + No comment provided by engineer. + + + Archive contacts to chat later. + Archiva contactos para charlar más tarde. + No comment provided by engineer. + + + Archive report + Archivar informe + No comment provided by engineer. + + + Archive report? + ¿Archivar informe? + No comment provided by engineer. + + + Archive reports + Archivar informes + swipe action + + + Archived contacts + Contactos archivados No comment provided by engineer. Archiving database + Archivando base de datos No comment provided by engineer. @@ -928,11 +1151,21 @@ Aceptar imágenes automáticamente No comment provided by engineer. + + Auto-accept settings + Auto aceptar configuración + alert title + Back Volver No comment provided by engineer. + + Background + Fondo + No comment provided by engineer. + Bad desktop address Dirección ordenador incorrecta @@ -948,16 +1181,61 @@ Hash de mensaje incorrecto No comment provided by engineer. + + Better calls + Llamadas mejoradas + No comment provided by engineer. + Better groups Grupos mejorados No comment provided by engineer. + + Better groups performance + Rendimiento de grupos mejorado + No comment provided by engineer. + + + Better message dates. + Sistema de fechas mejorado. + No comment provided by engineer. + Better messages Mensajes mejorados No comment provided by engineer. + + Better networking + Uso de red mejorado + No comment provided by engineer. + + + Better notifications + Notificaciones mejoradas + No comment provided by engineer. + + + Better privacy and security + Privacidad y seguridad mejoradas + No comment provided by engineer. + + + Better security ✅ + Seguridad mejorada ✅ + No comment provided by engineer. + + + Better user experience + Experiencia de usuario mejorada + No comment provided by engineer. + + + Black + Negro + No comment provided by engineer. + Block Bloquear @@ -990,7 +1268,17 @@ Blocked by admin - Bloqueado por el administrador + Bloqueado por administrador + No comment provided by engineer. + + + Blur for better privacy. + Difumina para mayor privacidad. + No comment provided by engineer. + + + Blur media + Difuminar multimedia No comment provided by engineer. @@ -1023,9 +1311,33 @@ Búlgaro, Finlandés, Tailandés y Ucraniano - gracias a los usuarios y [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + Dirección empresarial + No comment provided by engineer. + + + Business chats + Chats empresariales + No comment provided by engineer. + + + Businesses + Empresas + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). - Mediante perfil (por defecto) o [por conexión](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). + Mediante perfil (predeterminado) o [por conexión](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). + No comment provided by engineer. + + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + Al usar SimpleX Chat, aceptas: +- enviar únicamente contenido legal en los grupos públicos. +- respetar a los demás usuarios – spam prohibido. No comment provided by engineer. @@ -1038,11 +1350,26 @@ Llamadas No comment provided by engineer. + + Calls prohibited! + ¡Llamadas no permitidas! + No comment provided by engineer. + Camera not available Cámara no disponible No comment provided by engineer. + + Can't call contact + No se puede llamar al contacto + No comment provided by engineer. + + + Can't call member + No se puede llamar al miembro + No comment provided by engineer. + Can't invite contact! ¡No se puede invitar el contacto! @@ -1053,13 +1380,20 @@ ¡No se pueden invitar contactos! No comment provided by engineer. + + Can't message member + No se pueden enviar mensajes al miembro + No comment provided by engineer. + Cancel Cancelar - No comment provided by engineer. + alert action +alert button Cancel migration + Cancelar migración No comment provided by engineer. @@ -1067,9 +1401,24 @@ Keychain inaccesible para guardar la contraseña de la base de datos No comment provided by engineer. + + Cannot forward message + No se puede reenviar el mensaje + No comment provided by engineer. + Cannot receive file No se puede recibir el archivo + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Capacidad excedida - el destinatario no ha recibido los mensajes previos. + snd error text + + + Cellular + Móvil No comment provided by engineer. @@ -1077,6 +1426,16 @@ Cambiar No comment provided by engineer. + + Change automatic message deletion? + ¿Modificar la eliminación automática de mensajes? + alert title + + + Change chat profiles + Cambiar perfil de usuario + authentication reason + Change database passphrase? ¿Cambiar contraseña de la base de datos? @@ -1121,11 +1480,26 @@ Change self-destruct passcode Cambiar código autodestrucción authentication reason - set passcode view +set passcode view - - Chat archive - Archivo del chat + + Chat + Chat + No comment provided by engineer. + + + Chat already exists + El chat ya existe + No comment provided by engineer. + + + Chat already exists! + ¡El chat ya existe! + No comment provided by engineer. + + + Chat colors + Colores del chat No comment provided by engineer. @@ -1135,7 +1509,7 @@ Chat database - Base de datos del chat + Base de datos de SimpleX No comment provided by engineer. @@ -1143,6 +1517,11 @@ Base de datos eliminada No comment provided by engineer. + + Chat database exported + Base de datos exportada + No comment provided by engineer. + Chat database imported Base de datos importada @@ -1150,21 +1529,27 @@ Chat is running - Chat está en ejecución + SimpleX está en ejecución No comment provided by engineer. Chat is stopped - Chat está detenido + SimpleX está parado No comment provided by engineer. Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat. - Chat está detenido. Si estás usando esta base de datos en otro dispositivo, deberías transferirla de vuelta antes de iniciarlo. + SimpleX está parado. Si has usado esta base de datos en otro dispositivo, debes transferirla de vuelta antes de iniciar SimpleX. + No comment provided by engineer. + + + Chat list + Lista de chats No comment provided by engineer. Chat migrated! + ¡Chat migrado! No comment provided by engineer. @@ -1172,15 +1557,50 @@ Preferencias de Chat No comment provided by engineer. + + Chat preferences were changed. + Las preferencias del chat han sido modificadas. + alert message + + + Chat profile + Perfil de usuario + No comment provided by engineer. + + + Chat theme + Tema de chat + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + El chat será eliminado para todos los miembros. ¡No puede deshacerse! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + El chat será eliminado para tí. ¡No puede deshacerse! + No comment provided by engineer. + Chats Chats No comment provided by engineer. + + Check messages every 20 min. + Comprobar mensajes cada 20 min. + No comment provided by engineer. + + + Check messages when allowed. + Comprobar mensajes cuando se permita. + No comment provided by engineer. + Check server address and try again. Comprueba la dirección del servidor e inténtalo de nuevo. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1189,6 +1609,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + En el nuevo dispositivo selecciona _Migrar desde otro dispositivo_ y escanéa el código QR. No comment provided by engineer. @@ -1201,10 +1622,25 @@ Elige de la biblioteca No comment provided by engineer. + + Chunks deleted + Bloques eliminados + No comment provided by engineer. + + + Chunks downloaded + Bloques descargados + No comment provided by engineer. + + + Chunks uploaded + Bloques subidos + No comment provided by engineer. + Clear Vaciar - No comment provided by engineer. + swipe action Clear conversation @@ -1216,9 +1652,19 @@ ¿Vaciar conversación? No comment provided by engineer. + + Clear group? + ¿Vaciar grupo? + No comment provided by engineer. + + + Clear or delete group? + ¿Vaciar o eliminar grupo? + No comment provided by engineer. + Clear private notes? - ¿Borrar notas privadas? + ¿Eliminar notas privadas? No comment provided by engineer. @@ -1226,11 +1672,21 @@ Eliminar verificación No comment provided by engineer. - - Colors - Colores + + Color chats with the new themes. + Colorea los chats con los nuevos temas. No comment provided by engineer. + + Color mode + Modo de color + No comment provided by engineer. + + + Community guidelines violation + Violación de las normas de la comunidad + report reason + Compare file Comparar archivo @@ -1241,11 +1697,56 @@ Compara los códigos de seguridad con tus contactos. No comment provided by engineer. + + Completed + Completadas + No comment provided by engineer. + + + Conditions accepted on: %@. + Condiciones aceptadas el: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + Las condiciones se han aceptado para el(los) operador(s): **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + Las condiciones ya se han aceptado para el/los siguiente(s) operador(s): **%@**. + No comment provided by engineer. + + + Conditions of use + Condiciones de uso + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + Las condiciones serán aceptadas para el/los operador(es): **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + Las condiciones serán aceptadas el: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + Las condiciones serán aceptadas automáticamente para los operadores habilitados el: %@. + No comment provided by engineer. + Configure ICE servers Configure servidores ICE No comment provided by engineer. + + Configure server operators + Configurar operadores de servidores + No comment provided by engineer. + Confirm Confirmar @@ -1256,13 +1757,24 @@ Confirma Código No comment provided by engineer. + + Confirm contact deletion? + ¿Confirmas la eliminación del contacto? + No comment provided by engineer. + Confirm database upgrades Confirmar actualizaciones de la bases de datos No comment provided by engineer. + + Confirm files from unknown servers. + Confirma archivos de servidores desconocidos. + No comment provided by engineer. + Confirm network settings + Confirmar configuración de red No comment provided by engineer. @@ -1277,12 +1789,19 @@ Confirm that you remember database passphrase to migrate it. + Para migrar la base de datos confirma que recuerdas la frase de contraseña. No comment provided by engineer. Confirm upload + Confirmar subida No comment provided by engineer. + + Confirmed + Confirmado + token status text + Connect Conectar @@ -1303,6 +1822,11 @@ Conectar con ordenador No comment provided by engineer. + + Connect to your friends faster. + Conecta más rápido con tus amigos. + No comment provided by engineer. + Connect to yourself? ¿Conectarte a tí mismo? @@ -1342,16 +1866,31 @@ This is your own one-time link! Conectar con %@ No comment provided by engineer. + + Connected + Conectadas + No comment provided by engineer. + Connected desktop Ordenador conectado No comment provided by engineer. + + Connected servers + Servidores conectados + No comment provided by engineer. + Connected to desktop Conectado con ordenador No comment provided by engineer. + + Connecting + Conectando + No comment provided by engineer. + Connecting to server… Conectando con el servidor… @@ -1362,6 +1901,11 @@ This is your own one-time link! Conectando con el servidor... (error: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + Conectando con el contacto, por favor espera o revisa más tarde. + No comment provided by engineer. + Connecting to desktop Conectando con ordenador @@ -1372,6 +1916,16 @@ This is your own one-time link! Conexión No comment provided by engineer. + + Connection and servers status. + Estado de tu conexión y servidores. + No comment provided by engineer. + + + Connection blocked + Conexión bloqueada + No comment provided by engineer. + Connection error Error conexión @@ -1379,7 +1933,24 @@ This is your own one-time link! Connection error (AUTH) - Error conexión (Autenticación) + Error de conexión (Autenticación) + No comment provided by engineer. + + + Connection is blocked by server operator: +%@ + Conexión bloqueada por el operador del servidor: +%@ + No comment provided by engineer. + + + Connection not ready. + Conexión no establecida. + No comment provided by engineer. + + + Connection notifications + Notificaciones de conexión No comment provided by engineer. @@ -1387,6 +1958,16 @@ This is your own one-time link! ¡Solicitud de conexión enviada! No comment provided by engineer. + + Connection requires encryption renegotiation. + La conexión requiere renegociar el cifrado. + No comment provided by engineer. + + + Connection security + Seguridad de conexión + No comment provided by engineer. + Connection terminated Conexión finalizada @@ -1394,7 +1975,17 @@ This is your own one-time link! Connection timeout - Tiempo de conexión expirado + Tiempo de conexión agotado + No comment provided by engineer. + + + Connection with desktop stopped + La conexión con el escritorio (desktop) se ha parado + No comment provided by engineer. + + + Connections + Conexiones No comment provided by engineer. @@ -1407,6 +1998,11 @@ This is your own one-time link! El contácto ya existe No comment provided by engineer. + + Contact deleted! + ¡Contacto eliminado! + No comment provided by engineer. + Contact hidden: Contacto oculto: @@ -1417,9 +2013,9 @@ This is your own one-time link! El contacto está en línea notification - - Contact is not connected yet! - ¡El contacto aun no se ha conectado! + + Contact is deleted. + El contacto está eliminado. No comment provided by engineer. @@ -1432,6 +2028,11 @@ This is your own one-time link! Preferencias de contacto No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + El contacto será eliminado. ¡No puede deshacerse! + No comment provided by engineer. + Contacts Contactos @@ -1442,21 +2043,41 @@ This is your own one-time link! Tus contactos sólo pueden marcar los mensajes para eliminar. Tu podrás verlos. No comment provided by engineer. + + Content violates conditions of use + El contenido viola las condiciones de uso + blocking reason + Continue Continuar No comment provided by engineer. + + Conversation deleted! + ¡Conversación eliminada! + No comment provided by engineer. + Copy Copiar - chat item action + No comment provided by engineer. + + + Copy error + Copiar error + No comment provided by engineer. Core version: v%@ Versión Core: v%@ No comment provided by engineer. + + Corner + Esquina + No comment provided by engineer. + Correct name to %@? ¿Corregir el nombre a %@? @@ -1467,6 +2088,11 @@ This is your own one-time link! Crear No comment provided by engineer. + + Create 1-time link + Crear enlace de un uso + No comment provided by engineer. + Create SimpleX address Crear dirección SimpleX @@ -1477,11 +2103,6 @@ This is your own one-time link! Crear grupo usando perfil aleatorio. No comment provided by engineer. - - Create an address to let people connect with you. - Crea una dirección para que otras personas puedan conectar contigo. - No comment provided by engineer. - Create file Crear archivo @@ -1502,6 +2123,11 @@ This is your own one-time link! Crear enlace No comment provided by engineer. + + Create list + Crear lista + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 Crea perfil nuevo en la [aplicación para PC](https://simplex.Descargas/de chat/). 💻 @@ -1527,6 +2153,11 @@ This is your own one-time link! Crea tu perfil No comment provided by engineer. + + Created + Creadas + No comment provided by engineer. + Created at Creado @@ -1537,13 +2168,9 @@ This is your own one-time link! Creado: %@ copied message info - - Created on %@ - Creado en %@ - No comment provided by engineer. - Creating archive link + Creando enlace al archivo No comment provided by engineer. @@ -1556,11 +2183,21 @@ This is your own one-time link! Código de Acceso No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + El texto con las condiciones actuales no se ha podido cargar, puedes revisar las condiciones en el siguiente enlace: + No comment provided by engineer. + Current passphrase… Contraseña actual… No comment provided by engineer. + + Current profile + Perfil actual + No comment provided by engineer. + Currently maximum supported file size is %@. El tamaño máximo de archivo admitido es %@. @@ -1571,11 +2208,26 @@ This is your own one-time link! Tiempo personalizado No comment provided by engineer. + + Customizable message shape. + Forma personalizable de los mensajes. + No comment provided by engineer. + + + Customize theme + Personalizar tema + No comment provided by engineer. + Dark Oscuro No comment provided by engineer. + + Dark mode colors + Colores en modo oscuro + No comment provided by engineer. + Database ID ID base de datos @@ -1642,7 +2294,7 @@ This is your own one-time link! Database passphrase is different from saved in the keychain. - La contraseña es distinta a la almacenada en Keychain. + La contraseña es diferente a la almacenada en Keychain. No comment provided by engineer. @@ -1674,6 +2326,11 @@ This is your own one-time link! La base de datos migrará cuando se reinicie la aplicación No comment provided by engineer. + + Debug delivery + Informe debug + No comment provided by engineer. + Decentralized Descentralizada @@ -1687,16 +2344,17 @@ This is your own one-time link! Delete Eliminar - chat item action + alert action +swipe action + + + Delete %lld messages of members? + ¿Eliminar %lld mensajes de miembros? + No comment provided by engineer. Delete %lld messages? - ¿Elimina %lld mensajes? - No comment provided by engineer. - - - Delete Contact - Eliminar contacto + ¿Eliminar %lld mensajes? No comment provided by engineer. @@ -1724,14 +2382,14 @@ This is your own one-time link! Eliminar y notificar contacto No comment provided by engineer. - - Delete archive - Eliminar archivo + + Delete chat + Eliminar chat No comment provided by engineer. - - Delete chat archive? - ¿Eliminar archivo del chat? + + Delete chat messages from your device. + Elimina los mensajes del dispositivo. No comment provided by engineer. @@ -1741,7 +2399,12 @@ This is your own one-time link! Delete chat profile? - ¿Eliminar el perfil? + ¿Eliminar perfil? + No comment provided by engineer. + + + Delete chat? + ¿Eliminar chat? No comment provided by engineer. @@ -1754,11 +2417,9 @@ This is your own one-time link! Eliminar contacto No comment provided by engineer. - - Delete contact? -This cannot be undone! - ¿Eliminar contacto? -¡No podrá deshacerse! + + Delete contact? + ¿Eliminar contacto? No comment provided by engineer. @@ -1768,6 +2429,7 @@ This cannot be undone! Delete database from this device + Eliminar base de datos de este dispositivo No comment provided by engineer. @@ -1777,7 +2439,7 @@ This cannot be undone! Delete files and media? - Eliminar archivos y multimedia? + ¿Eliminar archivos y multimedia? No comment provided by engineer. @@ -1820,6 +2482,11 @@ This cannot be undone! ¿Eliminar enlace? No comment provided by engineer. + + Delete list? + ¿Eliminar lista? + alert title + Delete member message? ¿Eliminar el mensaje de miembro? @@ -1832,8 +2499,8 @@ This cannot be undone! Delete messages - Eliminar mensaje - No comment provided by engineer. + Activar + alert button Delete messages after @@ -1850,9 +2517,9 @@ This cannot be undone! ¿Eliminar base de datos antigua? No comment provided by engineer. - - Delete pending connection - Eliminar conexión pendiente + + Delete or moderate up to 200 messages. + Elimina o modera hasta 200 mensajes a la vez. No comment provided by engineer. @@ -1870,11 +2537,31 @@ This cannot be undone! Eliminar cola server test step + + Delete report + Eliminar informe + No comment provided by engineer. + + + Delete up to 20 messages at once. + Elimina hasta 20 mensajes a la vez. + No comment provided by engineer. + Delete user profile? ¿Eliminar perfil de usuario? No comment provided by engineer. + + Delete without notification + Elimina sin notificar + No comment provided by engineer. + + + Deleted + Eliminadas + No comment provided by engineer. + Deleted at Eliminado @@ -1885,6 +2572,16 @@ This cannot be undone! Eliminado: %@ copied message info + + Deletion errors + Errores de eliminación + No comment provided by engineer. + + + Delivered even when Apple drops them. + Entregados incluso cuando Apple los descarta. + No comment provided by engineer. + Delivery Entrega @@ -1920,11 +2617,41 @@ This cannot be undone! Ordenadores No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + La dirección del servidor de destino de %@ es incompatible con la configuración del servidor de reenvío %@. + No comment provided by engineer. + + + Destination server error: %@ + Error del servidor de destino: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + La versión del servidor de destino de %@ es incompatible con el servidor de reenvío %@. + No comment provided by engineer. + + + Detailed statistics + Estadísticas detalladas + No comment provided by engineer. + + + Details + Detalles + No comment provided by engineer. + Develop Desarrollo No comment provided by engineer. + + Developer options + Opciones desarrollador + No comment provided by engineer. + Developer tools Herramientas desarrollo @@ -1955,8 +2682,13 @@ This cannot be undone! Mensajes directos chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + Mensajes directos no permitidos entre miembros de este chat. + No comment provided by engineer. + + + Direct messages between members are prohibited. Los mensajes directos entre miembros del grupo no están permitidos. No comment provided by engineer. @@ -1970,11 +2702,26 @@ This cannot be undone! Desactivar Bloqueo SimpleX authentication reason + + Disable automatic message deletion? + ¿Desactivar la eliminación automática de mensajes? + alert title + + + Disable delete messages + Desactivar + alert button + Disable for all Desactivar para todos No comment provided by engineer. + + Disabled + Desactivado + No comment provided by engineer. + Disappearing message Mensaje temporal @@ -1990,8 +2737,8 @@ This cannot be undone! Los mensajes temporales no están permitidos en este chat. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. Los mensajes temporales no están permitidos en este grupo. No comment provided by engineer. @@ -2012,7 +2759,7 @@ This cannot be undone! Disconnect desktop? - ¿Desconectar ordenador? + ¿Desconectar del ordenador? No comment provided by engineer. @@ -2025,11 +2772,21 @@ This cannot be undone! Descubrir en red local No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + NO enviar mensajes directamente incluso si tu servidor o el de destino no soportan enrutamiento privado. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. NO uses SimpleX para llamadas de emergencia. No comment provided by engineer. + + Do NOT use private routing. + NO usar enrutamiento privado. + No comment provided by engineer. + Do it later Hacer más tarde @@ -2037,7 +2794,17 @@ This cannot be undone! Do not send history to new members. - No enviar historial a miembros nuevos. + No se envía el historial a los miembros nuevos. + No comment provided by engineer. + + + Do not use credentials with proxy. + No uses credenciales con proxy. + No comment provided by engineer. + + + Documents: + Documentos: No comment provided by engineer. @@ -2050,9 +2817,19 @@ This cannot be undone! No activar No comment provided by engineer. + + Don't miss important messages. + No pierdas los mensajes importantes. + No comment provided by engineer. + Don't show again - No mostrar de nuevo + No volver a mostrar + No comment provided by engineer. + + + Done + Hecho No comment provided by engineer. @@ -2060,8 +2837,20 @@ This cannot be undone! Degradar y abrir Chat No comment provided by engineer. + + Download + Descargar + alert button +chat item action + + + Download errors + Errores en la descarga + No comment provided by engineer. + Download failed + Descarga fallida No comment provided by engineer. @@ -2069,12 +2858,29 @@ This cannot be undone! Descargar archivo server test step + + Download files + Descargar archivos + alert action + + + Downloaded + Descargado + No comment provided by engineer. + + + Downloaded files + Archivos descargados + No comment provided by engineer. + Downloading archive + Descargando archivo No comment provided by engineer. Downloading link details + Descargando detalles del enlace No comment provided by engineer. @@ -2087,6 +2893,11 @@ This cannot be undone! Duración No comment provided by engineer. + + E2E encrypted notifications. + Notificaciones cifradas E2E. + No comment provided by engineer. + Edit Editar @@ -2107,6 +2918,11 @@ This cannot be undone! Activar (conservar anulaciones) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + Habilitar Flux en la configuración de Red y servidores para mejorar la privacidad de los metadatos. + No comment provided by engineer. + Enable SimpleX Lock Activar Bloqueo SimpleX @@ -2120,7 +2936,7 @@ This cannot be undone! Enable automatic message deletion? ¿Activar eliminación automática de mensajes? - No comment provided by engineer. + alert title Enable camera access @@ -2134,6 +2950,7 @@ This cannot be undone! Enable in direct chats (BETA)! + ¡Activar en chats directos (BETA)! No comment provided by engineer. @@ -2166,6 +2983,16 @@ This cannot be undone! Activar código de autodestrucción set passcode view + + Enabled + Activado + No comment provided by engineer. + + + Enabled for + Activado para + No comment provided by engineer. + Encrypt Cifrar @@ -2178,7 +3005,7 @@ This cannot be undone! Encrypt local files - Cifra archivos locales + Cifrar archivos locales No comment provided by engineer. @@ -2236,6 +3063,11 @@ This cannot be undone! Renegociación de cifrado fallida. No comment provided by engineer. + + Encryption renegotiation in progress. + Renegociación de cifrado en curso. + No comment provided by engineer. + Enter Passcode Introduce Código @@ -2253,6 +3085,7 @@ This cannot be undone! Enter passphrase + Introduce la frase de contraseña No comment provided by engineer. @@ -2267,7 +3100,7 @@ This cannot be undone! Enter server manually - Introduce el servidor manualmente + Añadir manualmente No comment provided by engineer. @@ -2300,30 +3133,36 @@ This cannot be undone! Error al cancelar cambio de dirección No comment provided by engineer. + + Error accepting conditions + Error al aceptar las condiciones + alert title + Error accepting contact request Error al aceptar solicitud del contacto No comment provided by engineer. - - Error accessing database file - Error al acceder al archivo de la base de datos - No comment provided by engineer. - Error adding member(s) Error al añadir miembro(s) No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + Error al añadir servidor + alert title Error changing address Error al cambiar servidor No comment provided by engineer. + + Error changing connection profile + Error al cambiar el perfil de conexión + No comment provided by engineer. + Error changing role Error al cambiar rol @@ -2334,6 +3173,21 @@ This cannot be undone! Error cambiando configuración No comment provided by engineer. + + Error changing to incognito! + ¡Error al cambiar a incógnito! + No comment provided by engineer. + + + Error checking token status + Error al verificar el estado del token + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + Error al conectar con el servidor de reenvío %@. Por favor, inténtalo más tarde. + No comment provided by engineer. + Error creating address Error al crear dirección @@ -2349,6 +3203,11 @@ This cannot be undone! Error al crear enlace de grupo No comment provided by engineer. + + Error creating list + Error al crear lista + alert title + Error creating member contact Error al establecer contacto con el miembro @@ -2364,6 +3223,11 @@ This cannot be undone! ¡Error al crear perfil! No comment provided by engineer. + + Error creating report + Error al crear informe + No comment provided by engineer. + Error decrypting file Error al descifrar el archivo @@ -2384,11 +3248,6 @@ This cannot be undone! Error al eliminar conexión No comment provided by engineer. - - Error deleting contact - Error al eliminar contacto - No comment provided by engineer. - Error deleting database Error al eliminar base de datos @@ -2411,6 +3270,7 @@ This cannot be undone! Error downloading the archive + Error al descargar el archivo No comment provided by engineer. @@ -2433,6 +3293,11 @@ This cannot be undone! Error al exportar base de datos No comment provided by engineer. + + Error exporting theme: %@ + Error al exportar tema: %@ + No comment provided by engineer. + Error importing chat database Error al importar base de datos @@ -2443,9 +3308,14 @@ This cannot be undone! Error al unirte al grupo No comment provided by engineer. - - Error loading %@ servers - Error al cargar servidores %@ + + Error loading servers + Error al cargar servidores + alert title + + + Error migrating settings + Error al migrar la configuración No comment provided by engineer. @@ -2456,16 +3326,36 @@ This cannot be undone! Error receiving file Error al recibir archivo + alert title + + + Error reconnecting server + Error al reconectar con el servidor No comment provided by engineer. + + Error reconnecting servers + Error al reconectar con los servidores + No comment provided by engineer. + + + Error registering for notifications + Error al registrarse para notificaciones + alert title + Error removing member - Error al eliminar miembro + Error al expulsar miembro No comment provided by engineer. - - Error saving %@ servers - Error al guardar servidores %@ + + Error reordering lists + Error al reorganizar listas + alert title + + + Error resetting statistics + Error al restablecer las estadísticas No comment provided by engineer. @@ -2473,6 +3363,11 @@ This cannot be undone! Error al guardar servidores ICE No comment provided by engineer. + + Error saving chat list + Error al guardar listas + alert title + Error saving group profile Error al guardar perfil de grupo @@ -2488,8 +3383,14 @@ This cannot be undone! Error al guardar contraseña en Keychain No comment provided by engineer. + + Error saving servers + Error al guardar servidores + alert title + Error saving settings + Error al guardar ajustes when migrating @@ -2529,19 +3430,29 @@ This cannot be undone! Error stopping chat - Error al detener Chat + Error al parar SimpleX + No comment provided by engineer. + + + Error switching profile + Error al cambiar perfil No comment provided by engineer. Error switching profile! ¡Error al cambiar perfil! - No comment provided by engineer. + alertTitle Error synchronizing connection Error al sincronizar conexión No comment provided by engineer. + + Error testing server connection + Error al testar la conexión al servidor + No comment provided by engineer. + Error updating group link Error al actualizar enlace de grupo @@ -2552,6 +3463,11 @@ This cannot be undone! Error al actualizar mensaje No comment provided by engineer. + + Error updating server + Error al actualizar el servidor + alert title + Error updating settings Error al actualizar configuración @@ -2564,10 +3480,12 @@ This cannot be undone! Error uploading the archive + Error al subir el archivo No comment provided by engineer. Error verifying passphrase: + Error al verificar la frase de contraseña: No comment provided by engineer. @@ -2578,7 +3496,9 @@ This cannot be undone! Error: %@ Error: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2590,6 +3510,16 @@ This cannot be undone! Error: sin archivo de base de datos No comment provided by engineer. + + Errors + Errores + No comment provided by engineer. + + + Errors in servers configuration. + Error en la configuración del servidor. + servers error + Even when disabled in the conversation. Incluso si está desactivado para la conversación. @@ -2605,6 +3535,11 @@ This cannot be undone! Expandir chat item action + + Expired + Expirado + token status text + Export database Exportar base de datos @@ -2615,6 +3550,11 @@ This cannot be undone! Error al exportar: No comment provided by engineer. + + Export theme + Exportar tema + No comment provided by engineer. + Exported database archive. Archivo de base de datos exportado. @@ -2622,6 +3562,7 @@ This cannot be undone! Exported file doesn't exist + El archivo exportado no existe No comment provided by engineer. @@ -2639,16 +3580,70 @@ This cannot be undone! ¡Rápido y sin necesidad de esperar a que el remitente esté en línea! No comment provided by engineer. + + Faster deletion of groups. + Eliminación más rápida de grupos. + No comment provided by engineer. + Faster joining and more reliable messages. Mensajería más segura y conexión más rápida. No comment provided by engineer. + + Faster sending messages. + Envío más rápido de mensajes. + No comment provided by engineer. + Favorite Favoritos + swipe action + + + Favorites + Favoritos No comment provided by engineer. + + File error + Error de archivo + file error alert title + + + File errors: +%@ + Error(es) de archivo +%@ + alert message + + + File is blocked by server operator: +%@. + Archivo bloqueado por el operador del servidor +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + Archivo no encontrado, probablemente haya sido eliminado o cancelado. + file error text + + + File server error: %@ + Error del servidor de archivos: %@ + file error text + + + File status + Estado del archivo + No comment provided by engineer. + + + File status: %@ + Estado del archivo: %@ + copied message info + File will be deleted from servers. El archivo será eliminado de los servidores. @@ -2656,12 +3651,12 @@ This cannot be undone! File will be received when your contact completes uploading it. - El archivo se recibirá cuando tu contacto termine de subirlo. + El archivo se recibirá cuando el contacto termine de subirlo. No comment provided by engineer. File will be received when your contact is online, please wait or check later! - El archivo se recibirá cuando tu contacto esté en línea, ¡por favor espera o compruébalo más tarde! + El archivo se recibirá cuando el contacto esté en línea, ¡por favor espera o revisa más tarde! No comment provided by engineer. @@ -2669,6 +3664,11 @@ This cannot be undone! Archivo: %@ No comment provided by engineer. + + Files + Archivos + No comment provided by engineer. + Files & media Archivos y multimedia @@ -2679,9 +3679,14 @@ This cannot be undone! Archivos y multimedia chat feature - - Files and media are prohibited in this group. - No se permiten archivos y multimedia en este grupo. + + Files and media are prohibited. + Los archivos y multimedia no están permitidos en este grupo. + No comment provided by engineer. + + + Files and media not allowed + Archivos y multimedia no permitidos No comment provided by engineer. @@ -2696,10 +3701,12 @@ This cannot be undone! Finalize migration + Finalizar migración No comment provided by engineer. Finalize migration on another device. + Finalizar la migración en otro dispositivo. No comment provided by engineer. @@ -2742,11 +3749,115 @@ This cannot be undone! Corrección no compatible con miembro del grupo No comment provided by engineer. + + For all moderators + Para todos los moderadores + No comment provided by engineer. + + + For chat profile %@: + Para el perfil de chat %@: + servers error + For console Para consola No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + Si por ejemplo tu contacto recibe los mensajes a través de un servidor de SimpleX Chat, tu aplicación los entregará a través de un servidor de Flux. + No comment provided by engineer. + + + For me + para mí + No comment provided by engineer. + + + For private routing + Para enrutamiento privado + No comment provided by engineer. + + + For social media + Para redes sociales + No comment provided by engineer. + + + Forward + Reenviar + chat item action + + + Forward %d message(s)? + ¿Reenviar %d mensaje(s)? + alert title + + + Forward and save messages + Reenviar y guardar mensajes + No comment provided by engineer. + + + Forward messages + Reenviar mensajes + alert action + + + Forward messages without files? + ¿Reenviar mensajes sin los archivos? + alert message + + + Forward up to 20 messages at once. + Desplazamiento de hasta 20 mensajes. + No comment provided by engineer. + + + Forwarded + Reenviado + No comment provided by engineer. + + + Forwarded from + Reenviado por + No comment provided by engineer. + + + Forwarding %lld messages + Reenviando %lld mensajes + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + El servidor de reenvío %@ no ha podido conectarse al servidor de destino %@. Por favor, intentalo más tarde. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + La dirección del servidor de reenvío es incompatible con la configuración de red: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + La versión del servidor de reenvío es incompatible con la configuración de red: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + Servidor de reenvío: %1$@ +Error del servidor de destino: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + Servidor de reenvío: %1$@ +Error: %2$@ + snd error text + Found desktop Ordenador encontrado @@ -2767,19 +3878,14 @@ This cannot be undone! Nombre completo (opcional) No comment provided by engineer. - - Full name: - Nombre completo: - No comment provided by engineer. - Fully decentralized – visible only to members. - Completamente descentralizado: sólo visible a los miembros. + Totalmente descentralizado. Visible sólo para los miembros. No comment provided by engineer. Fully re-implemented - work in background! - Completamente reimplementado: ¡funciona en segundo plano! + Totalmente revisado. ¡Funciona en segundo plano! No comment provided by engineer. @@ -2792,6 +3898,21 @@ This cannot be undone! GIFs y stickers No comment provided by engineer. + + Get notified when mentioned. + Las menciones ahora se notifican. + No comment provided by engineer. + + + Good afternoon! + ¡Buenas tardes! + message preview + + + Good morning! + ¡Buenos días! + message preview + Group Grupo @@ -2847,36 +3968,6 @@ This cannot be undone! Enlaces de grupo No comment provided by engineer. - - Group members can add message reactions. - Los miembros pueden añadir reacciones a los mensajes. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Los miembros del grupo pueden eliminar mensajes de forma irreversible. (24 horas) - No comment provided by engineer. - - - Group members can send direct messages. - Los miembros del grupo pueden enviar mensajes directos. - No comment provided by engineer. - - - Group members can send disappearing messages. - Los miembros del grupo pueden enviar mensajes temporales. - No comment provided by engineer. - - - Group members can send files and media. - Los miembros del grupo pueden enviar archivos y multimedia. - No comment provided by engineer. - - - Group members can send voice messages. - Los miembros del grupo pueden enviar mensajes de voz. - No comment provided by engineer. - Group message: Mensaje de grupo: @@ -2909,12 +4000,17 @@ This cannot be undone! Group will be deleted for all members - this cannot be undone! - El grupo será eliminado para todos los miembros. ¡No podrá deshacerse! + El grupo será eliminado para todos los miembros. ¡No puede deshacerse! No comment provided by engineer. Group will be deleted for you - this cannot be undone! - El grupo será eliminado para tí. ¡No podrá deshacerse! + El grupo será eliminado para tí. ¡No puede deshacerse! + No comment provided by engineer. + + + Groups + Grupos No comment provided by engineer. @@ -2922,6 +4018,11 @@ This cannot be undone! Ayuda No comment provided by engineer. + + Help admins moderating their groups. + Ayuda a los admins a moderar sus grupos. + No comment provided by engineer. + Hidden Oculto @@ -2972,10 +4073,20 @@ This cannot be undone! Cómo funciona SimpleX No comment provided by engineer. + + How it affects privacy + Cómo afecta a la privacidad + No comment provided by engineer. + + + How it helps privacy + Cómo ayuda a la privacidad + No comment provided by engineer. + How it works Cómo funciona - No comment provided by engineer. + alert button How to @@ -2994,6 +4105,7 @@ This cannot be undone! Hungarian interface + Interfaz en húngaro No comment provided by engineer. @@ -3001,9 +4113,14 @@ This cannot be undone! Servidores ICE (uno por línea) No comment provided by engineer. + + IP address + Dirección IP + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. - Si no puedes reunirte en persona, muestra el código QR por videollamada, o comparte el enlace. + Si no puedes reunirte en persona, muestra el código QR por videollamada o comparte el enlace. No comment provided by engineer. @@ -3028,12 +4145,12 @@ This cannot be undone! Image will be received when your contact completes uploading it. - La imagen se recibirá cuando tu contacto termine de subirla. + La imagen se recibirá cuando el contacto termine de subirla. No comment provided by engineer. Image will be received when your contact is online, please wait or check later! - La imagen se recibirá cuando tu contacto esté en línea, ¡por favor espera o compruébalo más tarde! + La imagen se recibirá cuando el contacto esté en línea, ¡por favor espera o revisa más tarde! No comment provided by engineer. @@ -3041,8 +4158,8 @@ This cannot be undone! Inmediatamente No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Inmune a spam y abuso No comment provided by engineer. @@ -3063,10 +4180,24 @@ This cannot be undone! Import failed + Error de importación + No comment provided by engineer. + + + Import theme + Importar tema No comment provided by engineer. Importing archive + Importando archivo + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Reducción del tráfico y entrega mejorada. +¡Pronto habrá nuevas mejoras! No comment provided by engineer. @@ -3086,6 +4217,7 @@ This cannot be undone! In order to continue, chat should be stopped. + Para continuar, SimpleX debe estar parado. No comment provided by engineer. @@ -3093,6 +4225,21 @@ This cannot be undone! En respuesta a No comment provided by engineer. + + In-call sounds + Sonido de llamada + No comment provided by engineer. + + + Inappropriate content + Contenido inapropiado + report reason + + + Inappropriate profile + Perfil inapropiado + report reason + Incognito Incógnito @@ -3163,6 +4310,11 @@ This cannot be undone! Instalar terminal para [SimpleX Chat](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Al instante + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3170,16 +4322,41 @@ This cannot be undone! No comment provided by engineer. - - Instantly - Al instante - No comment provided by engineer. - Interface Interfaz No comment provided by engineer. + + Interface colors + Colores del interfaz + No comment provided by engineer. + + + Invalid + No válido + token status text + + + Invalid (bad token) + No válido (token incorrecto) + token status text + + + Invalid (expired) + No válido (expirado) + token status text + + + Invalid (unregistered) + No válido (no registrado) + token status text + + + Invalid (wrong topic) + No válido (tópico incorrecto) + token status text + Invalid QR code Código QR no válido @@ -3202,6 +4379,7 @@ This cannot be undone! Invalid migration confirmation + Confirmación de migración no válida No comment provided by engineer. @@ -3217,7 +4395,7 @@ This cannot be undone! Invalid server address! ¡Dirección de servidor no válida! - No comment provided by engineer. + alert title Invalid status @@ -3239,6 +4417,11 @@ This cannot be undone! Invitar miembros No comment provided by engineer. + + Invite to chat + Invitar al chat + No comment provided by engineer. + Invite to group Invitar al grupo @@ -3254,8 +4437,8 @@ This cannot be undone! La eliminación irreversible de mensajes no está permitida en este chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. La eliminación irreversible de mensajes no está permitida en este grupo. No comment provided by engineer. @@ -3275,11 +4458,16 @@ This cannot be undone! 2. Message decryption failed, because you or your contact used old database backup. 3. The connection was compromised. Esto puede suceder cuando: -1. Los mensajes caducan en el cliente saliente tras 2 días o en el servidor tras 30 días. +1. Los mensajes caducan tras 2 días en el cliente saliente o tras 30 días en el servidor. 2. El descifrado ha fallado porque tu o tu contacto estáis usando una copia de seguridad antigua de la base de datos. 3. La conexión ha sido comprometida. No comment provided by engineer. + + It protects your IP address and connections. + Protege tu dirección IP y tus conexiones. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Parece que ya estás conectado mediante este enlace. Si no es así ha habido un error (%@). @@ -3298,7 +4486,7 @@ This cannot be undone! Join Unirte - No comment provided by engineer. + swipe action Join group @@ -3340,6 +4528,11 @@ This is your link for group %@! Keep Guardar + alert action + + + Keep conversation + Conservar conversación No comment provided by engineer. @@ -3350,7 +4543,7 @@ This is your link for group %@! Keep unused invitation? ¿Guardar invitación no usada? - No comment provided by engineer. + alert title Keep your connections @@ -3385,6 +4578,16 @@ This is your link for group %@! Leave Salir + swipe action + + + Leave chat + Salir del chat + No comment provided by engineer. + + + Leave chat? + ¿Salir del chat? No comment provided by engineer. @@ -3427,6 +4630,21 @@ This is your link for group %@! Ordenadores enlazados No comment provided by engineer. + + List + Lista + swipe action + + + List name and emoji should be different for all lists. + El nombre y el emoji deben ser diferentes en todas las listas. + No comment provided by engineer. + + + List name... + Nombre de la lista... + No comment provided by engineer. + Live message! ¡Mensaje en vivo! @@ -3437,11 +4655,6 @@ This is your link for group %@! Mensajes en vivo No comment provided by engineer. - - Local - Local - No comment provided by engineer. - Local name Nombre local @@ -3449,7 +4662,7 @@ This is your link for group %@! Local profile data only - Sólo datos del perfil local + Eliminar sólo el perfil No comment provided by engineer. @@ -3462,11 +4675,6 @@ This is your link for group %@! Modo bloqueo No comment provided by engineer. - - Make a private connection - Establecer una conexión privada - No comment provided by engineer. - Make one message disappear Escribir un mensaje temporal @@ -3477,21 +4685,11 @@ This is your link for group %@! ¡Hacer perfil privado! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Asegúrate de que las direcciones del servidor %@ tienen el formato correcto, están separadas por líneas y no duplicadas (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Asegúrate de que las direcciones del servidor WebRTC ICE tienen el formato correcto, están separadas por líneas y no duplicadas. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Muchos se preguntarán: *si SimpleX no tiene identificadores de usuario, ¿cómo puede entregar los mensajes?* - No comment provided by engineer. - Mark deleted for everyone Marcar como eliminado para todos @@ -3517,11 +4715,36 @@ This is your link for group %@! Máximo 30 segundos, recibido al instante. No comment provided by engineer. + + Media & file servers + Servidores de archivos y multimedia + No comment provided by engineer. + + + Medium + Medio + blur media + Member Miembro No comment provided by engineer. + + Member inactive + Miembro inactivo + item status text + + + Member reports + Informes de miembros + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + El rol del miembro cambiará a "%@" y todos serán notificados. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. El rol del miembro cambiará a "%@" y se notificará al grupo. @@ -3532,9 +4755,64 @@ This is your link for group %@! El rol del miembro cambiará a "%@" y recibirá una invitación nueva. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + El miembro será eliminado del chat. ¡No puede deshacerse! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! - El miembro será expulsado del grupo. ¡No podrá deshacerse! + El miembro será expulsado del grupo. ¡No puede deshacerse! + No comment provided by engineer. + + + Members can add message reactions. + Los miembros pueden añadir reacciones a los mensajes. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + Los miembros del grupo pueden eliminar mensajes de forma irreversible. (24 horas) + No comment provided by engineer. + + + Members can report messsages to moderators. + Los miembros pueden informar de mensajes a los moderadores. + No comment provided by engineer. + + + Members can send SimpleX links. + Los miembros del grupo pueden enviar enlaces SimpleX. + No comment provided by engineer. + + + Members can send direct messages. + Los miembros del grupo pueden enviar mensajes directos. + No comment provided by engineer. + + + Members can send disappearing messages. + Los miembros del grupo pueden enviar mensajes temporales. + No comment provided by engineer. + + + Members can send files and media. + Los miembros del grupo pueden enviar archivos y multimedia. + No comment provided by engineer. + + + Members can send voice messages. + Los miembros del grupo pueden enviar mensajes de voz. + No comment provided by engineer. + + + Mention members 👋 + Menciona a miembros 👋 + No comment provided by engineer. + + + Menus + Menus No comment provided by engineer. @@ -3547,11 +4825,31 @@ This is your link for group %@! ¡Confirmación de entrega de mensajes! No comment provided by engineer. + + Message delivery warning + Aviso de entrega de mensaje + item status text + Message draft Borrador de mensaje No comment provided by engineer. + + Message forwarded + Mensaje reenviado + item status text + + + Message may be delivered later if member becomes active. + El mensaje podría ser entregado más tarde si el miembro vuelve a estar activo. + item status description + + + Message queue info + Información cola de mensajes + No comment provided by engineer. + Message reactions Reacciones a mensajes @@ -3562,11 +4860,41 @@ This is your link for group %@! Las reacciones a los mensajes no están permitidas en este chat. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. Las reacciones a los mensajes no están permitidas en este grupo. No comment provided by engineer. + + Message reception + Recepción de mensaje + No comment provided by engineer. + + + Message servers + Servidores de mensajes + No comment provided by engineer. + + + Message shape + Forma del mensaje + No comment provided by engineer. + + + Message source remains private. + El autor del mensaje se mantiene privado. + No comment provided by engineer. + + + Message status + Estado del mensaje + No comment provided by engineer. + + + Message status: %@ + Estado del mensaje: %@ + copied message info + Message text Contacto y texto @@ -3574,6 +4902,7 @@ This is your link for group %@! Message too large + Mensaje demasiado largo No comment provided by engineer. @@ -3591,36 +4920,64 @@ This is your link for group %@! ¡Los mensajes de %@ serán mostrados! No comment provided by engineer. + + Messages in this chat will never be deleted. + Los mensajes de esta conversación nunca se eliminan. + alert message + + + Messages received + Mensajes recibidos + No comment provided by engineer. + + + Messages sent + Mensajes enviados + No comment provided by engineer. + + + Messages were deleted after you selected them. + Los mensajes han sido eliminados después de seleccionarlos. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + Los mensajes, archivos y llamadas están protegidos mediante **cifrado de extremo a extremo** con secreto perfecto hacía adelante, repudio y recuperación tras ataque. No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + Los mensajes, archivos y llamadas están protegidos mediante **cifrado de extremo a extremo resistente a tecnología cuántica** con secreto perfecto hacía adelante, repudio y recuperación tras ataque. No comment provided by engineer. Migrate device + Migrar dispositivo No comment provided by engineer. Migrate from another device + Migrar desde otro dispositivo No comment provided by engineer. Migrate here + Migrar aquí No comment provided by engineer. Migrate to another device + Migrar a otro dispositivo No comment provided by engineer. Migrate to another device via QR code. + Migrar a otro dispositivo mediante código QR. No comment provided by engineer. Migrating + Migrando No comment provided by engineer. @@ -3630,6 +4987,7 @@ This is your link for group %@! Migration complete + Migración completada No comment provided by engineer. @@ -3647,9 +5005,9 @@ This is your link for group %@! Migración completada No comment provided by engineer. - - Migrations: %@ - Migraciones: %@ + + Migrations: + Migraciones: No comment provided by engineer. @@ -3667,21 +5025,31 @@ This is your link for group %@! Moderado: %@ copied message info + + More + Más + swipe action + More improvements are coming soon! ¡Pronto habrá más mejoras! No comment provided by engineer. + + More reliable network connection. + Conexión de red más fiable. + No comment provided by engineer. + + + More reliable notifications + Notificaciones más fiables + No comment provided by engineer. + Most likely this connection is deleted. Probablemente la conexión ha sido eliminada. item status description - - Most likely this contact has deleted the connection with you. - Lo más probable es que este contacto haya eliminado la conexión contigo. - No comment provided by engineer. - Multiple chat profiles Múltiples perfiles @@ -3690,7 +5058,12 @@ This is your link for group %@! Mute Silenciar - No comment provided by engineer. + notification label action + + + Mute all + Silenciar todo + notification label action Muted when inactive! @@ -3700,13 +5073,38 @@ This is your link for group %@! Name Nombre - No comment provided by engineer. + swipe action Network & servers Servidores y Redes No comment provided by engineer. + + Network connection + Conexión de red + No comment provided by engineer. + + + Network decentralization + Descentralización de la red + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + Problema en la red - el mensaje ha expirado tras muchos intentos de envío. + snd error text + + + Network management + Gestión de la red + No comment provided by engineer. + + + Network operator + Operador de red + No comment provided by engineer. + Network settings Configuración de red @@ -3717,16 +5115,36 @@ This is your link for group %@! Estado de la red No comment provided by engineer. + + New + Nuevo + token status text + New Passcode Código Nuevo No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Se usarán credenciales SOCKS nuevas cada vez que inicies la aplicación. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Se usarán credenciales SOCKS nuevas para cada servidor. + No comment provided by engineer. + New chat Nuevo chat No comment provided by engineer. + + New chat experience 🎉 + Nueva experiencia de chat 🎉 + No comment provided by engineer. + New contact request Nueva solicitud de contacto @@ -3737,11 +5155,6 @@ This is your link for group %@! Contacto nuevo: notification - - New database archive - Nuevo archivo de bases de datos - No comment provided by engineer. - New desktop app! Nueva aplicación para PC! @@ -3752,11 +5165,21 @@ This is your link for group %@! Nuevo nombre mostrado No comment provided by engineer. + + New events + Eventos nuevos + notification + New in %@ Nuevo en %@ No comment provided by engineer. + + New media options + Nuevas opciones multimedia + No comment provided by engineer. + New member role Nuevo rol de miembro @@ -3772,6 +5195,11 @@ This is your link for group %@! Contraseña nueva… No comment provided by engineer. + + New server + Servidor nuevo + No comment provided by engineer. + No No @@ -3782,6 +5210,21 @@ This is your link for group %@! Sin contraseña de la aplicación Authentication unavailable + + No chats + Sin chats + No comment provided by engineer. + + + No chats found + Ningún chat encontrado + No comment provided by engineer. + + + No chats in list %@ + Sin chats en la lista %@ + No comment provided by engineer. + No contacts selected Ningún contacto seleccionado @@ -3802,6 +5245,11 @@ This is your link for group %@! ¡Sin dispositivo token! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + Aún no hay conexión directa con este miembro, el mensaje es reenviado por el administrador. + item status description + No filtered chats Sin chats filtrados @@ -3817,21 +5265,111 @@ This is your link for group %@! Sin historial No comment provided by engineer. + + No info, try to reload + No hay información, intenta recargar + No comment provided by engineer. + + + No media & file servers. + Sin servidores para archivos y multimedia. + servers error + + + No message + Ningún mensaje + No comment provided by engineer. + + + No message servers. + Sin servidores para mensajes. + servers error + + + No network connection + Sin conexión de red + No comment provided by engineer. + + + No permission to record speech + Sin permiso para grabación de voz + No comment provided by engineer. + + + No permission to record video + Sin permiso para grabación de vídeo + No comment provided by engineer. + No permission to record voice message Sin permiso para grabar mensajes de voz No comment provided by engineer. + + No push server + Sin servidores push + No comment provided by engineer. + No received or sent files Sin archivos recibidos o enviados No comment provided by engineer. + + No servers for private message routing. + Sin servidores para enrutamiento privado. + servers error + + + No servers to receive files. + Sin servidores para recibir archivos. + servers error + + + No servers to receive messages. + Sin servidores para recibir mensajes. + servers error + + + No servers to send files. + Sin servidores para enviar archivos. + servers error + + + No token! + ¡Sin token! + alert title + + + No unread chats + Ningún chat sin leer + No comment provided by engineer. + + + No user identifiers. + Sin identificadores de usuario. + No comment provided by engineer. + Not compatible! ¡No compatible! No comment provided by engineer. + + Notes + Notas + No comment provided by engineer. + + + Nothing selected + Nada seleccionado + No comment provided by engineer. + + + Nothing to forward! + ¡Nada para reenviar! + alert title + Notifications Notificaciones @@ -3842,6 +5380,21 @@ This is your link for group %@! ¡Las notificaciones están desactivadas! No comment provided by engineer. + + Notifications error + Error en notificaciones + alert title + + + Notifications privacy + Privacidad en las notificaciones + No comment provided by engineer. + + + Notifications status + Estado notificaciones + alert title + Now admins can: - delete members' messages. @@ -3859,36 +5412,35 @@ This is your link for group %@! Off Desactivado - No comment provided by engineer. + blur media Ok Ok - No comment provided by engineer. + alert button Old database Base de datos antigua No comment provided by engineer. - - Old database archive - Archivo de bases de datos antiguas - No comment provided by engineer. - One-time invitation link - Enlace único de invitación de un uso + Enlace de invitación de un solo uso No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Se requieren hosts .onion para la conexión. Requiere activación de la VPN. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Se **requieren** hosts .onion para la conexión. +Requiere activación de la VPN. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Se usarán hosts .onion si están disponibles. Requiere activación de la VPN. + + Onion hosts will be used when available. +Requires compatible VPN. + Se usarán hosts .onion si están disponibles. +Requiere activación de la VPN. No comment provided by engineer. @@ -3896,11 +5448,21 @@ This is your link for group %@! No se usarán hosts .onion. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + Sólo los propietarios del chat pueden cambiar las preferencias. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. Sólo los dispositivos cliente almacenan perfiles de usuario, contactos, grupos y mensajes enviados con **cifrado de extremo a extremo de 2 capas**. No comment provided by engineer. + + Only delete conversation + Eliminar sólo la conversación + No comment provided by engineer. + Only group owners can change group preferences. Sólo los propietarios pueden modificar las preferencias del grupo. @@ -3916,6 +5478,16 @@ This is your link for group %@! Sólo los propietarios del grupo pueden activar los mensajes de voz. No comment provided by engineer. + + Only sender and moderators see it + Solo el remitente y el moderador pueden verlo + No comment provided by engineer. + + + Only you and moderators see it + Solo tú y los moderadores podéis verlo + No comment provided by engineer. + Only you can add message reactions. Sólo tú puedes añadir reacciones a los mensajes. @@ -3969,13 +5541,18 @@ This is your link for group %@! Open Abrir - No comment provided by engineer. + alert action Open Settings Abrir Configuración No comment provided by engineer. + + Open changes + Abrir cambios + No comment provided by engineer. + Open chat Abrir chat @@ -3986,48 +5563,87 @@ This is your link for group %@! Abrir consola de Chat authentication reason + + Open conditions + Abrir condiciones + No comment provided by engineer. + Open group Grupo abierto No comment provided by engineer. + + Open link? + alert title + Open migration to another device + Abrir menú migración a otro dispositivo authentication reason - - Open user profiles - Abrir perfil de usuario - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Protocolo y código abiertos: cualquiera puede usar los servidores. - No comment provided by engineer. - Opening app… Iniciando aplicación… No comment provided by engineer. + + Operator + Operador + No comment provided by engineer. + + + Operator server + Servidor del operador + alert title + + + Or import archive file + O importa desde un archivo + No comment provided by engineer. + Or paste archive link + O pegar enlace del archivo No comment provided by engineer. Or scan QR code - O escanear código QR + O escanea el código QR No comment provided by engineer. Or securely share this file link + O comparte de forma segura este enlace al archivo No comment provided by engineer. Or show this code - O mostrar este código + O muestra el código QR No comment provided by engineer. + + Or to share privately + O para compartir en privado + No comment provided by engineer. + + + Organize chats into lists + Organiza tus chats en listas + No comment provided by engineer. + + + Other + Otro + No comment provided by engineer. + + + Other file errors: +%@ + Otro(s) error(es) de archivo. +%@ + alert message + PING count Contador PING @@ -4063,6 +5679,11 @@ This is your link for group %@! ¡Código de acceso guardado! No comment provided by engineer. + + Password + Contraseña + No comment provided by engineer. + Password to show Contraseña para hacerlo visible @@ -4090,17 +5711,17 @@ This is your link for group %@! Paste the link you received - Pegar el enlace recibido + Pega el enlace recibido No comment provided by engineer. - - People can connect to you only via the links you share. - Las personas pueden conectarse contigo solo mediante los enlaces que compartes. + + Pending + Pendientes No comment provided by engineer. - - Periodically - Periódico + + Periodic + Periódicamente No comment provided by engineer. @@ -4110,6 +5731,17 @@ This is your link for group %@! Picture-in-picture calls + Llamadas picture-in-picture + No comment provided by engineer. + + + Play from the chat list. + Reproduce desde la lista de chats. + No comment provided by engineer. + + + Please ask your contact to enable calls. + Por favor, pide a tu contacto que active las llamadas. No comment provided by engineer. @@ -4117,6 +5749,13 @@ This is your link for group %@! Solicita que tu contacto habilite el envío de mensajes de voz. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + Comprueba que el móvil y el ordenador están conectados a la misma red local y que el cortafuegos del ordenador permite la conexión. +Por favor, comparte cualquier otro problema con los desarrolladores. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Comprueba que has usado el enlace correcto o pide a tu contacto que te envíe otro. @@ -4134,6 +5773,7 @@ This is your link for group %@! Please confirm that network settings are correct for this device. + Por favor, confirma que la configuración de red es correcta para este dispositivo. No comment provided by engineer. @@ -4183,33 +5823,49 @@ Error: %@ Guarda la contraseña de forma segura, NO podrás cambiarla si la pierdes. No comment provided by engineer. + + Please try to disable and re-enable notfications. + Por favor, intenta desactivar y reactivar las notificaciones. + token info + + + Please wait for token activation to complete. + Por favor, espera a que el token de activación se complete. + token info + + + Please wait for token to be registered. + Por favor, espera a que el token se registre. + token info + Polish interface Interfaz en polaco No comment provided by engineer. + + Port + Puerto + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect - Posiblemente la huella digital del certificado en la dirección del servidor es incorrecta + Posiblemente la huella del certificado en la dirección del servidor es incorrecta server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Conserva el último borrador del mensaje con los datos adjuntos. No comment provided by engineer. - - Preset server - Servidor predefinido - No comment provided by engineer. - Preset server address - Dirección del servidor predefinida + Dirección predefinida del servidor + No comment provided by engineer. + + + Preset servers + Servidores predefinidos No comment provided by engineer. @@ -4217,9 +5873,24 @@ Error: %@ Vista previa No comment provided by engineer. + + Previously connected servers + Servidores conectados previamente + No comment provided by engineer. + Privacy & security - Privacidad y Seguridad + Seguridad y Privacidad + No comment provided by engineer. + + + Privacy for your customers. + Privacidad para tus clientes. + No comment provided by engineer. + + + Privacy policy and conditions of use. + Política de privacidad y condiciones de uso. No comment provided by engineer. @@ -4227,19 +5898,49 @@ Error: %@ Privacidad redefinida No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + Los chats privados, los grupos y tus contactos no son accesibles para los operadores de servidores. + No comment provided by engineer. + Private filenames Nombres de archivos privados No comment provided by engineer. + + Private media file names. + Nombres privados en archivos de media. + No comment provided by engineer. + + + Private message routing + Enrutamiento privado de mensajes + No comment provided by engineer. + + + Private message routing 🚀 + Enrutamiento privado de mensajes 🚀 + No comment provided by engineer. + Private notes Notas privadas name of notes to self + + Private routing + Enrutamiento privado + No comment provided by engineer. + + + Private routing error + Error de enrutamiento privado + No comment provided by engineer. + Profile and server connections - Perfil y conexiones de servidor + Eliminar perfil y conexiones No comment provided by engineer. @@ -4247,14 +5948,9 @@ Error: %@ Imagen del perfil No comment provided by engineer. - - Profile name - Nombre del perfil - No comment provided by engineer. - - - Profile name: - Nombre del perfil: + + Profile images + Forma de los perfiles No comment provided by engineer. @@ -4262,10 +5958,15 @@ Error: %@ Contraseña del perfil No comment provided by engineer. + + Profile theme + Tema del perfil + No comment provided by engineer. + Profile update will be sent to your contacts. La actualización del perfil se enviará a tus contactos. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4287,6 +5988,16 @@ Error: %@ No se permiten reacciones a los mensajes. No comment provided by engineer. + + Prohibit reporting messages to moderators. + No se permite informar de mensajes a los moderadores. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + No se permite enviar enlaces SimpleX. + No comment provided by engineer. + Prohibit sending direct messages to members. No se permiten mensajes directos entre miembros. @@ -4307,9 +6018,21 @@ Error: %@ No se permiten mensajes de voz. No comment provided by engineer. + + Protect IP address + Proteger dirección IP + No comment provided by engineer. + Protect app screen - Proteger la pantalla de la aplicación + Proteger la pantalla + No comment provided by engineer. + + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + Protege tu dirección IP de los servidores de retransmisión elegidos por tus contactos. +Actívalo en ajustes de *Servidores y Redes*. No comment provided by engineer. @@ -4319,25 +6042,42 @@ Error: %@ Protocol timeout - Tiempo de espera del protocolo + Timeout protocolo No comment provided by engineer. Protocol timeout per KB - Límite de espera del protocolo por KB + Timeout protocolo por KB + No comment provided by engineer. + + + Proxied + Como proxy + No comment provided by engineer. + + + Proxied servers + Servidores con proxy + No comment provided by engineer. + + + Proxy requires password + El proxy requiere contraseña No comment provided by engineer. Push notifications - Notificaciones automáticas + Notificaciones push No comment provided by engineer. Push server + Servidor push No comment provided by engineer. Quantum resistant encryption + Cifrado resistente a tecnología cuántica No comment provided by engineer. @@ -4345,6 +6085,11 @@ Error: %@ Valora la aplicación No comment provided by engineer. + + Reachable chat toolbar + Barra de menú accesible + No comment provided by engineer. + React… Reacciona… @@ -4353,36 +6098,31 @@ Error: %@ Read Leer - No comment provided by engineer. + swipe action Read more Saber más No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Saber más en el [Manual del Usuario](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). - Saber más en [Guía de Usuario](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). + Conoce más en la [Guía del Usuario](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). + No comment provided by engineer. + + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Conoce más en el [Manual del Usuario](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). No comment provided by engineer. Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). - Saber más en el [Manual del Usuario](https://simplex.chat/docs/guide/readme.html#connect-to-friends). - No comment provided by engineer. - - - Read more in our GitHub repository. - Saber más en nuestro repositorio GitHub. + Conoce más en el [Manual del Usuario](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). - Saber más en nuestro [repositorio GitHub](https://github.com/simplex-chat/simplex-chat#readme). + Conoce más en nuestro [repositorio GitHub](https://github.com/simplex-chat/simplex-chat#readme). No comment provided by engineer. @@ -4390,6 +6130,11 @@ Error: %@ Las confirmaciones están desactivadas No comment provided by engineer. + + Receive errors + Errores de recepción + No comment provided by engineer. + Received at Recibido a las @@ -4410,6 +6155,21 @@ Error: %@ Mensaje entrante message info title + + Received messages + Mensajes recibidos + No comment provided by engineer. + + + Received reply + Respuesta recibida + No comment provided by engineer. + + + Received total + Total recibidos + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. La dirección de recepción pasará a otro servidor. El cambio se completará cuando el remitente esté en línea. @@ -4430,16 +6190,46 @@ Error: %@ Historial reciente y [bot del directorio](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) mejorados. No comment provided by engineer. + + Recipient(s) can't see who this message is from. + Los destinatarios no ven de quién procede este mensaje. + No comment provided by engineer. + Recipients see updates as you type them. Los destinatarios ven actualizarse mientras escribes. No comment provided by engineer. + + Reconnect + Reconectar + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Reconectar todos los servidores conectados para forzar la entrega del mensaje. Se usa tráfico adicional. No comment provided by engineer. + + Reconnect all servers + Reconectar todos los servidores + No comment provided by engineer. + + + Reconnect all servers? + ¿Reconectar todos los servidores? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + Reconectar con el servidor para forzar la entrega de mensajes. Se usa tráfico adicional. + No comment provided by engineer. + + + Reconnect server? + ¿Reconectar servidor? + No comment provided by engineer. + Reconnect servers? ¿Reconectar servidores? @@ -4460,10 +6250,26 @@ Error: %@ Reducción del uso de batería No comment provided by engineer. + + Register + Registrar + No comment provided by engineer. + + + Register notification token? + ¿Registrar el token de notificaciones? + token info + + + Registered + Registrado + token status text + Reject Rechazar - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4477,7 +6283,7 @@ Error: %@ Relay server is only used if necessary. Another party can observe your IP address. - El retransmisor sólo se usa en caso de necesidad. Un tercero podría ver tu IP. + El servidor de retransmisión sólo se usa en caso de necesidad. Un tercero podría ver tu IP. No comment provided by engineer. @@ -4490,6 +6296,16 @@ Error: %@ Eliminar No comment provided by engineer. + + Remove archive? + ¿Eliminar archivo? + No comment provided by engineer. + + + Remove image + Eliminar imagen + No comment provided by engineer. + Remove member Expulsar miembro @@ -4527,10 +6343,12 @@ Error: %@ Repeat download + Repetir descarga No comment provided by engineer. Repeat import + Repetir importación No comment provided by engineer. @@ -4540,6 +6358,7 @@ Error: %@ Repeat upload + Repetir subida No comment provided by engineer. @@ -4547,6 +6366,56 @@ Error: %@ Responder chat item action + + Report + Informe + chat item action + + + Report content: only group moderators will see it. + Informar de contenido: sólo los moderadores del grupo lo verán. + report reason + + + Report member profile: only group moderators will see it. + Informar del perfil de un miembro: sólo los moderadores del grupo lo verán. + report reason + + + Report other: only group moderators will see it. + Informar de otros: sólo los moderadores del grupo lo verán. + report reason + + + Report reason? + ¿Motivo del informe? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + Informar de spam: sólo los moderadores del grupo lo verán. + report reason + + + Report violation: only group moderators will see it. + Informar de violación: sólo los moderadores del grupo lo verán. + report reason + + + Report: %@ + Informe: %@ + report in notification + + + Reporting messages to moderators is prohibited. + No se permite informar de mensajes a los moderadores. + No comment provided by engineer. + + + Reports + Informes + No comment provided by engineer. + Required Obligatorio @@ -4557,14 +6426,39 @@ Error: %@ Restablecer No comment provided by engineer. + + Reset all hints + Reiniciar todas las pistas + No comment provided by engineer. + + + Reset all statistics + Restablecer todas las estadísticas + No comment provided by engineer. + + + Reset all statistics? + ¿Restablecer todas las estadísticas? + No comment provided by engineer. + Reset colors Restablecer colores No comment provided by engineer. + + Reset to app theme + Restablecer al tema de la aplicación + No comment provided by engineer. + Reset to defaults - Restablecer valores por defecto + Restablecer valores predetarminados + No comment provided by engineer. + + + Reset to user theme + Restablecer al tema del usuario No comment provided by engineer. @@ -4607,9 +6501,9 @@ Error: %@ Revelar chat item action - - Revert - Revertir + + Review conditions + Revisar condiciones No comment provided by engineer. @@ -4634,58 +6528,70 @@ Error: %@ Run chat - Ejecutar chat + Ejecutar SimpleX No comment provided by engineer. - - SMP servers - Servidores SMP + + SMP server + Servidor SMP + No comment provided by engineer. + + + SOCKS proxy + Proxy SOCKS + No comment provided by engineer. + + + Safely receive files + Recibe archivos de forma segura No comment provided by engineer. Safer groups + Grupos más seguros No comment provided by engineer. Save Guardar - chat item action + alert button +chat item action Save (and notify contacts) Guardar (y notificar contactos) - No comment provided by engineer. + alert button Save and notify contact Guardar y notificar contacto - No comment provided by engineer. + alert button Save and notify group members Guardar y notificar grupo No comment provided by engineer. + + Save and reconnect + Guardar y reconectar + No comment provided by engineer. + Save and update group profile Guardar y actualizar perfil del grupo No comment provided by engineer. - - Save archive - Guardar archivo - No comment provided by engineer. - - - Save auto-accept settings - Guardar configuración de auto aceptar - No comment provided by engineer. - Save group profile Guardar perfil de grupo No comment provided by engineer. + + Save list + Guardar lista + No comment provided by engineer. + Save passphrase and open chat Guardar contraseña y abrir el chat @@ -4699,7 +6605,7 @@ Error: %@ Save preferences? ¿Guardar preferencias? - No comment provided by engineer. + alert title Save profile password @@ -4714,28 +6620,53 @@ Error: %@ Save servers? ¿Guardar servidores? - No comment provided by engineer. - - - Save settings? - ¿Guardar configuración? - No comment provided by engineer. + alert title Save welcome message? ¿Guardar mensaje de bienvenida? No comment provided by engineer. + + Save your profile? + ¿Guardar tu perfil? + alert title + + + Saved + Guardado + No comment provided by engineer. + Saved WebRTC ICE servers will be removed Los servidores WebRTC ICE guardados serán eliminados No comment provided by engineer. + + Saved from + Guardado desde + No comment provided by engineer. + Saved message Mensaje guardado message info title + + Saving %lld messages + Guardando %lld mensajes + No comment provided by engineer. + + + Scale + Escala + No comment provided by engineer. + + + Scan / Paste link + Escanear / Pegar enlace + No comment provided by engineer. + Scan QR code Escanear código QR @@ -4758,7 +6689,7 @@ Error: %@ Scan server QR code - Escanear código QR del servidor + Escanear código QR No comment provided by engineer. @@ -4776,11 +6707,21 @@ Error: %@ Buscar o pegar enlace SimpleX No comment provided by engineer. + + Secondary + Secundario + No comment provided by engineer. + Secure queue Cola segura server test step + + Secured + Aseguradas + No comment provided by engineer. + Security assessment Evaluación de la seguridad @@ -4794,6 +6735,21 @@ Error: %@ Select Seleccionar + chat item action + + + Select chat profile + Selecciona perfil de chat + No comment provided by engineer. + + + Selected %lld + Seleccionados %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Las preferencias seleccionadas no permiten este mensaje. No comment provided by engineer. @@ -4823,7 +6779,7 @@ Error: %@ Send a live message - it will update for the recipient(s) as you type it - Envía un mensaje en vivo: se actualizará para el(los) destinatario(s) a medida que se escribe + Envía un mensaje en vivo: se actualizará para el (los) destinatario(s) a medida que se escribe No comment provided by engineer. @@ -4831,14 +6787,9 @@ Error: %@ Enviar confirmaciones de entrega a No comment provided by engineer. - - Send direct message - Enviar mensaje directo - No comment provided by engineer. - Send direct message to connect - Enviar mensaje directo para conectar + Envía un mensaje para conectar No comment provided by engineer. @@ -4846,6 +6797,11 @@ Error: %@ Enviar mensaje temporal No comment provided by engineer. + + Send errors + Errores de envío + No comment provided by engineer. + Send link previews Enviar previsualizacion de enlaces @@ -4856,14 +6812,29 @@ Error: %@ Mensaje en vivo No comment provided by engineer. + + Send message to enable calls. + Enviar mensaje para activar llamadas. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + Enviar mensajes directamente cuando tu dirección IP está protegida y tu servidor o el de destino no admitan enrutamiento privado. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + Enviar mensajes directamente cuando tu servidor o el de destino no admitan enrutamiento privado. + No comment provided by engineer. + Send notifications Enviar notificaciones No comment provided by engineer. - - Send notifications: - Enviar notificaciones: + + Send private reports + Envía informes privados No comment provided by engineer. @@ -4883,13 +6854,13 @@ Error: %@ Send up to 100 last messages to new members. - Enviar hasta 100 últimos mensajes a los miembros nuevos. + Se envían hasta 100 mensajes más recientes a los miembros nuevos. No comment provided by engineer. Sender cancelled file transfer. El remitente ha cancelado la transferencia de archivos. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4946,6 +6917,11 @@ Error: %@ Enviado: %@ copied message info + + Sent directly + Directamente + No comment provided by engineer. + Sent file event Evento de archivo enviado @@ -4956,11 +6932,71 @@ Error: %@ Mensaje saliente message info title + + Sent messages + Mensajes enviados + No comment provided by engineer. + Sent messages will be deleted after set time. Los mensajes enviados se eliminarán una vez transcurrido el tiempo establecido. No comment provided by engineer. + + Sent reply + Respuesta enviada + No comment provided by engineer. + + + Sent total + Total enviados + No comment provided by engineer. + + + Sent via proxy + Mediante proxy + No comment provided by engineer. + + + Server + Servidor + No comment provided by engineer. + + + Server added to operator %@. + Servidor añadido al operador %@. + alert message + + + Server address + Dirección del servidor + No comment provided by engineer. + + + Server address is incompatible with network settings. + La dirección del servidor es incompatible con la configuración de la red. + srv error text. + + + Server address is incompatible with network settings: %@. + La dirección del servidor es incompatible con la configuración de la red: %@. + No comment provided by engineer. + + + Server operator changed. + El operador del servidor ha cambiado. + alert title + + + Server operators + Operadores de servidores + No comment provided by engineer. + + + Server protocol changed. + El protocolo del servidor ha cambiado. + alert title + Server requires authorization to create queues, check password El servidor requiere autorización para crear colas, comprueba la contraseña @@ -4973,7 +7009,22 @@ Error: %@ Server test failed! - ¡Error en prueba del servidor! + ¡Prueba no superada! + No comment provided by engineer. + + + Server type + Tipo de servidor + No comment provided by engineer. + + + Server version is incompatible with network settings. + La versión del servidor es incompatible con la configuración de red. + srv error text + + + Server version is incompatible with your app: %@. + La versión del servidor es incompatible con tu aplicación: %@. No comment provided by engineer. @@ -4981,6 +7032,16 @@ Error: %@ Servidores No comment provided by engineer. + + Servers info + Info servidores + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + Las estadísticas de los servidores serán restablecidas. ¡No puede deshacerse! + No comment provided by engineer. + Session code Código de sesión @@ -4991,11 +7052,21 @@ Error: %@ Establecer 1 día No comment provided by engineer. + + Set chat name… + Nombre para el chat… + No comment provided by engineer. + Set contact name… Escribe el nombre del contacto… No comment provided by engineer. + + Set default theme + Establecer tema predeterminado + No comment provided by engineer. + Set group preferences Establecer preferencias de grupo @@ -5006,6 +7077,11 @@ Error: %@ Úsalo en lugar de la autenticación del sistema. No comment provided by engineer. + + Set message expiration in chats. + Establece el vencimiento para los mensajes en los chats. + No comment provided by engineer. + Set passcode Código autodestrucción @@ -5013,6 +7089,7 @@ Error: %@ Set passphrase + Definir frase de contraseña No comment provided by engineer. @@ -5035,24 +7112,55 @@ Error: %@ Configuración No comment provided by engineer. + + Settings were changed. + La configuración ha sido modificada. + alert message + + + Shape profile images + Dar forma a las imágenes de perfil + No comment provided by engineer. + Share Compartir - chat item action + alert action +chat item action Share 1-time link Compartir enlace de un uso No comment provided by engineer. + + Share 1-time link with a friend + Compartir enlace de un uso con un amigo + No comment provided by engineer. + + + Share SimpleX address on social media. + Comparte tu dirección SimpleX en redes sociales. + No comment provided by engineer. + Share address Compartir dirección No comment provided by engineer. + + Share address publicly + Campartir dirección públicamente + No comment provided by engineer. + Share address with contacts? ¿Compartir la dirección con los contactos? + alert title + + + Share from other apps. + Comparte desde otras aplicaciones. No comment provided by engineer. @@ -5060,9 +7168,19 @@ Error: %@ Compartir enlace No comment provided by engineer. + + Share profile + Perfil a compartir + No comment provided by engineer. + Share this 1-time invite link - Compartir este enlace de un uso + Comparte este enlace de un solo uso + No comment provided by engineer. + + + Share to SimpleX + Compartir con Simplex No comment provided by engineer. @@ -5070,8 +7188,14 @@ Error: %@ Compartir con contactos No comment provided by engineer. + + Short link + Enlace corto + No comment provided by engineer. + Show QR code + Mostrar código QR No comment provided by engineer. @@ -5089,21 +7213,46 @@ Error: %@ Mostrar último mensaje No comment provided by engineer. + + Show message status + Estado del mensaje + No comment provided by engineer. + + + Show percentage + Mostrar porcentajes + No comment provided by engineer. + Show preview Mostrar vista previa No comment provided by engineer. + + Show → on messages sent via private routing. + Mostrar → en mensajes con enrutamiento privado. + No comment provided by engineer. + Show: Mostrar: No comment provided by engineer. + + SimpleX + SimpleX + No comment provided by engineer. + SimpleX Address Dirección SimpleX No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + Simplex Chat y Flux han acordado incluir en la aplicación servidores operados por Flux. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. La seguridad de SimpleX Chat ha sido auditada por Trail of Bits. @@ -5134,6 +7283,21 @@ Error: %@ Dirección SimpleX No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + Compartir enlaces de un solo uso y direcciones SimpleX es seguro a través de cualquier medio. + No comment provided by engineer. + + + SimpleX address or 1-time link? + ¿Dirección SimpleX o enlace de un uso? + No comment provided by engineer. + + + SimpleX channel link + Enlace de canal SimpleX + simplex link type + SimpleX contact address Dirección de contacto SimpleX @@ -5152,6 +7316,16 @@ Error: %@ SimpleX links Enlaces SimpleX + chat feature + + + SimpleX links are prohibited. + Los enlaces SimpleX no se permiten en este grupo. + No comment provided by engineer. + + + SimpleX links not allowed + Enlaces SimpleX no permitidos No comment provided by engineer. @@ -5159,11 +7333,21 @@ Error: %@ Invitación SimpleX de un uso simplex link type + + SimpleX protocols reviewed by Trail of Bits. + Protocolos de SimpleX auditados por Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Modo incógnito simplificado No comment provided by engineer. + + Size + Tamaño + No comment provided by engineer. + Skip Omitir @@ -5176,7 +7360,22 @@ Error: %@ Small groups (max 20) - Grupos pequeños (máx. 20) + Grupos pequeños (max. 20) + No comment provided by engineer. + + + Soft + Suave + blur media + + + Some app settings were not migrated. + Algunas configuraciones de la app no han sido migradas. + No comment provided by engineer. + + + Some file(s) were not exported: + Algunos archivos no han sido exportados: No comment provided by engineer. @@ -5184,11 +7383,34 @@ Error: %@ Algunos errores no críticos ocurrieron durante la importación - para más detalles puedes ver la consola de Chat. No comment provided by engineer. + + Some non-fatal errors occurred during import: + Han ocurrido algunos errores no críticos durante la importación: + No comment provided by engineer. + + + Some servers failed the test: +%@ + Algunos servidores no han superado la prueba: +%@ + alert message + Somebody Alguien notification title + + Spam + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + Cuadrada, circular o cualquier forma intermedia. + No comment provided by engineer. + Start chat Iniciar chat @@ -5204,33 +7426,39 @@ Error: %@ Iniciar migración No comment provided by engineer. + + Starting from %@. + Iniciado el %@. + No comment provided by engineer. + + + Statistics + Estadísticas + No comment provided by engineer. + Stop - Detener + Parar No comment provided by engineer. Stop SimpleX - Detener SimpleX + Parar SimpleX authentication reason Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - Detén SimpleX para habilitar las acciones sobre la base de datos + Parar SimpleX No comment provided by engineer. Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. - Para poder exportar, importar o eliminar la base de datos primero debes detener Chat. Durante el tiempo que esté detenido no podrás recibir ni enviar mensajes. + Para exportar, importar o eliminar la base de datos debes parar SimpleX. Mientra tanto no podrás enviar ni recibir mensajes. No comment provided by engineer. Stop chat? - ¿Detener Chat? + ¿Parar Chat? No comment provided by engineer. @@ -5251,27 +7479,63 @@ Error: %@ Stop sharing Dejar de compartir - No comment provided by engineer. + alert action Stop sharing address? ¿Dejar de compartir la dirección? - No comment provided by engineer. + alert title Stopping chat + Parando chat No comment provided by engineer. + + Storage + Almacenamiento + No comment provided by engineer. + + + Strong + Fuerte + blur media + Submit Enviar No comment provided by engineer. + + Subscribed + Suscritas + No comment provided by engineer. + + + Subscription errors + Errores de suscripción + No comment provided by engineer. + + + Subscriptions ignored + Suscripciones ignoradas + No comment provided by engineer. + Support SimpleX Chat Soporte SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Intercambia audio y video durante la llamada. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Cambia el perfil de chat para invitaciones de un solo uso. + No comment provided by engineer. + System Sistema @@ -5282,9 +7546,19 @@ Error: %@ Autenticación del sistema No comment provided by engineer. + + TCP connection + Conexión TCP + No comment provided by engineer. + TCP connection timeout - Tiempo de espera de la conexión TCP agotado + Timeout de la conexión TCP + No comment provided by engineer. + + + TCP port for messaging + Puerto TCP para mensajes No comment provided by engineer. @@ -5302,11 +7576,21 @@ Error: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + Cola + No comment provided by engineer. + Take picture Tomar foto No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + Pulsa Crear dirección SimpleX en el menú para crearla más tarde. + No comment provided by engineer. + Tap button Pulsa el botón @@ -5334,7 +7618,7 @@ Error: %@ Tap to paste link - Pulsa para pegar enlace + Pulsa para pegar el enlacePulsa para pegar enlace No comment provided by engineer. @@ -5342,16 +7626,21 @@ Error: %@ Pulsa para escanear No comment provided by engineer. - - Tap to start a new chat - Pulsa para iniciar chat nuevo - No comment provided by engineer. + + Temporary file error + Error en archivo temporal + file error alert title Test failed at step %@. - La prueba ha fallado en el paso %@. + Prueba no superada en el paso %@. server test failure + + Test notifications + Probar notificaciones + No comment provided by engineer. + Test server Probar servidor @@ -5364,8 +7653,8 @@ Error: %@ Tests failed! - ¡Pruebas fallidas! - No comment provided by engineer. + ¡Pruebas no superadas! + alert title Thank you for installing SimpleX Chat! @@ -5374,17 +7663,12 @@ Error: %@ Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! - Gracias a los usuarios: [contribuye vía Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#traducir-el-aplicaciones)! + ¡Nuestro agradecimiento a todos los colaboradores, [puedes contribuir a través de Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#traducir-el-aplicaciones)! No comment provided by engineer. Thanks to the users – contribute via Weblate! - ¡Gracias a los colaboradores! Contribuye a través de Weblate. - No comment provided by engineer. - - - The 1st platform without any user identifiers – private by design. - La primera plataforma sin identificadores de usuario: diseñada para la privacidad. + ¡Nuestro agradecimiento a todos los colaboradores! Puedes contribuir a través de Weblate No comment provided by engineer. @@ -5396,7 +7680,17 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. The app can notify you when you receive messages or contact requests - please open settings to enable. - La aplicación puede notificarte cuando recibas mensajes o solicitudes de contacto: abre la configuración para habilitar. + La aplicación puede notificarte cuando recibas mensajes o solicitudes de contacto: por favor, abre la configuración para activarlo. + No comment provided by engineer. + + + The app protects your privacy by using different operators in each conversation. + La aplicación protege tu privacidad mediante el uso de diferentes operadores en cada conversación. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + La aplicación pedirá que confirmes las descargas desde servidores de archivos desconocidos (excepto si son .onion). No comment provided by engineer. @@ -5406,7 +7700,12 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. The code you scanned is not a SimpleX link QR code. - El código QR escaneado no es un enlace SimpleX. + El código QR escaneado no es un enlace de SimpleX. + No comment provided by engineer. + + + The connection reached the limit of undelivered messages, your contact may be offline. + La conexión ha alcanzado el límite de mensajes no entregados. es posible que tu contacto esté desconectado. No comment provided by engineer. @@ -5429,6 +7728,11 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. El cifrado funciona y un cifrado nuevo no es necesario. ¡Podría dar lugar a errores de conexión! No comment provided by engineer. + + The future of messaging + La nueva generación de mensajería privada + No comment provided by engineer. + The hash of the previous message is different. El hash del mensaje anterior es diferente. @@ -5444,9 +7748,14 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. El mensaje será marcado como moderado para todos los miembros. No comment provided by engineer. - - The next generation of private messaging - La nueva generación de mensajería privada + + The messages will be deleted for all members. + Los mensajes serán eliminados para todos los miembros. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. + Los mensajes serán marcados como moderados para todos los miembros. No comment provided by engineer. @@ -5454,9 +7763,14 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. La base de datos antigua no se eliminó durante la migración, puede eliminarse. No comment provided by engineer. - - The profile is only shared with your contacts. - El perfil sólo se comparte con tus contactos. + + The same conditions will apply to operator **%@**. + Las mismas condiciones se aplicarán al operador **%@**. + No comment provided by engineer. + + + The second preset operator in the app! + ¡Segundo operador predefinido! No comment provided by engineer. @@ -5471,17 +7785,32 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. The servers for new connections of your current chat profile **%@**. - Lista de servidores para las conexiones nuevas de tu perfil actual **%@**. + Servidores para conexiones nuevas en tu perfil **%@**. + No comment provided by engineer. + + + The servers for new files of your current chat profile **%@**. + Servidores para enviar archivos en tu perfil **%@**. No comment provided by engineer. The text you pasted is not a SimpleX link. - El texto pegado no es un enlace SimpleX. + El texto pegado no es un enlace de SimpleX. No comment provided by engineer. - - Theme - Tema + + The uploaded database archive will be permanently removed from the servers. + El archivo de bases de datos subido será eliminado permanentemente de los servidores. + No comment provided by engineer. + + + Themes + Temas + No comment provided by engineer. + + + These conditions will also apply for: **%@**. + Estas condiciones también se aplican para: **%@**. No comment provided by engineer. @@ -5491,7 +7820,7 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. They can be overridden in contact and group settings. - Se pueden anular en la configuración de contactos. + Se puede modificar desde la configuración particular de cada grupo y contacto. No comment provided by engineer. @@ -5501,9 +7830,14 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes. - Esta acción es irreversible. Se eliminarán los mensajes enviados y recibidos anteriores a la selección. Puede tardar varios minutos. + Esta acción es irreversible. Se eliminarán los mensajes enviados y recibidos anteriores a la selección. Podría tardar varios minutos. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + Todos los mensajes previos al período seleccionado serán eliminados del chat. ¡No puede deshacerse! + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. Esta acción es irreversible. Tu perfil, contactos, mensajes y archivos se perderán irreversiblemente. @@ -5511,10 +7845,12 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. This chat is protected by end-to-end encryption. + Este chat está protegido por cifrado de extremo a extremo. E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + Este chat está protegido por cifrado de extremo a extremo resistente a tecnología cuántica. E2EE info chat item @@ -5547,11 +7883,31 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. ¡Este es tu propio enlace de un solo uso! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + Este enlace requiere una versión más reciente de la aplicación. Por favor, actualiza la aplicación o pide a tu contacto un enlace compatible. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + Este enlace ha sido usado en otro dispositivo móvil, por favor crea un enlace nuevo en el ordenador. + No comment provided by engineer. + + + This message was deleted or not received yet. + El mensaje ha sido eliminado o aún no se ha recibido. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. Esta configuración se aplica a los mensajes del perfil actual **%@**. No comment provided by engineer. + + Title + Título + No comment provided by engineer. + To ask any questions and to receive updates: Para consultar cualquier duda y recibir actualizaciones: @@ -5572,9 +7928,9 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. Para hacer una conexión nueva No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Para proteger tu privacidad, en lugar de los identificadores de usuario que usan el resto de plataformas, SimpleX dispone de identificadores para las colas de mensajes, independientes para cada uno de tus contactos. + + To protect against your link being replaced, you can compare contact security codes. + Para protegerte contra una sustitución del enlace, puedes comparar los códigos de seguridad con tu contacto. No comment provided by engineer. @@ -5582,6 +7938,11 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. Para proteger la zona horaria, los archivos de imagen/voz usan la hora UTC. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + Para proteger tu dirección IP, el enrutamiento privado usa tu lista de servidores SMP para enviar mensajes. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5589,6 +7950,26 @@ You will be prompted to complete authentication before this feature is enabled.< Se te pedirá que completes la autenticación antes de activar esta función. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Para proteger tu privacidad, SimpleX usa identificadores distintos para cada uno de tus contactos. + No comment provided by engineer. + + + To receive + Para recibir + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + Para grabación de voz, por favor concede el permiso para usar el micrófono. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Para grabación de vídeo, por favor concede el permiso para usar la cámara. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Para grabar el mensaje de voz concede permiso para usar el micrófono. @@ -5599,26 +7980,61 @@ Se te pedirá que completes la autenticación antes de activar esta función.
Para hacer visible tu perfil oculto, introduce la contraseña en el campo de búsqueda del menú **Mis perfiles**. No comment provided by engineer. + + To send + Para enviar + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. Para permitir las notificaciones automáticas instantáneas, la base de datos se debe migrar. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + Para usar los servidores de **%@**, debes aceptar las condiciones de uso. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. Para verificar el cifrado de extremo a extremo con tu contacto, compara (o escanea) el código en ambos dispositivos. No comment provided by engineer. + + Toggle chat list: + Alternar lista de chats: + No comment provided by engineer. + Toggle incognito when connecting. Activa incógnito al conectar. No comment provided by engineer. + + Token status: %@. + Estado token: %@. + token status + + + Toolbar opacity + Opacidad barra + No comment provided by engineer. + + + Total + Total + No comment provided by engineer. + Transport isolation Aislamiento de transporte No comment provided by engineer. + + Transport sessions + Sesiones de transporte + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Intentando conectar con el servidor usado para recibir mensajes de este contacto (error: %@). @@ -5666,7 +8082,7 @@ Se te pedirá que completes la autenticación antes de activar esta función.
Unblock member for all? - ¿Desbloquear miembro para todos? + ¿Desbloquear el miembro para todos? No comment provided by engineer. @@ -5674,10 +8090,10 @@ Se te pedirá que completes la autenticación antes de activar esta función.
¿Desbloquear miembro? No comment provided by engineer. - - Unexpected error: %@ - Error inesperado: %@ - item status description + + Undelivered messages + Mensajes no entregados + No comment provided by engineer. Unexpected migration state @@ -5687,7 +8103,7 @@ Se te pedirá que completes la autenticación antes de activar esta función.
Unfav. No fav. - No comment provided by engineer. + swipe action Unhide @@ -5724,6 +8140,11 @@ Se te pedirá que completes la autenticación antes de activar esta función.Error desconocido No comment provided by engineer. + + Unknown servers! + ¡Servidores desconocidos! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. A menos que utilices la interfaz de llamadas de iOS, activa el modo No molestar para evitar interrupciones. @@ -5732,9 +8153,8 @@ Se te pedirá que completes la autenticación antes de activar esta función. Unless your contact deleted the connection or this link was already used, it might be a bug - please report it. To connect, please ask your contact to create another connection link and check that you have a stable network connection. - A menos que tu contacto haya eliminado la conexión o -que este enlace ya se haya usado, podría ser un error. Por favor, notifícalo. -Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueba que tienes buena conexión de red. + A menos que tu contacto haya eliminado la conexión o el enlace se haya usado, podría ser un error. Por favor, notifícalo. +Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión de red. No comment provided by engineer. @@ -5760,11 +8180,16 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Unmute Activar audio - No comment provided by engineer. + notification label action Unread No leído + swipe action + + + Unsupported connection link + Enlace de conexión no compatible No comment provided by engineer. @@ -5777,11 +8202,6 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Actualizar No comment provided by engineer. - - Update .onion hosts setting? - ¿Actualizar la configuración de los hosts .onion? - No comment provided by engineer. - Update database passphrase Actualizar contraseña de la base de datos @@ -5792,19 +8212,19 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb ¿Actualizar la configuración de red? No comment provided by engineer. - - Update transport isolation mode? - ¿Actualizar el modo de aislamiento de transporte? + + Update settings? + ¿Actualizar configuración? + No comment provided by engineer. + + + Updated conditions + Condiciones actualizadas No comment provided by engineer. Updating settings will re-connect the client to all servers. - Al actualizar la configuración el cliente se reconectará a todos los servidores. - No comment provided by engineer. - - - Updating this setting will re-connect the client to all servers. - Al actualizar esta configuración el cliente se reconectará a todos los servidores. + Para actualizar la configuración el cliente se reconectará a todos los servidores. No comment provided by engineer. @@ -5812,8 +8232,14 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Actualizar y abrir Chat No comment provided by engineer. + + Upload errors + Errores en subida + No comment provided by engineer. + Upload failed + Error de subida No comment provided by engineer. @@ -5821,8 +8247,24 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Subir archivo server test step + + Uploaded + Subido + No comment provided by engineer. + + + Uploaded files + Archivos subidos + No comment provided by engineer. + Uploading archive + Subiendo archivo + No comment provided by engineer. + + + Use %@ + Usar %@ No comment provided by engineer. @@ -5830,11 +8272,26 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Usar hosts .onion No comment provided by engineer. + + Use SOCKS proxy + Usar proxy SOCKS + No comment provided by engineer. + Use SimpleX Chat servers? ¿Usar servidores SimpleX Chat? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + Se usa el puerto TCP %@ cuando no se ha especificado otro. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + Usar puerto TCP 443 solo en servidores predefinidos. + No comment provided by engineer. + Use chat Usar Chat @@ -5845,9 +8302,19 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Usar perfil actual No comment provided by engineer. + + Use for files + Uso para archivos + No comment provided by engineer. + + + Use for messages + Uso para mensajes + No comment provided by engineer. + Use for new connections - Usar para conexiones nuevas + Para conexiones nuevas No comment provided by engineer. @@ -5870,23 +8337,54 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb ¿Usar sólo notificaciones locales? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + Usar enrutamiento privado con servidores desconocidos cuando tu dirección IP no está protegida. + No comment provided by engineer. + + + Use private routing with unknown servers. + Usar enrutamiento privado con servidores de mensaje desconocidos. + No comment provided by engineer. + Use server Usar servidor No comment provided by engineer. + + Use servers + Usar servidores + No comment provided by engineer. + + + Use short links (BETA) + Usar enlaces cortos (BETA) + No comment provided by engineer. + Use the app while in the call. + Usar la aplicación durante la llamada. No comment provided by engineer. - - User profile - Perfil de usuario + + Use the app with one hand. + Usa la aplicación con una sola mano. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - Usar hosts .onion requiere un proveedor VPN compatible. + + Use web port + Usar puerto web + No comment provided by engineer. + + + User selection + Selección de usuarios + No comment provided by engineer. + + + Username + Nombre de usuario No comment provided by engineer. @@ -5916,10 +8414,12 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Verify database passphrase + Verificar la contraseña de la base de datos No comment provided by engineer. Verify passphrase + Verificar frase de contraseña No comment provided by engineer. @@ -5944,12 +8444,12 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Video will be received when your contact completes uploading it. - El video se recibirá cuando tu contacto termine de subirlo. + El video se recibirá cuando el contacto termine de subirlo. No comment provided by engineer. Video will be received when your contact is online, please wait or check later! - El vídeo se recibirá cuando tu contacto esté en línea, por favor espera o compruébalo más tarde. + El vídeo se recibirá cuando el contacto esté en línea, por favor espera o revisa más tarde. No comment provided by engineer. @@ -5957,11 +8457,21 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Vídeos y archivos de hasta 1Gb No comment provided by engineer. + + View conditions + Ver condiciones + No comment provided by engineer. + View security code Mostrar código de seguridad No comment provided by engineer. + + View updated conditions + Ver condiciones actualizadas + No comment provided by engineer. + Visible history Historial visible @@ -5977,11 +8487,16 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Los mensajes de voz no están permitidos en este chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. Los mensajes de voz no están permitidos en este grupo. No comment provided by engineer. + + Voice messages not allowed + Mensajes de voz no permitidos + No comment provided by engineer. + Voice messages prohibited! ¡Mensajes de voz no permitidos! @@ -6012,8 +8527,19 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Esperando el vídeo No comment provided by engineer. + + Wallpaper accent + Color imagen de fondo + No comment provided by engineer. + + + Wallpaper background + Color de fondo + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures + Atención: el inicio del chat en varios dispositivos es incompatible y provocará fallos en la entrega de mensajes No comment provided by engineer. @@ -6038,6 +8564,7 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Welcome message is too long + Mensaje de bienvenida demasiado largo No comment provided by engineer. @@ -6050,9 +8577,14 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Si disponibles No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Cuando alguien solicite conectarse podrás aceptar o rechazar la solicitud. + + When connecting audio and video calls. + Al iniciar llamadas de audio y vídeo. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. + Cuando está habilitado más de un operador, ninguno dispone de los metadatos para conocer quién se comunica con quién. No comment provided by engineer. @@ -6060,6 +8592,21 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Cuando compartes un perfil incógnito con alguien, este perfil también se usará para los grupos a los que te inviten. No comment provided by engineer. + + WiFi + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + ¡Será habilitado en los chats directos! + No comment provided by engineer. + + + Wired ethernet + Ethernet por cable + No comment provided by engineer. + With encrypted files and media. Con cifrado de archivos y multimedia. @@ -6075,28 +8622,44 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb Con uso reducido de batería. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + Sin Tor o VPN, tu dirección IP será visible para los servidores de archivos. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + Sin Tor o VPN, tu dirección IP será visible para estos servidores XFTP: %@. + alert message + Wrong database passphrase Contraseña de base de datos incorrecta No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + Clave incorrecta o conexión desconocida - probablemente esta conexión fue eliminada. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + Clave incorrecta o dirección del bloque del archivo desconocida. Es probable que el archivo se haya eliminado. + file error text + Wrong passphrase! ¡Contraseña incorrecta! No comment provided by engineer. - - XFTP servers - Servidores XFTP - No comment provided by engineer. - - - You - + + XFTP server + Servidor XFTP No comment provided by engineer. You **must not** use the same database on two devices. + **No debes** usar la misma base de datos en dos dispositivos. No comment provided by engineer. @@ -6116,7 +8679,12 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb You are already connected to %@. - Ya estás conectado a %@. + Ya estás conectado con %@. + No comment provided by engineer. + + + You are already connected with %@. + Ya estás conectado con %@. No comment provided by engineer. @@ -6166,11 +8734,26 @@ Repeat join request? Has sido invitado a un grupo No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + No tienes conexión directa a estos servidores. Los mensajes destinados a estos usan enrutamiento privado. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. Puede aceptar llamadas desde la pantalla de bloqueo, sin autenticación de dispositivos y aplicaciones. No comment provided by engineer. + + You can change it in Appearance settings. + Puedes cambiar la posición de la barra desde el menú Apariencia. + No comment provided by engineer. + + + You can configure servers via settings. + Puedes configurar los servidores a través de su configuración. + No comment provided by engineer. + You can create it later Puedes crearla más tarde @@ -6188,6 +8771,7 @@ Repeat join request? You can give another try. + Puedes intentarlo de nuevo. No comment provided by engineer. @@ -6200,11 +8784,21 @@ Repeat join request? Puedes hacerlo visible para tus contactos de SimpleX en Configuración. No comment provided by engineer. - - You can now send messages to %@ - Ya puedes enviar mensajes a %@ + + You can now chat with %@ + Ya puedes chatear con %@ notification body + + You can send messages to %@ from Archived contacts. + Puedes enviar mensajes a %@ desde Contactos archivados. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + Puedes añadir un nombre a la conexión para recordar a quién corresponde. + No comment provided by engineer. + You can set lock screen notification preview via settings. Puedes configurar las notificaciones de la pantalla de bloqueo desde Configuración. @@ -6220,16 +8814,16 @@ Repeat join request? Puedes compartir esta dirección con tus contactos para que puedan conectar con **%@**. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - Puedes compartir tu dirección como enlace o código QR para que cualquiera pueda conectarse contigo. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app Puede iniciar Chat a través de la Configuración / Base de datos de la aplicación o reiniciando la aplicación No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + Aún puedes ver la conversación con %@ en la lista de chats. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. Puedes activar el Bloqueo SimpleX a través de Configuración. @@ -6243,23 +8837,23 @@ Repeat join request? You can view invitation link again in connection details. Podrás ver el enlace de invitación en detalles de conexión. - No comment provided by engineer. + alert message You can't send messages! ¡No puedes enviar mensajes! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Tú controlas a través de qué servidor(es) **recibes** los mensajes. Tus contactos controlan a través de qué servidor(es) **envías** tus mensajes. - No comment provided by engineer. - You could not be verified; please try again. No has podido ser autenticado. Inténtalo de nuevo. No comment provided by engineer. + + You decide who can connect. + Tu decides quién se conecta. + No comment provided by engineer. + You have already requested connection via this address! ¡Ya has solicitado la conexión mediante esta dirección! @@ -6272,11 +8866,6 @@ Repeat connection request? ¿Repetir solicitud? No comment provided by engineer. - - You have no chats - No tienes chats - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. La contraseña no se almacena en el dispositivo, tienes que introducirla cada vez que inicies la aplicación. @@ -6297,11 +8886,26 @@ Repeat connection request? Te has unido a este grupo. Conectando con el emisor de la invitacíon. No comment provided by engineer. + + You may migrate the exported database. + Puedes migrar la base de datos exportada. + No comment provided by engineer. + + + You may save the exported archive. + Puedes guardar el archivo exportado. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. Debes usar la versión más reciente de tu base de datos ÚNICAMENTE en un dispositivo, de lo contrario podrías dejar de recibir mensajes de algunos contactos. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + Debes permitir que tus contacto te llamen para poder llamarles. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. Para poder enviar mensajes de voz antes debes permitir que tu contacto pueda enviarlos. @@ -6317,29 +8921,34 @@ Repeat connection request? Has enviado una invitación de grupo No comment provided by engineer. + + You should receive notifications. + Deberías recibir notificaciones. + token info + You will be connected to group when the group host's device is online, please wait or check later! - Te conectarás al grupo cuando el dispositivo del anfitrión esté en línea, por favor espera o compruébalo más tarde. + Te conectarás al grupo cuando el dispositivo del anfitrión esté en línea, por favor espera o revisa más tarde. No comment provided by engineer. You will be connected when group link host's device is online, please wait or check later! - Te conectarás cuando el dispositivo propietario del grupo esté en línea, por favor espera o compruébalo más tarde. + Te conectarás cuando el dispositivo propietario del grupo esté en línea, por favor espera o revisa más tarde. No comment provided by engineer. You will be connected when your connection request is accepted, please wait or check later! - Te conectarás cuando tu solicitud se acepte, por favor espera o compruébalo más tarde. + Te conectarás cuando tu solicitud se acepte, por favor espera o revisa más tarde. No comment provided by engineer. You will be connected when your contact's device is online, please wait or check later! - Te conectarás cuando el dispositivo de tu contacto esté en línea, por favor espera o compruébalo más tarde. + Te conectarás cuando el dispositivo del contacto esté en línea, por favor espera o revisa más tarde. No comment provided by engineer. You will be required to authenticate when you start or resume the app after 30 seconds in background. - Se te pedirá identificarte cuándo inicies o continues usando la aplicación tras 30 segundos en segundo plano. + Se te pedirá autenticarte cuando inicies la aplicación o sigas usándola tras 30 segundos en segundo plano. No comment provided by engineer. @@ -6352,6 +8961,11 @@ Repeat connection request? Seguirás recibiendo llamadas y notificaciones de los perfiles silenciados cuando estén activos. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + Dejarás de recibir mensajes de este chat. El historial del chat se conserva. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Dejarás de recibir mensajes de este grupo. El historial del chat se conservará. @@ -6372,31 +8986,16 @@ Repeat connection request? Estás usando un perfil incógnito en este grupo. Para evitar descubrir tu perfil principal no se permite invitar contactos No comment provided by engineer. - - Your %@ servers - Mis servidores %@ - No comment provided by engineer. - Your ICE servers Servidores ICE No comment provided by engineer. - - Your SMP servers - Servidores SMP - No comment provided by engineer. - Your SimpleX address Mi dirección SimpleX No comment provided by engineer. - - Your XFTP servers - Servidores XFTP - No comment provided by engineer. - Your calls Llamadas @@ -6412,16 +9011,19 @@ Repeat connection request? La base de datos no está cifrada - establece una contraseña para cifrarla. No comment provided by engineer. + + Your chat preferences + Tus preferencias de chat + alert title + Your chat profiles Mis perfiles No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Tu contacto debe estar en línea para que se complete la conexión. -Puedes cancelar esta conexión y eliminar el contacto (e intentarlo más tarde con un enlace nuevo). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Tu conexión ha sido trasladada a %@ pero ha ocurrido un error inesperado al redirigirte al perfil. No comment provided by engineer. @@ -6439,6 +9041,11 @@ Puedes cancelar esta conexión y eliminar el contacto (e intentarlo más tarde c Tus contactos permanecerán conectados. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Tus credenciales podrían ser enviadas sin cifrar. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. La base de datos actual será ELIMINADA y SUSTITUIDA por la importada. @@ -6466,34 +9073,37 @@ Puedes cancelar esta conexión y eliminar el contacto (e intentarlo más tarde c Your profile **%@** will be shared. - Tu perfil **%@** será compartido. + El perfil **%@** será compartido. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Tu perfil se almacena en tu dispositivo y sólo se comparte con tus contactos. -Los servidores de SimpleX no pueden ver tu perfil. + + Your profile is stored on your device and only shared with your contacts. + El perfil sólo se comparte con tus contactos. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Tu perfil, contactos y mensajes se almacenan en tu dispositivo. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Tu perfil es almacenado en tu dispositivo y solamente se comparte con tus contactos. Los servidores SimpleX no pueden ver tu perfil. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Tu perfil ha sido modificado. Si lo guardas la actualización será enviada a todos tus contactos. + alert message + Your random profile Tu perfil aleatorio No comment provided by engineer. - - Your server - Tu servidor - No comment provided by engineer. - Your server address - Dirección de tu servidor + Dirección del servidor + No comment provided by engineer. + + + Your servers + Tus servidores No comment provided by engineer. @@ -6536,11 +9146,21 @@ Los servidores de SimpleX no pueden ver tu perfil. llamada aceptada call status + + accepted invitation + invitación aceptada + chat list item title + admin administrador member role + + admins + administradores + feature role + agreeing encryption for %@… acordando cifrado para %@… @@ -6551,6 +9171,11 @@ Los servidores de SimpleX no pueden ver tu perfil. acordando cifrado… chat item text + + all members + todos los miembros + feature role + always siempre @@ -6561,6 +9186,16 @@ Los servidores de SimpleX no pueden ver tu perfil. y %lld evento(s) más No comment provided by engineer. + + archived report + informes archivados + No comment provided by engineer. + + + attempts + intentos + No comment provided by engineer. + audio call (not e2e encrypted) llamada (sin cifrar) @@ -6588,19 +9223,25 @@ Los servidores de SimpleX no pueden ver tu perfil. blocked %@ - %@ bloqueado + ha bloqueado a %@ rcv group event chat item blocked by admin - bloqueado por el administrador - marked deleted chat item preview text + bloqueado por administrador + blocked chat item +marked deleted chat item preview text bold negrita No comment provided by engineer. + + call + llamada + No comment provided by engineer. + call error error en llamada @@ -6673,7 +9314,7 @@ Los servidores de SimpleX no pueden ver tu perfil. connecting - conectando + conectando... No comment provided by engineer. @@ -6704,7 +9345,7 @@ Los servidores de SimpleX no pueden ver tu perfil. connecting… conectando… - chat list item title + No comment provided by engineer. connection established @@ -6751,19 +9392,25 @@ Los servidores de SimpleX no pueden ver tu perfil. días time unit + + decryption errors + errores de descifrado + No comment provided by engineer. + default (%@) - por defecto (%@) - pref value + predeterminado (%@) + delete after time +pref value default (no) - por defecto (no) + predeterminado (no) No comment provided by engineer. default (yes) - por defecto (sí) + predeterminado (sí) No comment provided by engineer. @@ -6778,7 +9425,7 @@ Los servidores de SimpleX no pueden ver tu perfil. deleted group - grupo eliminado + ha eliminado el grupo rcv group event chat item @@ -6801,6 +9448,11 @@ Los servidores de SimpleX no pueden ver tu perfil. mensaje duplicado integrity error chat item + + duplicates + duplicados + No comment provided by engineer. + e2e encrypted cifrado de extremo a extremo @@ -6876,9 +9528,14 @@ Los servidores de SimpleX no pueden ver tu perfil. error No comment provided by engineer. - - event happened - evento ocurrido + + expired + expirados + No comment provided by engineer. + + + forwarded + reenviado No comment provided by engineer. @@ -6906,6 +9563,11 @@ Los servidores de SimpleX no pueden ver tu perfil. iOS Keychain se usará para almacenar la contraseña de forma segura después de reiniciar la aplicación o cambiar la contraseña. Esto permitirá recibir notificaciones automáticas. No comment provided by engineer. + + inactive + inactivo + No comment provided by engineer. + incognito via contact address link en modo incógnito mediante enlace de dirección del contacto @@ -6946,6 +9608,11 @@ Los servidores de SimpleX no pueden ver tu perfil. invitación al grupo %@ group name + + invite + Invitar + No comment provided by engineer. + invited ha sido invitado @@ -7001,6 +9668,11 @@ Los servidores de SimpleX no pueden ver tu perfil. conectado rcv group event chat item + + message + mensaje + No comment provided by engineer. + message received mensaje recibido @@ -7026,6 +9698,11 @@ Los servidores de SimpleX no pueden ver tu perfil. moderado por %@ marked deleted chat item preview text + + moderator + moderador + member role + months meses @@ -7034,7 +9711,7 @@ Los servidores de SimpleX no pueden ver tu perfil. never nunca - No comment provided by engineer. + delete after time new message @@ -7065,8 +9742,8 @@ Los servidores de SimpleX no pueden ver tu perfil. off desactivado enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7083,18 +9760,44 @@ Los servidores de SimpleX no pueden ver tu perfil. Activado group pref value + + other + otros + No comment provided by engineer. + + + other errors + otros errores + No comment provided by engineer. + owner propietario member role + + owners + propietarios + feature role + peer-to-peer p2p No comment provided by engineer. + + pending + pendiente + No comment provided by engineer. + + + pending approval + pendiente de aprobación + No comment provided by engineer. + quantum resistant e2e encryption + cifrado e2e resistente a tecnología cuántica chat item text @@ -7107,6 +9810,11 @@ Los servidores de SimpleX no pueden ver tu perfil. confirmación recibida… No comment provided by engineer. + + rejected + rechazado + No comment provided by engineer. + rejected call llamada rechazada @@ -7129,7 +9837,7 @@ Los servidores de SimpleX no pueden ver tu perfil. removed profile picture - imagen de perfil eliminada + ha eliminado la imagen del perfil profile update event chat item @@ -7137,6 +9845,26 @@ Los servidores de SimpleX no pueden ver tu perfil. te ha expulsado rcv group event chat item + + requested to connect + solicitado para conectar + chat list item title + + + saved + guardado + No comment provided by engineer. + + + saved from %@ + Guardado desde %@ + No comment provided by engineer. + + + search + buscar + No comment provided by engineer. + sec seg @@ -7162,6 +9890,15 @@ Los servidores de SimpleX no pueden ver tu perfil. Enviar mensaje directo No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + información cola del servidor: %1$@ + +último mensaje recibido: %2$@ + queue info + set new contact address nueva dirección de contacto @@ -7169,11 +9906,12 @@ Los servidores de SimpleX no pueden ver tu perfil. set new profile picture - nueva imagen de perfil + tiene nueva imagen del perfil profile update event chat item standard end-to-end encryption + cifrado estándar de extremo a extremo chat item text @@ -7193,7 +9931,7 @@ Los servidores de SimpleX no pueden ver tu perfil. unblocked %@ - %@ desbloqueado + ha desbloqueado a %@ rcv group event chat item @@ -7201,11 +9939,21 @@ Los servidores de SimpleX no pueden ver tu perfil. desconocido connection info + + unknown servers + con servidores desconocidos + No comment provided by engineer. + unknown status estado desconocido No comment provided by engineer. + + unprotected + con IP desprotegida + No comment provided by engineer. + updated group profile ha actualizado el perfil del grupo @@ -7246,6 +9994,11 @@ Los servidores de SimpleX no pueden ver tu perfil. mediante retransmisor No comment provided by engineer. + + video + video + No comment provided by engineer. + video call (not e2e encrypted) videollamada (sin cifrar) @@ -7271,11 +10024,21 @@ Los servidores de SimpleX no pueden ver tu perfil. semanas time unit + + when IP hidden + con IP oculta + No comment provided by engineer. + yes pref value + + you + tu + No comment provided by engineer. + you are invited to group has sido invitado a un grupo @@ -7350,7 +10113,7 @@ Los servidores de SimpleX no pueden ver tu perfil.
- +
@@ -7387,7 +10150,7 @@ Los servidores de SimpleX no pueden ver tu perfil.
- +
@@ -7407,4 +10170,250 @@ Los servidores de SimpleX no pueden ver tu perfil.
+ +
+ +
+ + + %d new events + %d evento(s) nuevo(s) + notification body + + + From %d chat(s) + De %d chat(s) + notification body + + + From: %@ + De: %@ + notification body + + + New events + Eventos nuevos + notification + + + New messages + Mensajes nuevos + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright © 2024 SimpleX Chat. Todos los derechos reservados. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + ¡Aplicación bloqueada! + No comment provided by engineer. + + + Cancel + Cancelar + No comment provided by engineer. + + + Cannot access keychain to save database password + Keychain inaccesible para guardar la contraseña de la base de datos + No comment provided by engineer. + + + Cannot forward message + No se puede reenviar el mensaje + No comment provided by engineer. + + + Comment + Comentario + No comment provided by engineer. + + + Currently maximum supported file size is %@. + El tamaño máximo de archivo admitido es %@. + No comment provided by engineer. + + + Database downgrade required + Se requiere volver a versión anterior de la base de datos + No comment provided by engineer. + + + Database encrypted! + ¡Base de datos cifrada! + No comment provided by engineer. + + + Database error + Error en base de datos + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + La contraseña de la base de datos es diferente a la almacenada en keychain. + No comment provided by engineer. + + + Database passphrase is required to open chat. + Se requiere la contraseña de la base de datos para abrir la aplicación. + No comment provided by engineer. + + + Database upgrade required + Se requiere actualizar la base de datos + No comment provided by engineer. + + + Error preparing file + Error al preparar el archivo + No comment provided by engineer. + + + Error preparing message + Error al preparar el mensaje + No comment provided by engineer. + + + Error: %@ + Error: %@ + No comment provided by engineer. + + + File error + Error de archivo + No comment provided by engineer. + + + Incompatible database version + Versión de base de datos incompatible + No comment provided by engineer. + + + Invalid migration confirmation + Confirmación de migración no válida + No comment provided by engineer. + + + Keychain error + Error en keychain + No comment provided by engineer. + + + Large file! + ¡Archivo grande! + No comment provided by engineer. + + + No active profile + Ningún perfil activo + No comment provided by engineer. + + + Ok + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + Abre la aplicación para volver a versión anterior de la base de datos. + No comment provided by engineer. + + + Open the app to upgrade the database. + Abre la aplicación para actualizar la base de datos. + No comment provided by engineer. + + + Passphrase + Frase de contraseña + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Por favor, crea un perfil en SimpleX + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Las preferencias seleccionadas no permiten este mensaje. + No comment provided by engineer. + + + Sending a message takes longer than expected. + Enviar el mensaje lleva más tiempo del esperado. + No comment provided by engineer. + + + Sending message… + Enviando mensaje… + No comment provided by engineer. + + + Share + Compartir + No comment provided by engineer. + + + Slow network? + ¿Red lenta? + No comment provided by engineer. + + + Unknown database error: %@ + Error desconocido en la base de datos: %@ + No comment provided by engineer. + + + Unsupported format + Formato sin soporte + No comment provided by engineer. + + + Wait + Espera + No comment provided by engineer. + + + Wrong database passphrase + Contraseña incorrecta de la base de datos + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + Puedes dar permiso para compartir en Privacidad y Seguridad / Bloque SimpleX. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/es.xcloc/contents.json b/apps/ios/SimpleX Localizations/es.xcloc/contents.json index c7d2c05ffa..80cffac8d2 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/es.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "es", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff index 3e971985ef..a54666bb10 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (voidaan kopioida) @@ -124,9 +97,12 @@ %@ on vahvistettu No comment provided by engineer. + + %@ server + No comment provided by engineer. + %@ servers - %@ palvelimet No comment provided by engineer. @@ -138,6 +114,10 @@ %@ haluaa muodostaa yhteyden! notification title + + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members No comment provided by engineer. @@ -157,11 +137,31 @@ %d päivää time interval + + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d tuntia time interval + + %d messages not forwarded + alert title + %d min %d min @@ -177,6 +177,10 @@ %d sek time interval + + %d seconds(s) + delete after time + %d skipped message(s) %d ohitettua viestiä @@ -242,11 +246,6 @@ %lld uutta käyttöliittymän kieltä No comment provided by engineer. - - %lld second(s) - %lld sekunti(a) - No comment provided by engineer. - %lld seconds %lld sekuntia @@ -297,11 +296,6 @@ %u viestit ohitettu. No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) No comment provided by engineer. @@ -310,31 +304,21 @@ (this device v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Lisää uusi kontakti**: luo kertakäyttöinen QR-koodi tai linkki kontaktille. + + **Create 1-time link**: to create and share a new invitation link. No comment provided by engineer. **Create group**: to create a new group. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **Yksityisempi**: tarkista uudet viestit 20 minuutin välein. Laitetunnus jaetaan SimpleX Chat -palvelimen kanssa, mutta ei sitä, kuinka monta yhteystietoa tai viestiä sinulla on. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **Yksityisin**: älä käytä SimpleX Chat -ilmoituspalvelinta, tarkista viestit ajoittain taustalla (riippuu siitä, kuinka usein käytät sovellusta). No comment provided by engineer. @@ -347,11 +331,15 @@ **Huomaa**: et voi palauttaa tai muuttaa tunnuslausetta, jos kadotat sen. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Suositus**: laitetunnus ja ilmoitukset lähetetään SimpleX Chat -ilmoituspalvelimelle, mutta ei viestin sisältöä, kokoa tai sitä, keneltä se on peräisin. No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **Varoitus**: Välittömät push-ilmoitukset vaativat tunnuslauseen, joka on tallennettu Keychainiin. @@ -376,11 +364,6 @@ \*bold* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -411,11 +394,6 @@ - historian muokkaaminen. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec time to disappear @@ -428,7 +406,8 @@ 1 day 1 päivä - time interval + delete after time +time interval 1 hour @@ -443,12 +422,26 @@ 1 month 1 kuukausi - time interval + delete after time +time interval 1 week 1 viikko - time interval + delete after time +time interval + + + 1 year + delete after time + + + 1-time link + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + No comment provided by engineer. 5 minutes @@ -465,11 +458,6 @@ 30 sekuntia No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -519,31 +507,29 @@ Keskeytä osoitteenvaihto? No comment provided by engineer. - - About SimpleX - Tietoja SimpleX:stä - No comment provided by engineer. - About SimpleX Chat Tietoja SimpleX Chatistä No comment provided by engineer. - - About SimpleX address - Tietoja SimpleX osoitteesta + + About operators No comment provided by engineer. - - Accent color - Korostusväri + + Accent No comment provided by engineer. Accept Hyväksy accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + No comment provided by engineer. Accept connection request? @@ -558,20 +544,40 @@ Accept incognito Hyväksy tuntematon - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + No comment provided by engineer. + + + Acknowledged + No comment provided by engineer. + + + Acknowledgement errors + No comment provided by engineer. + + + Active + token status text + + + Active connections + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Lisää osoite profiiliisi, jotta kontaktisi voivat jakaa sen muiden kanssa. Profiilipäivitys lähetetään kontakteillesi. No comment provided by engineer. - - Add contact + + Add friends No comment provided by engineer. - - Add preset servers - Lisää esiasetettuja palvelimia + + Add list No comment provided by engineer. @@ -579,14 +585,18 @@ Lisää profiili No comment provided by engineer. + + Add server + Lisää palvelin + No comment provided by engineer. + Add servers by scanning QR codes. Lisää palvelimia skannaamalla QR-koodeja. No comment provided by engineer. - - Add server… - Lisää palvelin… + + Add team members No comment provided by engineer. @@ -594,11 +604,39 @@ Lisää toiseen laitteeseen No comment provided by engineer. + + Add to list + No comment provided by engineer. + Add welcome message Lisää tervetuloviesti No comment provided by engineer. + + Add your team members to the conversations. + No comment provided by engineer. + + + Added media & file servers + No comment provided by engineer. + + + Added message servers + No comment provided by engineer. + + + Additional accent + No comment provided by engineer. + + + Additional accent 2 + No comment provided by engineer. + + + Additional secondary + No comment provided by engineer. + Address Osoite @@ -609,6 +647,14 @@ Osoitteenmuutos keskeytetään. Käytetään vanhaa vastaanotto-osoitetta. No comment provided by engineer. + + Address or 1-time link? + No comment provided by engineer. + + + Address settings + No comment provided by engineer. + Admins can block a member for all. No comment provided by engineer. @@ -623,6 +669,14 @@ Verkon lisäasetukset No comment provided by engineer. + + Advanced settings + No comment provided by engineer. + + + All + No comment provided by engineer. + All app data is deleted. Kaikki sovelluksen tiedot poistetaan. @@ -633,16 +687,28 @@ Kaikki keskustelut ja viestit poistetaan - tätä ei voi kumota! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + alert message + All data is erased when it is entered. Kaikki tiedot poistetaan, kun se syötetään. No comment provided by engineer. + + All data is kept private on your device. + No comment provided by engineer. + All group members will remain connected. Kaikki ryhmän jäsenet pysyvät yhteydessä. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! No comment provided by engineer. @@ -656,6 +722,18 @@ All new messages from %@ will be hidden! No comment provided by engineer. + + All profiles + profile dropdown + + + All reports will be archived for you. + No comment provided by engineer. + + + All servers + No comment provided by engineer. + All your contacts will remain connected. Kaikki kontaktisi pysyvät yhteydessä. @@ -680,11 +758,19 @@ Salli puhelut vain, jos kontaktisi sallii ne. No comment provided by engineer. + + Allow calls? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Salli katoavat viestit vain, jos kontaktisi sallii sen sinulle. No comment provided by engineer. + + Allow downgrade + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Salli peruuttamaton viestien poisto vain, jos kontaktisi sallii ne sinulle. (24 tuntia) @@ -710,11 +796,23 @@ Salli katoavien viestien lähettäminen. No comment provided by engineer. + + Allow sharing + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Salli lähetettyjen viestien peruuttamaton poistaminen. (24 tuntia) No comment provided by engineer. + + Allow to report messsages to moderators. + No comment provided by engineer. + + + Allow to send SimpleX links. + No comment provided by engineer. + Allow to send files and media. Salli tiedostojen ja median lähettäminen. @@ -773,6 +871,10 @@ Already joining the group! No comment provided by engineer. + + Always use private routing. + No comment provided by engineer. + Always use relay Käytä aina relettä @@ -783,11 +885,20 @@ Luodaan tyhjä chat-profiili annetulla nimellä, ja sovellus avautuu normaalisti. No comment provided by engineer. + + Another reason + report reason + Answer call Vastaa puheluun No comment provided by engineer. + + Anybody can host servers. + Avoimen lähdekoodin protokolla ja koodi - kuka tahansa voi käyttää palvelimia. + No comment provided by engineer. + App build: %@ Sovellusversio: %@ @@ -801,6 +912,10 @@ App encrypts new local files (except videos). No comment provided by engineer. + + App group: + No comment provided by engineer. + App icon Sovelluksen kuvake @@ -816,6 +931,10 @@ Sovelluksen pääsykoodi korvataan itsetuhoutuvalla pääsykoodilla. No comment provided by engineer. + + App session + No comment provided by engineer. + App version Sovellusversio @@ -835,10 +954,46 @@ Apply No comment provided by engineer. + + Apply to + No comment provided by engineer. + + + Archive + No comment provided by engineer. + + + Archive %lld reports? + No comment provided by engineer. + + + Archive all reports? + No comment provided by engineer. + Archive and upload No comment provided by engineer. + + Archive contacts to chat later. + No comment provided by engineer. + + + Archive report + No comment provided by engineer. + + + Archive report? + No comment provided by engineer. + + + Archive reports + swipe action + + + Archived contacts + No comment provided by engineer. + Archiving database No comment provided by engineer. @@ -903,11 +1058,19 @@ Hyväksy kuvat automaattisesti No comment provided by engineer. + + Auto-accept settings + alert title + Back Takaisin No comment provided by engineer. + + Background + No comment provided by engineer. + Bad desktop address No comment provided by engineer. @@ -922,15 +1085,51 @@ Virheellinen viestin tarkiste No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups No comment provided by engineer. + + Better groups performance + No comment provided by engineer. + + + Better message dates. + No comment provided by engineer. + Better messages Parempia viestejä No comment provided by engineer. + + Better networking + No comment provided by engineer. + + + Better notifications + No comment provided by engineer. + + + Better privacy and security + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + + + Black + No comment provided by engineer. + Block No comment provided by engineer. @@ -959,6 +1158,14 @@ Blocked by admin No comment provided by engineer. + + Blur for better privacy. + No comment provided by engineer. + + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Sekä sinä että kontaktisi voivat käyttää viestireaktioita. @@ -988,11 +1195,29 @@ Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + No comment provided by engineer. + + + Business chats + No comment provided by engineer. + + + Businesses + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). Chat-profiilin mukaan (oletus) tai [yhteyden mukaan](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + No comment provided by engineer. + Call already ended! Puhelu on jo päättynyt! @@ -1003,10 +1228,22 @@ Puhelut No comment provided by engineer. + + Calls prohibited! + No comment provided by engineer. + Camera not available No comment provided by engineer. + + Can't call contact + No comment provided by engineer. + + + Can't call member + No comment provided by engineer. + Can't invite contact! Kontaktia ei voi kutsua! @@ -1017,10 +1254,15 @@ Kontakteja ei voi kutsua! No comment provided by engineer. + + Can't message member + No comment provided by engineer. + Cancel Peruuta - No comment provided by engineer. + alert action +alert button Cancel migration @@ -1031,9 +1273,21 @@ Ei pääsyä avainnippuun tietokannan salasanan tallentamiseksi No comment provided by engineer. + + Cannot forward message + No comment provided by engineer. + Cannot receive file Tiedostoa ei voi vastaanottaa + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + snd error text + + + Cellular No comment provided by engineer. @@ -1041,6 +1295,14 @@ Muuta No comment provided by engineer. + + Change automatic message deletion? + alert title + + + Change chat profiles + authentication reason + Change database passphrase? Muutetaanko tietokannan tunnuslause? @@ -1085,11 +1347,22 @@ Change self-destruct passcode Vaihda itsetuhoutuva pääsykoodi authentication reason - set passcode view +set passcode view - - Chat archive - Chat-arkisto + + Chat + No comment provided by engineer. + + + Chat already exists + No comment provided by engineer. + + + Chat already exists! + No comment provided by engineer. + + + Chat colors No comment provided by engineer. @@ -1107,6 +1380,10 @@ Chat-tietokanta poistettu No comment provided by engineer. + + Chat database exported + No comment provided by engineer. + Chat database imported Chat-tietokanta tuotu @@ -1126,6 +1403,10 @@ Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat. No comment provided by engineer. + + Chat list + No comment provided by engineer. + Chat migrated! No comment provided by engineer. @@ -1135,15 +1416,44 @@ Chat-asetukset No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + Käyttäjäprofiili + No comment provided by engineer. + + + Chat theme + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + No comment provided by engineer. + Chats Keskustelut No comment provided by engineer. + + Check messages every 20 min. + No comment provided by engineer. + + + Check messages when allowed. + No comment provided by engineer. + Check server address and try again. Tarkista palvelimen osoite ja yritä uudelleen. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1164,10 +1474,22 @@ Valitse kirjastosta No comment provided by engineer. + + Chunks deleted + No comment provided by engineer. + + + Chunks downloaded + No comment provided by engineer. + + + Chunks uploaded + No comment provided by engineer. + Clear Tyhjennä - No comment provided by engineer. + swipe action Clear conversation @@ -1179,6 +1501,14 @@ Tyhjennä keskustelu? No comment provided by engineer. + + Clear group? + No comment provided by engineer. + + + Clear or delete group? + No comment provided by engineer. + Clear private notes? No comment provided by engineer. @@ -1188,11 +1518,18 @@ Tyhjennä vahvistus No comment provided by engineer. - - Colors - Värit + + Color chats with the new themes. No comment provided by engineer. + + Color mode + No comment provided by engineer. + + + Community guidelines violation + report reason + Compare file Vertaa tiedostoa @@ -1203,11 +1540,47 @@ Vertaa turvakoodeja kontaktiesi kanssa. No comment provided by engineer. + + Completed + No comment provided by engineer. + + + Conditions accepted on: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + No comment provided by engineer. + + + Conditions of use + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + No comment provided by engineer. + Configure ICE servers Määritä ICE-palvelimet No comment provided by engineer. + + Configure server operators + No comment provided by engineer. + Confirm Vahvista @@ -1218,11 +1591,19 @@ Vahvista pääsykoodi No comment provided by engineer. + + Confirm contact deletion? + No comment provided by engineer. + Confirm database upgrades Vahvista tietokannan päivitykset No comment provided by engineer. + + Confirm files from unknown servers. + No comment provided by engineer. + Confirm network settings No comment provided by engineer. @@ -1245,6 +1626,10 @@ Confirm upload No comment provided by engineer. + + Confirmed + token status text + Connect Yhdistä @@ -1263,6 +1648,10 @@ Connect to desktop No comment provided by engineer. + + Connect to your friends faster. + No comment provided by engineer. + Connect to yourself? No comment provided by engineer. @@ -1295,14 +1684,26 @@ This is your own one-time link! Connect with %@ No comment provided by engineer. + + Connected + No comment provided by engineer. + Connected desktop No comment provided by engineer. + + Connected servers + No comment provided by engineer. + Connected to desktop No comment provided by engineer. + + Connecting + No comment provided by engineer. + Connecting to server… Yhteyden muodostaminen palvelimeen… @@ -1313,6 +1714,10 @@ This is your own one-time link! Yhteyden muodostaminen palvelimeen... (virhe: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + No comment provided by engineer. + Connecting to desktop No comment provided by engineer. @@ -1322,6 +1727,14 @@ This is your own one-time link! Yhteys No comment provided by engineer. + + Connection and servers status. + No comment provided by engineer. + + + Connection blocked + No comment provided by engineer. + Connection error Yhteysvirhe @@ -1332,11 +1745,32 @@ This is your own one-time link! Yhteysvirhe (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + No comment provided by engineer. + + + Connection not ready. + No comment provided by engineer. + + + Connection notifications + No comment provided by engineer. + Connection request sent! Yhteyspyyntö lähetetty! No comment provided by engineer. + + Connection requires encryption renegotiation. + No comment provided by engineer. + + + Connection security + No comment provided by engineer. + Connection terminated No comment provided by engineer. @@ -1346,6 +1780,14 @@ This is your own one-time link! Yhteyden aikakatkaisu No comment provided by engineer. + + Connection with desktop stopped + No comment provided by engineer. + + + Connections + No comment provided by engineer. + Contact allows Kontakti sallii @@ -1356,6 +1798,10 @@ This is your own one-time link! Kontakti on jo olemassa No comment provided by engineer. + + Contact deleted! + No comment provided by engineer. + Contact hidden: Kontakti piilotettu: @@ -1366,9 +1812,8 @@ This is your own one-time link! Kontakti on yhdistetty notification - - Contact is not connected yet! - Kontaktia ei ole vielä yhdistetty! + + Contact is deleted. No comment provided by engineer. @@ -1381,6 +1826,10 @@ This is your own one-time link! Kontaktin asetukset No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + No comment provided by engineer. + Contacts Kontaktit @@ -1391,21 +1840,37 @@ This is your own one-time link! Kontaktit voivat merkitä viestit poistettaviksi; voit katsella niitä. No comment provided by engineer. + + Content violates conditions of use + blocking reason + Continue Jatka No comment provided by engineer. + + Conversation deleted! + No comment provided by engineer. + Copy Kopioi - chat item action + No comment provided by engineer. + + + Copy error + No comment provided by engineer. Core version: v%@ Ydinversio: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? No comment provided by engineer. @@ -1415,6 +1880,10 @@ This is your own one-time link! Luo No comment provided by engineer. + + Create 1-time link + No comment provided by engineer. + Create SimpleX address Luo SimpleX-osoite @@ -1424,11 +1893,6 @@ This is your own one-time link! Create a group using a random profile. No comment provided by engineer. - - Create an address to let people connect with you. - Luo osoite, jolla ihmiset voivat ottaa sinuun yhteyttä. - No comment provided by engineer. - Create file Luo tiedosto @@ -1448,6 +1912,10 @@ This is your own one-time link! Luo linkki No comment provided by engineer. + + Create list + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 Luo uusi profiili [työpöytäsovelluksessa](https://simplex.chat/downloads/). 💻 @@ -1455,6 +1923,7 @@ This is your own one-time link! Create profile + Luo profiilisi No comment provided by engineer. @@ -1472,6 +1941,10 @@ This is your own one-time link! Luo profiilisi No comment provided by engineer. + + Created + No comment provided by engineer. + Created at No comment provided by engineer. @@ -1480,11 +1953,6 @@ This is your own one-time link! Created at: %@ copied message info - - Created on %@ - Luotu %@ - No comment provided by engineer. - Creating archive link No comment provided by engineer. @@ -1498,11 +1966,19 @@ This is your own one-time link! Nykyinen pääsykoodi No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + No comment provided by engineer. + Current passphrase… Nykyinen tunnuslause… No comment provided by engineer. + + Current profile + No comment provided by engineer. + Currently maximum supported file size is %@. Nykyinen tuettu enimmäistiedostokoko on %@. @@ -1513,11 +1989,23 @@ This is your own one-time link! Mukautettu aika No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + + + Customize theme + No comment provided by engineer. + Dark Tumma No comment provided by engineer. + + Dark mode colors + No comment provided by engineer. + Database ID Tietokannan tunnus @@ -1616,6 +2104,10 @@ This is your own one-time link! Tietokanta siirretään, kun sovellus käynnistyy uudelleen No comment provided by engineer. + + Debug delivery + No comment provided by engineer. + Decentralized Hajautettu @@ -1629,17 +2121,17 @@ This is your own one-time link! Delete Poista - chat item action + alert action +swipe action + + + Delete %lld messages of members? + No comment provided by engineer. Delete %lld messages? No comment provided by engineer. - - Delete Contact - Poista kontakti - No comment provided by engineer. - Delete address Poista osoite @@ -1664,14 +2156,12 @@ This is your own one-time link! Delete and notify contact No comment provided by engineer. - - Delete archive - Poista arkisto + + Delete chat No comment provided by engineer. - - Delete chat archive? - Poista keskusteluarkisto? + + Delete chat messages from your device. No comment provided by engineer. @@ -1684,6 +2174,10 @@ This is your own one-time link! Poista keskusteluprofiili? No comment provided by engineer. + + Delete chat? + No comment provided by engineer. + Delete connection Poista yhteys @@ -1694,9 +2188,8 @@ This is your own one-time link! Poista kontakti No comment provided by engineer. - - Delete contact? -This cannot be undone! + + Delete contact? No comment provided by engineer. @@ -1758,6 +2251,10 @@ This cannot be undone! Poista linkki? No comment provided by engineer. + + Delete list? + alert title + Delete member message? Poista jäsenviesti? @@ -1771,7 +2268,7 @@ This cannot be undone! Delete messages Poista viestit - No comment provided by engineer. + alert button Delete messages after @@ -1788,9 +2285,8 @@ This cannot be undone! Poista vanha tietokanta? No comment provided by engineer. - - Delete pending connection - Poista vireillä oleva yhteys + + Delete or moderate up to 200 messages. No comment provided by engineer. @@ -1808,11 +2304,27 @@ This cannot be undone! Poista jono server test step + + Delete report + No comment provided by engineer. + + + Delete up to 20 messages at once. + No comment provided by engineer. + Delete user profile? Poista käyttäjäprofiili? No comment provided by engineer. + + Delete without notification + No comment provided by engineer. + + + Deleted + No comment provided by engineer. + Deleted at Poistettu klo @@ -1823,6 +2335,14 @@ This cannot be undone! Poistettu klo: %@ copied message info + + Deletion errors + No comment provided by engineer. + + + Delivered even when Apple drops them. + No comment provided by engineer. + Delivery Toimitus @@ -1855,11 +2375,35 @@ This cannot be undone! Desktop devices No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + + + Destination server error: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + + + Detailed statistics + No comment provided by engineer. + + + Details + No comment provided by engineer. + Develop Kehitä No comment provided by engineer. + + Developer options + No comment provided by engineer. + Developer tools Kehittäjätyökalut @@ -1890,8 +2434,12 @@ This cannot be undone! Yksityisviestit chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + No comment provided by engineer. + + + Direct messages between members are prohibited. Yksityisviestit jäsenten välillä ovat kiellettyjä tässä ryhmässä. No comment provided by engineer. @@ -1905,11 +2453,23 @@ This cannot be undone! Poista SimpleX Lock käytöstä authentication reason + + Disable automatic message deletion? + alert title + + + Disable delete messages + alert button + Disable for all Poista käytöstä kaikilta No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Tuhoutuva viesti @@ -1925,8 +2485,8 @@ This cannot be undone! Katoavat viestit ovat kiellettyjä tässä keskustelussa. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. Katoavat viestit ovat kiellettyjä tässä ryhmässä. No comment provided by engineer. @@ -1958,11 +2518,19 @@ This cannot be undone! Discover via local network No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. Älä käytä SimpleX-sovellusta hätäpuheluihin. No comment provided by engineer. + + Do NOT use private routing. + No comment provided by engineer. + Do it later Tee myöhemmin @@ -1972,6 +2540,14 @@ This cannot be undone! Do not send history to new members. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + + + Documents: + No comment provided by engineer. + Don't create address Älä luo osoitetta @@ -1982,16 +2558,33 @@ This cannot be undone! Älä salli No comment provided by engineer. + + Don't miss important messages. + No comment provided by engineer. + Don't show again Älä näytä uudelleen No comment provided by engineer. + + Done + No comment provided by engineer. + Downgrade and open chat Alenna ja avaa keskustelu No comment provided by engineer. + + Download + alert button +chat item action + + + Download errors + No comment provided by engineer. + Download failed No comment provided by engineer. @@ -2001,6 +2594,18 @@ This cannot be undone! Lataa tiedosto server test step + + Download files + alert action + + + Downloaded + No comment provided by engineer. + + + Downloaded files + No comment provided by engineer. + Downloading archive No comment provided by engineer. @@ -2019,6 +2624,10 @@ This cannot be undone! Kesto No comment provided by engineer. + + E2E encrypted notifications. + No comment provided by engineer. + Edit Muokkaa @@ -2039,6 +2648,10 @@ This cannot be undone! Salli (pidä ohitukset) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + No comment provided by engineer. + Enable SimpleX Lock Ota SimpleX Lock käyttöön @@ -2052,7 +2665,7 @@ This cannot be undone! Enable automatic message deletion? Ota automaattinen viestien poisto käyttöön? - No comment provided by engineer. + alert title Enable camera access @@ -2097,6 +2710,14 @@ This cannot be undone! Ota itsetuhoava pääsykoodi käyttöön set passcode view + + Enabled + No comment provided by engineer. + + + Enabled for + No comment provided by engineer. + Encrypt Salaa @@ -2163,6 +2784,10 @@ This cannot be undone! Encryption re-negotiation failed. No comment provided by engineer. + + Encryption renegotiation in progress. + No comment provided by engineer. + Enter Passcode Syötä pääsykoodi @@ -2224,30 +2849,33 @@ This cannot be undone! Virhe osoitteenmuutoksen keskeytyksessä No comment provided by engineer. + + Error accepting conditions + alert title + Error accepting contact request Virhe kontaktipyynnön hyväksymisessä No comment provided by engineer. - - Error accessing database file - Virhe tietokantatiedoston käyttämisessä - No comment provided by engineer. - Error adding member(s) Virhe lisättäessä jäseniä No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + alert title Error changing address Virhe osoitteenvaihdossa No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role Virhe roolin vaihdossa @@ -2258,6 +2886,18 @@ This cannot be undone! Virhe asetuksen muuttamisessa No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + + + Error checking token status + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Virhe osoitteen luomisessa @@ -2273,6 +2913,10 @@ This cannot be undone! Virhe ryhmälinkin luomisessa No comment provided by engineer. + + Error creating list + alert title + Error creating member contact No comment provided by engineer. @@ -2286,6 +2930,10 @@ This cannot be undone! Virhe profiilin luomisessa! No comment provided by engineer. + + Error creating report + No comment provided by engineer. + Error decrypting file Virhe tiedoston salauksen purussa @@ -2306,11 +2954,6 @@ This cannot be undone! Virhe yhteyden poistamisessa No comment provided by engineer. - - Error deleting contact - Virhe kontaktin poistamisessa - No comment provided by engineer. - Error deleting database Virhe tietokannan poistamisessa @@ -2355,6 +2998,10 @@ This cannot be undone! Virhe vietäessä keskustelujen tietokantaa No comment provided by engineer. + + Error exporting theme: %@ + No comment provided by engineer. + Error importing chat database Virhe keskustelujen tietokannan tuonnissa @@ -2365,9 +3012,12 @@ This cannot be undone! Virhe ryhmään liittymisessä No comment provided by engineer. - - Error loading %@ servers - Virhe %@-palvelimien lataamisessa + + Error loading servers + alert title + + + Error migrating settings No comment provided by engineer. @@ -2377,16 +3027,31 @@ This cannot be undone! Error receiving file Virhe tiedoston vastaanottamisessa + alert title + + + Error reconnecting server No comment provided by engineer. + + Error reconnecting servers + No comment provided by engineer. + + + Error registering for notifications + alert title + Error removing member Virhe poistettaessa jäsentä No comment provided by engineer. - - Error saving %@ servers - Virhe %@ palvelimien tallentamisessa + + Error reordering lists + alert title + + + Error resetting statistics No comment provided by engineer. @@ -2394,6 +3059,10 @@ This cannot be undone! Virhe ICE-palvelimien tallentamisessa No comment provided by engineer. + + Error saving chat list + alert title + Error saving group profile Virhe ryhmäprofiilin tallentamisessa @@ -2409,6 +3078,10 @@ This cannot be undone! Virhe tunnuslauseen tallentamisessa avainnippuun No comment provided by engineer. + + Error saving servers + alert title + Error saving settings when migrating @@ -2451,16 +3124,24 @@ This cannot be undone! Virhe keskustelun lopettamisessa No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! Virhe profiilin vaihdossa! - No comment provided by engineer. + alertTitle Error synchronizing connection Virhe yhteyden synkronoinnissa No comment provided by engineer. + + Error testing server connection + No comment provided by engineer. + Error updating group link Virhe ryhmälinkin päivittämisessä @@ -2471,6 +3152,10 @@ This cannot be undone! Virhe viestin päivityksessä No comment provided by engineer. + + Error updating server + alert title + Error updating settings Virhe asetusten päivittämisessä @@ -2497,7 +3182,9 @@ This cannot be undone! Error: %@ Virhe: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2509,6 +3196,14 @@ This cannot be undone! Virhe: ei tietokantatiedostoa No comment provided by engineer. + + Errors + No comment provided by engineer. + + + Errors in servers configuration. + servers error + Even when disabled in the conversation. Jopa kun ei käytössä keskustelussa. @@ -2523,6 +3218,10 @@ This cannot be undone! Expand chat item action + + Expired + token status text + Export database Vie tietokanta @@ -2533,6 +3232,10 @@ This cannot be undone! Vientivirhe: No comment provided by engineer. + + Export theme + No comment provided by engineer. + Exported database archive. Viety tietokanta-arkisto. @@ -2557,15 +3260,57 @@ This cannot be undone! Nopea ja ei odotusta, kunnes lähettäjä on online-tilassa! No comment provided by engineer. + + Faster deletion of groups. + No comment provided by engineer. + Faster joining and more reliable messages. No comment provided by engineer. + + Faster sending messages. + No comment provided by engineer. + Favorite Suosikki + swipe action + + + Favorites No comment provided by engineer. + + File error + file error alert title + + + File errors: +%@ + alert message + + + File is blocked by server operator: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + file error text + + + File server error: %@ + file error text + + + File status + No comment provided by engineer. + + + File status: %@ + copied message info + File will be deleted from servers. Tiedosto poistetaan palvelimilta. @@ -2586,6 +3331,10 @@ This cannot be undone! Tiedosto: %@ No comment provided by engineer. + + Files + No comment provided by engineer. + Files & media Tiedostot & media @@ -2596,11 +3345,15 @@ This cannot be undone! Tiedostot ja media chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. Tiedostot ja media ovat tässä ryhmässä kiellettyjä. No comment provided by engineer. + + Files and media not allowed + No comment provided by engineer. + Files and media prohibited! Tiedostot ja media kielletty! @@ -2659,11 +3412,93 @@ This cannot be undone! Ryhmän jäsen ei tue korjausta No comment provided by engineer. + + For all moderators + No comment provided by engineer. + + + For chat profile %@: + servers error + For console Konsoliin No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + No comment provided by engineer. + + + For me + No comment provided by engineer. + + + For private routing + No comment provided by engineer. + + + For social media + No comment provided by engineer. + + + Forward + chat item action + + + Forward %d message(s)? + alert title + + + Forward and save messages + No comment provided by engineer. + + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + + + Forwarded + No comment provided by engineer. + + + Forwarded from + No comment provided by engineer. + + + Forwarding %lld messages + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + snd error text + Found desktop No comment provided by engineer. @@ -2683,11 +3518,6 @@ This cannot be undone! Koko nimi (valinnainen) No comment provided by engineer. - - Full name: - Koko nimi: - No comment provided by engineer. - Fully decentralized – visible only to members. No comment provided by engineer. @@ -2707,6 +3537,18 @@ This cannot be undone! GIFit ja tarrat No comment provided by engineer. + + Get notified when mentioned. + No comment provided by engineer. + + + Good afternoon! + message preview + + + Good morning! + message preview + Group Ryhmä @@ -2760,36 +3602,6 @@ This cannot be undone! Ryhmälinkit No comment provided by engineer. - - Group members can add message reactions. - Ryhmän jäsenet voivat lisätä viestireaktioita. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Ryhmän jäsenet voivat poistaa lähetetyt viestit peruuttamattomasti. (24 tuntia) - No comment provided by engineer. - - - Group members can send direct messages. - Ryhmän jäsenet voivat lähettää suoraviestejä. - No comment provided by engineer. - - - Group members can send disappearing messages. - Ryhmän jäsenet voivat lähettää katoavia viestejä. - No comment provided by engineer. - - - Group members can send files and media. - Ryhmän jäsenet voivat lähettää tiedostoja ja mediaa. - No comment provided by engineer. - - - Group members can send voice messages. - Ryhmän jäsenet voivat lähettää ääniviestejä. - No comment provided by engineer. - Group message: Ryhmäviesti: @@ -2830,11 +3642,19 @@ This cannot be undone! Ryhmä poistetaan sinulta - tätä ei voi perua! No comment provided by engineer. + + Groups + No comment provided by engineer. + Help Apua No comment provided by engineer. + + Help admins moderating their groups. + No comment provided by engineer. + Hidden Piilotettu @@ -2884,10 +3704,17 @@ This cannot be undone! Miten SimpleX toimii No comment provided by engineer. + + How it affects privacy + No comment provided by engineer. + + + How it helps privacy + No comment provided by engineer. + How it works - Kuinka se toimii - No comment provided by engineer. + alert button How to @@ -2913,6 +3740,10 @@ This cannot be undone! ICE-palvelimet (yksi per rivi) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Jos et voi tavata henkilökohtaisesti, näytä QR-koodi videopuhelussa tai jaa linkki. @@ -2953,8 +3784,8 @@ This cannot be undone! Heti No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Immuuni roskapostille ja väärinkäytöksille No comment provided by engineer. @@ -2977,10 +3808,19 @@ This cannot be undone! Import failed No comment provided by engineer. + + Import theme + No comment provided by engineer. + Importing archive No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery No comment provided by engineer. @@ -3004,6 +3844,18 @@ This cannot be undone! Vastauksena No comment provided by engineer. + + In-call sounds + No comment provided by engineer. + + + Inappropriate content + report reason + + + Inappropriate profile + report reason + Incognito Incognito @@ -3072,6 +3924,11 @@ This cannot be undone! Asenna [SimpleX Chat terminaalille](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Heti + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3079,16 +3936,35 @@ This cannot be undone! No comment provided by engineer. - - Instantly - Heti - No comment provided by engineer. - Interface Käyttöliittymä No comment provided by engineer. + + Interface colors + No comment provided by engineer. + + + Invalid + token status text + + + Invalid (bad token) + token status text + + + Invalid (expired) + token status text + + + Invalid (unregistered) + token status text + + + Invalid (wrong topic) + token status text + Invalid QR code No comment provided by engineer. @@ -3121,7 +3997,7 @@ This cannot be undone! Invalid server address! Virheellinen palvelinosoite! - No comment provided by engineer. + alert title Invalid status @@ -3143,6 +4019,10 @@ This cannot be undone! Kutsu jäseniä No comment provided by engineer. + + Invite to chat + No comment provided by engineer. + Invite to group Kutsu ryhmään @@ -3158,8 +4038,8 @@ This cannot be undone! Viestien peruuttamaton poisto on kielletty tässä keskustelussa. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. Viestien peruuttamaton poisto on kielletty tässä ryhmässä. No comment provided by engineer. @@ -3184,6 +4064,10 @@ This cannot be undone! 3. Yhteys vaarantui. No comment provided by engineer. + + It protects your IP address and connections. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Näyttäisi, että olet jo yhteydessä tämän linkin kautta. Jos näin ei ole, tapahtui virhe (%@). @@ -3202,7 +4086,7 @@ This cannot be undone! Join Liity - No comment provided by engineer. + swipe action Join group @@ -3238,6 +4122,10 @@ This is your link for group %@! Keep + alert action + + + Keep conversation No comment provided by engineer. @@ -3246,7 +4134,7 @@ This is your link for group %@! Keep unused invitation? - No comment provided by engineer. + alert title Keep your connections @@ -3281,6 +4169,14 @@ This is your link for group %@! Leave Poistu + swipe action + + + Leave chat + No comment provided by engineer. + + + Leave chat? No comment provided by engineer. @@ -3320,6 +4216,18 @@ This is your link for group %@! Linked desktops No comment provided by engineer. + + List + swipe action + + + List name and emoji should be different for all lists. + No comment provided by engineer. + + + List name... + No comment provided by engineer. + Live message! Live-viesti! @@ -3330,11 +4238,6 @@ This is your link for group %@! Live-viestit No comment provided by engineer. - - Local - Paikallinen - No comment provided by engineer. - Local name Paikallinen nimi @@ -3355,11 +4258,6 @@ This is your link for group %@! Lukitustila No comment provided by engineer. - - Make a private connection - Luo yksityinen yhteys - No comment provided by engineer. - Make one message disappear Hävitä yksi viesti @@ -3370,21 +4268,11 @@ This is your link for group %@! Tee profiilista yksityinen! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Varmista, että %@-palvelinosoitteet ovat oikeassa muodossa, että ne on erotettu toisistaan riveittäin ja että ne eivät ole päällekkäisiä (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Varmista, että WebRTC ICE -palvelinosoitteet ovat oikeassa muodossa, rivieroteltuina ja että ne eivät ole päällekkäisiä. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Monet ihmiset kysyivät: *Jos SimpleX:llä ei ole käyttäjätunnuksia, miten se voi toimittaa viestejä?* - No comment provided by engineer. - Mark deleted for everyone Merkitse poistetuksi kaikilta @@ -3410,11 +4298,31 @@ This is your link for group %@! Enintään 30 sekuntia, vastaanotetaan välittömästi. No comment provided by engineer. + + Media & file servers + No comment provided by engineer. + + + Medium + blur media + Member Jäsen No comment provided by engineer. + + Member inactive + item status text + + + Member reports + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. Jäsenen rooli muuttuu muotoon "%@". Kaikille ryhmän jäsenille ilmoitetaan asiasta. @@ -3425,11 +4333,61 @@ This is your link for group %@! Jäsenen rooli muutetaan muotoon "%@". Jäsen saa uuden kutsun. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! Jäsen poistetaan ryhmästä - tätä ei voi perua! No comment provided by engineer. + + Members can add message reactions. + Ryhmän jäsenet voivat lisätä viestireaktioita. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + Ryhmän jäsenet voivat poistaa lähetetyt viestit peruuttamattomasti. (24 tuntia) + No comment provided by engineer. + + + Members can report messsages to moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + No comment provided by engineer. + + + Members can send direct messages. + Ryhmän jäsenet voivat lähettää suoraviestejä. + No comment provided by engineer. + + + Members can send disappearing messages. + Ryhmän jäsenet voivat lähettää katoavia viestejä. + No comment provided by engineer. + + + Members can send files and media. + Ryhmän jäsenet voivat lähettää tiedostoja ja mediaa. + No comment provided by engineer. + + + Members can send voice messages. + Ryhmän jäsenet voivat lähettää ääniviestejä. + No comment provided by engineer. + + + Mention members 👋 + No comment provided by engineer. + + + Menus + No comment provided by engineer. + Message delivery error Viestin toimitusvirhe @@ -3440,11 +4398,27 @@ This is your link for group %@! Viestien toimituskuittaukset! No comment provided by engineer. + + Message delivery warning + item status text + Message draft Viestiluonnos No comment provided by engineer. + + Message forwarded + item status text + + + Message may be delivered later if member becomes active. + item status description + + + Message queue info + No comment provided by engineer. + Message reactions Viestireaktiot @@ -3455,11 +4429,35 @@ This is your link for group %@! Viestireaktiot ovat kiellettyjä tässä keskustelussa. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. Viestireaktiot ovat kiellettyjä tässä ryhmässä. No comment provided by engineer. + + Message reception + No comment provided by engineer. + + + Message servers + No comment provided by engineer. + + + Message shape + No comment provided by engineer. + + + Message source remains private. + No comment provided by engineer. + + + Message status + No comment provided by engineer. + + + Message status: %@ + copied message info + Message text Viestin teksti @@ -3483,6 +4481,22 @@ This is your link for group %@! Messages from %@ will be shown! No comment provided by engineer. + + Messages in this chat will never be deleted. + alert message + + + Messages received + No comment provided by engineer. + + + Messages sent + No comment provided by engineer. + + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. No comment provided by engineer. @@ -3539,9 +4553,9 @@ This is your link for group %@! Siirto on valmis No comment provided by engineer. - - Migrations: %@ - Siirrot: %@ + + Migrations: + Siirrot: No comment provided by engineer. @@ -3559,21 +4573,28 @@ This is your link for group %@! Moderoitu klo: %@ copied message info + + More + swipe action + More improvements are coming soon! Lisää parannuksia on tulossa pian! No comment provided by engineer. + + More reliable network connection. + No comment provided by engineer. + + + More reliable notifications + No comment provided by engineer. + Most likely this connection is deleted. Todennäköisesti tämä yhteys on poistettu. item status description - - Most likely this contact has deleted the connection with you. - Todennäköisesti tämä kontakti on poistanut yhteyden sinuun. - No comment provided by engineer. - Multiple chat profiles Useita keskusteluprofiileja @@ -3582,7 +4603,11 @@ This is your link for group %@! Mute Mykistä - No comment provided by engineer. + notification label action + + + Mute all + notification label action Muted when inactive! @@ -3592,13 +4617,33 @@ This is your link for group %@! Name Nimi - No comment provided by engineer. + swipe action Network & servers Verkko ja palvelimet No comment provided by engineer. + + Network connection + No comment provided by engineer. + + + Network decentralization + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + snd error text + + + Network management + No comment provided by engineer. + + + Network operator + No comment provided by engineer. + Network settings Verkkoasetukset @@ -3609,15 +4654,31 @@ This is your link for group %@! Verkon tila No comment provided by engineer. + + New + token status text + New Passcode Uusi pääsykoodi No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat No comment provided by engineer. + + New chat experience 🎉 + No comment provided by engineer. + New contact request Uusi kontaktipyyntö @@ -3628,11 +4689,6 @@ This is your link for group %@! Uusi kontakti: notification - - New database archive - Uusi tietokanta-arkisto - No comment provided by engineer. - New desktop app! No comment provided by engineer. @@ -3642,11 +4698,19 @@ This is your link for group %@! Uusi näyttönimi No comment provided by engineer. + + New events + notification + New in %@ Uutta %@ No comment provided by engineer. + + New media options + No comment provided by engineer. + New member role Uusi jäsenrooli @@ -3662,6 +4726,10 @@ This is your link for group %@! Uusi tunnuslause… No comment provided by engineer. + + New server + No comment provided by engineer. + No Ei @@ -3672,6 +4740,18 @@ This is your link for group %@! Ei sovelluksen salasanaa Authentication unavailable + + No chats + No comment provided by engineer. + + + No chats found + No comment provided by engineer. + + + No chats in list %@ + No comment provided by engineer. + No contacts selected Kontakteja ei ole valittu @@ -3692,6 +4772,10 @@ This is your link for group %@! Ei laitetunnusta! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + item status description + No filtered chats Ei suodatettuja keskusteluja @@ -3707,20 +4791,94 @@ This is your link for group %@! Ei historiaa No comment provided by engineer. + + No info, try to reload + No comment provided by engineer. + + + No media & file servers. + servers error + + + No message + No comment provided by engineer. + + + No message servers. + servers error + + + No network connection + No comment provided by engineer. + + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message Ei lupaa ääniviestin tallentamiseen No comment provided by engineer. + + No push server + Paikallinen + No comment provided by engineer. + No received or sent files Ei vastaanotettuja tai lähetettyjä tiedostoja No comment provided by engineer. + + No servers for private message routing. + servers error + + + No servers to receive files. + servers error + + + No servers to receive messages. + servers error + + + No servers to send files. + servers error + + + No token! + alert title + + + No unread chats + No comment provided by engineer. + + + No user identifiers. + Ensimmäinen alusta ilman käyttäjätunnisteita – suunniteltu yksityiseksi. + No comment provided by engineer. + Not compatible! No comment provided by engineer. + + Notes + No comment provided by engineer. + + + Nothing selected + No comment provided by engineer. + + + Nothing to forward! + alert title + Notifications Ilmoitukset @@ -3731,6 +4889,18 @@ This is your link for group %@! Ilmoitukset on poistettu käytöstä! No comment provided by engineer. + + Notifications error + alert title + + + Notifications privacy + No comment provided by engineer. + + + Notifications status + alert title + Now admins can: - delete members' messages. @@ -3747,36 +4917,35 @@ This is your link for group %@! Off Pois - No comment provided by engineer. + blur media Ok Ok - No comment provided by engineer. + alert button Old database Vanha tietokanta No comment provided by engineer. - - Old database archive - Vanha tietokanta-arkisto - No comment provided by engineer. - One-time invitation link Kertakutsulinkki No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Yhteyden muodostamiseen tarvitaan Onion-isäntiä. Edellyttää VPN:n sallimista. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Yhteyden muodostamiseen tarvitaan Onion-isäntiä. +Edellyttää VPN:n sallimista. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Onion-isäntiä käytetään, kun niitä on saatavilla. Edellyttää VPN:n sallimista. + + Onion hosts will be used when available. +Requires compatible VPN. + Onion-isäntiä käytetään, kun niitä on saatavilla. +Edellyttää VPN:n sallimista. No comment provided by engineer. @@ -3784,11 +4953,19 @@ This is your link for group %@! Onion-isäntiä ei käytetä. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. Vain asiakaslaitteet tallentavat käyttäjäprofiileja, yhteystietoja, ryhmiä ja viestejä, jotka on lähetetty **kaksinkertaisella päästä päähän -salauksella**. No comment provided by engineer. + + Only delete conversation + No comment provided by engineer. + Only group owners can change group preferences. Vain ryhmän omistajat voivat muuttaa ryhmän asetuksia. @@ -3804,6 +4981,14 @@ This is your link for group %@! Vain ryhmän omistajat voivat ottaa ääniviestit käyttöön. No comment provided by engineer. + + Only sender and moderators see it + No comment provided by engineer. + + + Only you and moderators see it + No comment provided by engineer. + Only you can add message reactions. Vain sinä voit lisätä viestireaktioita. @@ -3856,13 +5041,17 @@ This is your link for group %@! Open - No comment provided by engineer. + alert action Open Settings Avaa Asetukset No comment provided by engineer. + + Open changes + No comment provided by engineer. + Open chat Avaa keskustelu @@ -3873,28 +5062,38 @@ This is your link for group %@! Avaa keskustelukonsoli authentication reason + + Open conditions + No comment provided by engineer. + Open group No comment provided by engineer. + + Open link? + alert title + Open migration to another device authentication reason - - Open user profiles - Avaa käyttäjäprofiilit - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Avoimen lähdekoodin protokolla ja koodi - kuka tahansa voi käyttää palvelimia. - No comment provided by engineer. - Opening app… No comment provided by engineer. + + Operator + No comment provided by engineer. + + + Operator server + alert title + + + Or import archive file + No comment provided by engineer. + Or paste archive link No comment provided by engineer. @@ -3911,6 +5110,23 @@ This is your link for group %@! Or show this code No comment provided by engineer. + + Or to share privately + No comment provided by engineer. + + + Organize chats into lists + No comment provided by engineer. + + + Other + No comment provided by engineer. + + + Other file errors: +%@ + alert message + PING count PING-määrä @@ -3946,6 +5162,10 @@ This is your link for group %@! Pääsykoodi asetettu! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show Salasana näytettäväksi @@ -3972,13 +5192,12 @@ This is your link for group %@! Paste the link you received No comment provided by engineer. - - People can connect to you only via the links you share. - Ihmiset voivat ottaa sinuun yhteyttä vain jakamiesi linkkien kautta. + + Pending No comment provided by engineer. - - Periodically + + Periodic Ajoittain No comment provided by engineer. @@ -3991,11 +5210,24 @@ This is your link for group %@! Picture-in-picture calls No comment provided by engineer. + + Play from the chat list. + No comment provided by engineer. + + + Please ask your contact to enable calls. + No comment provided by engineer. + Please ask your contact to enable sending voice messages. Pyydä kontaktiasi sallimaan ääniviestien lähettäminen. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Tarkista, että käytit oikeaa linkkiä tai pyydä kontaktiasi lähettämään sinulle uusi linkki. @@ -4060,59 +5292,106 @@ Error: %@ Säilytä tunnuslause turvallisesti, ET voi muuttaa sitä, jos kadotat sen. No comment provided by engineer. + + Please try to disable and re-enable notfications. + token info + + + Please wait for token activation to complete. + token info + + + Please wait for token to be registered. + token info + Polish interface Puolalainen käyttöliittymä No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Palvelimen osoitteen varmenteen sormenjälki on mahdollisesti virheellinen server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Säilytä viimeinen viestiluonnos liitteineen. No comment provided by engineer. - - Preset server - Esiasetettu palvelin - No comment provided by engineer. - Preset server address Esiasetettu palvelimen osoite No comment provided by engineer. + + Preset servers + No comment provided by engineer. + Preview Esikatselu No comment provided by engineer. + + Previously connected servers + No comment provided by engineer. + Privacy & security Yksityisyys ja turvallisuus No comment provided by engineer. + + Privacy for your customers. + No comment provided by engineer. + + + Privacy policy and conditions of use. + No comment provided by engineer. + Privacy redefined Yksityisyys uudelleen määritettynä No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + No comment provided by engineer. + Private filenames Yksityiset tiedostonimet No comment provided by engineer. + + Private media file names. + No comment provided by engineer. + + + Private message routing + No comment provided by engineer. + + + Private message routing 🚀 + No comment provided by engineer. + Private notes name of notes to self + + Private routing + No comment provided by engineer. + + + Private routing error + No comment provided by engineer. + Profile and server connections Profiili- ja palvelinyhteydet @@ -4123,12 +5402,8 @@ Error: %@ Profiilikuva No comment provided by engineer. - - Profile name - No comment provided by engineer. - - - Profile name: + + Profile images No comment provided by engineer. @@ -4136,10 +5411,14 @@ Error: %@ Profiilin salasana No comment provided by engineer. + + Profile theme + No comment provided by engineer. + Profile update will be sent to your contacts. Profiilipäivitys lähetetään kontakteillesi. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4161,6 +5440,14 @@ Error: %@ Estä viestireaktiot. No comment provided by engineer. + + Prohibit reporting messages to moderators. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + No comment provided by engineer. + Prohibit sending direct messages to members. Estä suorien viestien lähettäminen jäsenille. @@ -4181,11 +5468,20 @@ Error: %@ Estä ääniviestien lähettäminen. No comment provided by engineer. + + Protect IP address + No comment provided by engineer. + Protect app screen Suojaa sovellusnäyttö No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + No comment provided by engineer. + Protect your chat profiles with a password! Suojaa keskusteluprofiilisi salasanalla! @@ -4201,6 +5497,18 @@ Error: %@ Protokollan aikakatkaisu per KB No comment provided by engineer. + + Proxied + No comment provided by engineer. + + + Proxied servers + No comment provided by engineer. + + + Proxy requires password + No comment provided by engineer. + Push notifications Push-ilmoitukset @@ -4219,6 +5527,10 @@ Error: %@ Arvioi sovellus No comment provided by engineer. + + Reachable chat toolbar + No comment provided by engineer. + React… Reagoi… @@ -4227,32 +5539,27 @@ Error: %@ Read Lue - No comment provided by engineer. + swipe action Read more Lue lisää No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Lue lisää [Käyttöoppaasta](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Lue lisää [Käyttöoppaasta](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). Lue lisää [Käyttöoppaasta](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - - Read more in our GitHub repository. - Lue lisää GitHub-tietovarastostamme. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). Lue lisää [GitHub-arkistosta](https://github.com/simplex-chat/simplex-chat#readme). @@ -4263,6 +5570,10 @@ Error: %@ Kuittaukset pois käytöstä No comment provided by engineer. + + Receive errors + No comment provided by engineer. + Received at Vastaanotettu klo @@ -4283,6 +5594,18 @@ Error: %@ Vastaanotettu viesti message info title + + Received messages + No comment provided by engineer. + + + Received reply + No comment provided by engineer. + + + Received total + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. Vastaanotto-osoite vaihdetaan toiseen palvelimeen. Osoitteenmuutos tehdään sen jälkeen, kun lähettäjä tulee verkkoon. @@ -4302,16 +5625,40 @@ Error: %@ Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + No comment provided by engineer. + Recipients see updates as you type them. Vastaanottajat näkevät päivitykset, kun kirjoitat niitä. No comment provided by engineer. + + Reconnect + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Yhdistä kaikki yhdistetyt palvelimet uudelleen pakottaaksesi viestin toimituksen. Tämä käyttää ylimääräistä liikennettä. No comment provided by engineer. + + Reconnect all servers + No comment provided by engineer. + + + Reconnect all servers? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + No comment provided by engineer. + + + Reconnect server? + No comment provided by engineer. + Reconnect servers? Yhdistä palvelimet uudelleen? @@ -4332,10 +5679,23 @@ Error: %@ Pienempi akun käyttö No comment provided by engineer. + + Register + No comment provided by engineer. + + + Register notification token? + token info + + + Registered + token status text + Reject Hylkää - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4362,6 +5722,14 @@ Error: %@ Poista No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + + + Remove image + No comment provided by engineer. + Remove member Poista jäsen @@ -4417,6 +5785,46 @@ Error: %@ Vastaa chat item action + + Report + chat item action + + + Report content: only group moderators will see it. + report reason + + + Report member profile: only group moderators will see it. + report reason + + + Report other: only group moderators will see it. + report reason + + + Report reason? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + report reason + + + Report violation: only group moderators will see it. + report reason + + + Report: %@ + report in notification + + + Reporting messages to moderators is prohibited. + No comment provided by engineer. + + + Reports + No comment provided by engineer. + Required Pakollinen @@ -4427,16 +5835,36 @@ Error: %@ Oletustilaan No comment provided by engineer. + + Reset all hints + No comment provided by engineer. + + + Reset all statistics + No comment provided by engineer. + + + Reset all statistics? + No comment provided by engineer. + Reset colors Oletusvärit No comment provided by engineer. + + Reset to app theme + No comment provided by engineer. + Reset to defaults Palauta oletusasetukset No comment provided by engineer. + + Reset to user theme + No comment provided by engineer. + Restart the app to create a new chat profile Käynnistä sovellus uudelleen uuden keskusteluprofiilin luomiseksi @@ -4476,9 +5904,8 @@ Error: %@ Paljasta chat item action - - Revert - Palauta + + Review conditions No comment provided by engineer. @@ -4506,9 +5933,16 @@ Error: %@ Käynnistä chat No comment provided by engineer. - - SMP servers - SMP-palvelimet + + SMP server + No comment provided by engineer. + + + SOCKS proxy + No comment provided by engineer. + + + Safely receive files No comment provided by engineer. @@ -4518,43 +5952,42 @@ Error: %@ Save Tallenna - chat item action + alert button +chat item action Save (and notify contacts) Tallenna (ja ilmoita kontakteille) - No comment provided by engineer. + alert button Save and notify contact Tallenna ja ilmoita kontaktille - No comment provided by engineer. + alert button Save and notify group members Tallenna ja ilmoita ryhmän jäsenille No comment provided by engineer. + + Save and reconnect + No comment provided by engineer. + Save and update group profile Tallenna ja päivitä ryhmäprofiili No comment provided by engineer. - - Save archive - Tallenna arkisto - No comment provided by engineer. - - - Save auto-accept settings - Tallenna automaattisen hyväksynnän asetukset - No comment provided by engineer. - Save group profile Tallenna ryhmäprofiili No comment provided by engineer. + + Save list + No comment provided by engineer. + Save passphrase and open chat Tallenna tunnuslause ja avaa keskustelu @@ -4568,7 +6001,7 @@ Error: %@ Save preferences? Tallenna asetukset? - No comment provided by engineer. + alert title Save profile password @@ -4583,27 +6016,46 @@ Error: %@ Save servers? Tallenna palvelimet? - No comment provided by engineer. - - - Save settings? - Tallenna asetukset? - No comment provided by engineer. + alert title Save welcome message? Tallenna tervetuloviesti? No comment provided by engineer. + + Save your profile? + alert title + + + Saved + No comment provided by engineer. + Saved WebRTC ICE servers will be removed Tallennetut WebRTC ICE -palvelimet poistetaan No comment provided by engineer. + + Saved from + No comment provided by engineer. + Saved message message info title + + Saving %lld messages + No comment provided by engineer. + + + Scale + No comment provided by engineer. + + + Scan / Paste link + No comment provided by engineer. + Scan QR code Skannaa QR-koodi @@ -4641,11 +6093,19 @@ Error: %@ Search or paste SimpleX link No comment provided by engineer. + + Secondary + No comment provided by engineer. + Secure queue Turvallinen jono server test step + + Secured + No comment provided by engineer. + Security assessment Turvallisuusarviointi @@ -4659,6 +6119,18 @@ Error: %@ Select Valitse + chat item action + + + Select chat profile + No comment provided by engineer. + + + Selected %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. No comment provided by engineer. @@ -4696,11 +6168,6 @@ Error: %@ Lähetä toimituskuittaukset vastaanottajalle No comment provided by engineer. - - Send direct message - Lähetä yksityisviesti - No comment provided by engineer. - Send direct message to connect No comment provided by engineer. @@ -4710,6 +6177,10 @@ Error: %@ Lähetä katoava viesti No comment provided by engineer. + + Send errors + No comment provided by engineer. + Send link previews Lähetä linkkien esikatselu @@ -4720,14 +6191,25 @@ Error: %@ Lähetä live-viesti No comment provided by engineer. + + Send message to enable calls. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + No comment provided by engineer. + Send notifications Lähetys ilmoitukset No comment provided by engineer. - - Send notifications: - Lähetys ilmoitukset: + + Send private reports No comment provided by engineer. @@ -4752,7 +6234,7 @@ Error: %@ Sender cancelled file transfer. Lähettäjä peruutti tiedoston siirron. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4809,6 +6291,10 @@ Error: %@ Lähetetty klo: %@ copied message info + + Sent directly + No comment provided by engineer. + Sent file event Lähetetty tiedosto tapahtuma @@ -4819,11 +6305,59 @@ Error: %@ Lähetetty viesti message info title + + Sent messages + No comment provided by engineer. + Sent messages will be deleted after set time. Lähetetyt viestit poistetaan asetetun ajan kuluttua. No comment provided by engineer. + + Sent reply + No comment provided by engineer. + + + Sent total + No comment provided by engineer. + + + Sent via proxy + No comment provided by engineer. + + + Server + No comment provided by engineer. + + + Server added to operator %@. + alert message + + + Server address + No comment provided by engineer. + + + Server address is incompatible with network settings. + srv error text. + + + Server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Server operator changed. + alert title + + + Server operators + No comment provided by engineer. + + + Server protocol changed. + alert title + Server requires authorization to create queues, check password Palvelin vaatii valtuutuksen jonojen luomiseen, tarkista salasana @@ -4839,11 +6373,31 @@ Error: %@ Palvelintesti epäonnistui! No comment provided by engineer. + + Server type + No comment provided by engineer. + + + Server version is incompatible with network settings. + srv error text + + + Server version is incompatible with your app: %@. + No comment provided by engineer. + Servers Palvelimet No comment provided by engineer. + + Servers info + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + No comment provided by engineer. + Session code No comment provided by engineer. @@ -4853,11 +6407,19 @@ Error: %@ Aseta 1 päivä No comment provided by engineer. + + Set chat name… + No comment provided by engineer. + Set contact name… Aseta kontaktin nimi… No comment provided by engineer. + + Set default theme + No comment provided by engineer. + Set group preferences Aseta ryhmän asetukset @@ -4868,6 +6430,10 @@ Error: %@ Aseta se järjestelmän todennuksen sijaan. No comment provided by engineer. + + Set message expiration in chats. + No comment provided by engineer. + Set passcode Aseta pääsykoodi @@ -4897,24 +6463,49 @@ Error: %@ Asetukset No comment provided by engineer. + + Settings were changed. + alert message + + + Shape profile images + No comment provided by engineer. + Share Jaa - chat item action + alert action +chat item action Share 1-time link Jaa kertakäyttölinkki No comment provided by engineer. + + Share 1-time link with a friend + No comment provided by engineer. + + + Share SimpleX address on social media. + No comment provided by engineer. + Share address Jaa osoite No comment provided by engineer. + + Share address publicly + No comment provided by engineer. + Share address with contacts? Jaa osoite kontakteille? + alert title + + + Share from other apps. No comment provided by engineer. @@ -4922,15 +6513,27 @@ Error: %@ Jaa linkki No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link No comment provided by engineer. + + Share to SimpleX + No comment provided by engineer. + Share with contacts Jaa kontaktien kanssa No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code No comment provided by engineer. @@ -4950,21 +6553,41 @@ Error: %@ Näytä viimeiset viestit No comment provided by engineer. + + Show message status + No comment provided by engineer. + + + Show percentage + No comment provided by engineer. + Show preview Näytä esikatselu No comment provided by engineer. + + Show → on messages sent via private routing. + No comment provided by engineer. + Show: Näytä: No comment provided by engineer. + + SimpleX + No comment provided by engineer. + SimpleX Address SimpleX-osoite No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. Trail of Bits on tarkastanut SimpleX Chatin tietoturvan. @@ -4995,6 +6618,18 @@ Error: %@ SimpleX-osoite No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + No comment provided by engineer. + + + SimpleX address or 1-time link? + No comment provided by engineer. + + + SimpleX channel link + simplex link type + SimpleX contact address SimpleX-yhteystiedot @@ -5013,6 +6648,14 @@ Error: %@ SimpleX links SimpleX-linkit + chat feature + + + SimpleX links are prohibited. + No comment provided by engineer. + + + SimpleX links not allowed No comment provided by engineer. @@ -5020,10 +6663,18 @@ Error: %@ SimpleX-kertakutsu simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode No comment provided by engineer. + + Size + No comment provided by engineer. + Skip Ohita @@ -5039,16 +6690,46 @@ Error: %@ Pienryhmät (max 20) No comment provided by engineer. + + Soft + blur media + + + Some app settings were not migrated. + No comment provided by engineer. + + + Some file(s) were not exported: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. Tuonnin aikana tapahtui joitakin ei-vakavia virheitä – saatat nähdä Chat-konsolissa lisätietoja. No comment provided by engineer. + + Some non-fatal errors occurred during import: + No comment provided by engineer. + + + Some servers failed the test: +%@ + alert message + Somebody Joku notification title + + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + No comment provided by engineer. + Start chat Aloita keskustelu @@ -5063,6 +6744,14 @@ Error: %@ Aloita siirto No comment provided by engineer. + + Starting from %@. + No comment provided by engineer. + + + Statistics + No comment provided by engineer. + Stop Lopeta @@ -5077,11 +6766,6 @@ Error: %@ Stop chat No comment provided by engineer. - - Stop chat to enable database actions - Pysäytä keskustelu tietokantatoimien mahdollistamiseksi - No comment provided by engineer. - Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. Pysäytä keskustelut viedäksesi, tuodaksesi tai poistaaksesi keskustelujen tietokannan. Et voi vastaanottaa ja lähettää viestejä, kun keskustelut on pysäytetty. @@ -5110,27 +6794,55 @@ Error: %@ Stop sharing Lopeta jakaminen - No comment provided by engineer. + alert action Stop sharing address? Lopeta osoitteen jakaminen? - No comment provided by engineer. + alert title Stopping chat No comment provided by engineer. + + Storage + No comment provided by engineer. + + + Strong + blur media + Submit Lähetä No comment provided by engineer. + + Subscribed + No comment provided by engineer. + + + Subscription errors + No comment provided by engineer. + + + Subscriptions ignored + No comment provided by engineer. + Support SimpleX Chat SimpleX Chat tuki No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System Järjestelmä @@ -5141,11 +6853,19 @@ Error: %@ Järjestelmän todennus No comment provided by engineer. + + TCP connection + No comment provided by engineer. + TCP connection timeout TCP-yhteyden aikakatkaisu No comment provided by engineer. + + TCP port for messaging + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5161,11 +6881,19 @@ Error: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture Ota kuva No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + No comment provided by engineer. + Tap button Napauta painiketta @@ -5198,16 +6926,19 @@ Error: %@ Tap to scan No comment provided by engineer. - - Tap to start a new chat - Aloita uusi keskustelu napauttamalla - No comment provided by engineer. + + Temporary file error + file error alert title Test failed at step %@. Testi epäonnistui vaiheessa %@. server test failure + + Test notifications + No comment provided by engineer. + Test server Testipalvelin @@ -5221,7 +6952,7 @@ Error: %@ Tests failed! Testit epäonnistuivat! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5238,11 +6969,6 @@ Error: %@ Kiitokset käyttäjille – osallistu Weblaten kautta! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - Ensimmäinen alusta ilman käyttäjätunnisteita – suunniteltu yksityiseksi. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5255,6 +6981,14 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.Sovellus voi ilmoittaa sinulle, kun saat viestejä tai yhteydenottopyyntöjä - avaa asetukset ottaaksesi ne käyttöön. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + No comment provided by engineer. + The attempt to change database passphrase was not completed. Tietokannan tunnuslauseen muuttamista ei suoritettu loppuun. @@ -5264,6 +6998,10 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.The code you scanned is not a SimpleX link QR code. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + No comment provided by engineer. + The connection you accepted will be cancelled! Hyväksymäsi yhteys peruuntuu! @@ -5284,6 +7022,11 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.Salaus toimii ja uutta salaussopimusta ei tarvita. Tämä voi johtaa yhteysvirheisiin! No comment provided by engineer. + + The future of messaging + Seuraavan sukupolven yksityisviestit + No comment provided by engineer. + The hash of the previous message is different. Edellisen viestin tarkiste on erilainen. @@ -5299,9 +7042,12 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.Viesti merkitään moderoiduksi kaikille jäsenille. No comment provided by engineer. - - The next generation of private messaging - Seuraavan sukupolven yksityisviestit + + The messages will be deleted for all members. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. No comment provided by engineer. @@ -5309,9 +7055,12 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.Vanhaa tietokantaa ei poistettu siirron aikana, se voidaan kuitenkin poistaa. No comment provided by engineer. - - The profile is only shared with your contacts. - Profiili jaetaan vain kontaktiesi kanssa. + + The same conditions will apply to operator **%@**. + No comment provided by engineer. + + + The second preset operator in the app! No comment provided by engineer. @@ -5329,13 +7078,24 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.Palvelimet nykyisen keskusteluprofiilisi uusille yhteyksille **%@**. No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. No comment provided by engineer. - - Theme - Teema + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + + + Themes + No comment provided by engineer. + + + These conditions will also apply for: **%@**. No comment provided by engineer. @@ -5358,6 +7118,10 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.Tätä toimintoa ei voi kumota - valittua aikaisemmin lähetetyt ja vastaanotetut viestit poistetaan. Tämä voi kestää useita minuutteja. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. Tätä toimintoa ei voi kumota - profiilisi, kontaktisi, viestisi ja tiedostosi poistuvat peruuttamattomasti. @@ -5397,11 +7161,27 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.This is your own one-time link! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + No comment provided by engineer. + + + This message was deleted or not received yet. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. Tämä asetus koskee nykyisen keskusteluprofiilisi viestejä *%@**. No comment provided by engineer. + + Title + No comment provided by engineer. + To ask any questions and to receive updates: Voit esittää kysymyksiä ja saada päivityksiä: @@ -5421,9 +7201,8 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.Uuden yhteyden luominen No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Yksityisyyden suojaamiseksi kaikkien muiden alustojen käyttämien käyttäjätunnusten sijaan SimpleX käyttää viestijonojen tunnisteita, jotka ovat kaikille kontakteille erillisiä. + + To protect against your link being replaced, you can compare contact security codes. No comment provided by engineer. @@ -5431,6 +7210,10 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.Aikavyöhykkeen suojaamiseksi kuva-/äänitiedostot käyttävät UTC:tä. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5438,6 +7221,23 @@ You will be prompted to complete authentication before this feature is enabled.< Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus otetaan käyttöön. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Yksityisyyden suojaamiseksi kaikkien muiden alustojen käyttämien käyttäjätunnusten sijaan SimpleX käyttää viestijonojen tunnisteita, jotka ovat kaikille kontakteille erillisiä. + No comment provided by engineer. + + + To receive + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Jos haluat nauhoittaa ääniviestin, anna lupa käyttää mikrofonia. @@ -5448,25 +7248,53 @@ Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus ote Voit paljastaa piilotetun profiilisi syöttämällä koko salasanan hakukenttään **Keskusteluprofiilisi** -sivulla. No comment provided by engineer. + + To send + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. Keskustelujen-tietokanta on siirrettävä välittömien push-ilmoitusten tukemiseksi. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. Voit tarkistaa päästä päähän -salauksen kontaktisi kanssa vertaamalla (tai skannaamalla) laitteidenne koodia. No comment provided by engineer. + + Toggle chat list: + No comment provided by engineer. + Toggle incognito when connecting. No comment provided by engineer. + + Token status: %@. + token status + + + Toolbar opacity + No comment provided by engineer. + + + Total + No comment provided by engineer. + Transport isolation Kuljetuksen eristäminen No comment provided by engineer. + + Transport sessions + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Yritetään muodostaa yhteyttä palvelimeen, jota käytetään tämän kontaktin viestien vastaanottamiseen (virhe: %@). @@ -5516,10 +7344,9 @@ Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus ote Unblock member? No comment provided by engineer. - - Unexpected error: %@ - Odottamaton virhe: %@ - item status description + + Undelivered messages + No comment provided by engineer. Unexpected migration state @@ -5529,7 +7356,7 @@ Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus ote Unfav. Epäsuotuisa. - No comment provided by engineer. + swipe action Unhide @@ -5566,6 +7393,10 @@ Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus ote Tuntematon virhe No comment provided by engineer. + + Unknown servers! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. Ellet käytä iOS:n puhelinkäyttöliittymää, ota Älä häiritse -tila käyttöön keskeytysten välttämiseksi. @@ -5599,11 +7430,15 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Unmute Poista mykistys - No comment provided by engineer. + notification label action Unread Lukematon + swipe action + + + Unsupported connection link No comment provided by engineer. @@ -5615,11 +7450,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Päivitä No comment provided by engineer. - - Update .onion hosts setting? - Päivitä .onion-isäntien asetus? - No comment provided by engineer. - Update database passphrase Päivitä tietokannan tunnuslause @@ -5630,9 +7460,12 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Päivitä verkkoasetukset? No comment provided by engineer. - - Update transport isolation mode? - Päivitä kuljetuksen eristystila? + + Update settings? + No comment provided by engineer. + + + Updated conditions No comment provided by engineer. @@ -5640,16 +7473,15 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Asetusten päivittäminen yhdistää asiakkaan uudelleen kaikkiin palvelimiin. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - Tämän asetuksen päivittäminen yhdistää asiakkaan uudelleen kaikkiin palvelimiin. - No comment provided by engineer. - Upgrade and open chat Päivitä ja avaa keskustelu No comment provided by engineer. + + Upload errors + No comment provided by engineer. + Upload failed No comment provided by engineer. @@ -5659,20 +7491,44 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Lataa tiedosto server test step + + Uploaded + No comment provided by engineer. + + + Uploaded files + No comment provided by engineer. + Uploading archive No comment provided by engineer. + + Use %@ + No comment provided by engineer. + Use .onion hosts Käytä .onion-isäntiä No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? Käytä SimpleX Chat palvelimia? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat Käytä chattia @@ -5683,6 +7539,14 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Käytä nykyistä profiilia No comment provided by engineer. + + Use for files + No comment provided by engineer. + + + Use for messages + No comment provided by engineer. + Use for new connections Käytä uusiin yhteyksiin @@ -5706,23 +7570,45 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Use only local notifications? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + No comment provided by engineer. + + + Use private routing with unknown servers. + No comment provided by engineer. + Use server Käytä palvelinta No comment provided by engineer. + + Use servers + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. No comment provided by engineer. - - User profile - Käyttäjäprofiili + + Use the app with one hand. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - .onion-isäntien käyttäminen vaatii yhteensopivan VPN-palveluntarjoajan. + + Use web port + No comment provided by engineer. + + + User selection + No comment provided by engineer. + + + Username No comment provided by engineer. @@ -5789,11 +7675,19 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Videot ja tiedostot 1 Gt asti No comment provided by engineer. + + View conditions + No comment provided by engineer. + View security code Näytä turvakoodi No comment provided by engineer. + + View updated conditions + No comment provided by engineer. + Visible history chat feature @@ -5808,11 +7702,15 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Ääniviestit ovat kiellettyjä tässä keskustelussa. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. Ääniviestit ovat kiellettyjä tässä ryhmässä. No comment provided by engineer. + + Voice messages not allowed + No comment provided by engineer. + Voice messages prohibited! Ääniviestit kielletty! @@ -5842,6 +7740,14 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Odottaa videota No comment provided by engineer. + + Wallpaper accent + No comment provided by engineer. + + + Wallpaper background + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures No comment provided by engineer. @@ -5880,9 +7786,12 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Kun saatavilla No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Kun ihmiset pyytävät yhteyden muodostamista, voit hyväksyä tai hylätä sen. + + When connecting audio and video calls. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. No comment provided by engineer. @@ -5890,6 +7799,18 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Kun jaat inkognitoprofiilin jonkun kanssa, tätä profiilia käytetään ryhmissä, joihin tämä sinut kutsuu. No comment provided by engineer. + + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + No comment provided by engineer. + + + Wired ethernet + No comment provided by engineer. + With encrypted files and media. No comment provided by engineer. @@ -5903,24 +7824,34 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja With reduced battery usage. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + alert message + Wrong database passphrase Väärä tietokannan tunnuslause No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + file error text + Wrong passphrase! Väärä tunnuslause! No comment provided by engineer. - - XFTP servers - XFTP-palvelimet - No comment provided by engineer. - - - You - Sinä + + XFTP server No comment provided by engineer. @@ -5947,6 +7878,10 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Olet jo muodostanut yhteyden %@:n kanssa. No comment provided by engineer. + + You are already connected with %@. + No comment provided by engineer. + You are already connecting to %@. No comment provided by engineer. @@ -5986,11 +7921,23 @@ Repeat join request? Sinut on kutsuttu ryhmään No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. Voit vastaanottaa puheluita lukitusnäytöltä ilman laitteen ja sovelluksen todennusta. No comment provided by engineer. + + You can change it in Appearance settings. + No comment provided by engineer. + + + You can configure servers via settings. + No comment provided by engineer. + You can create it later Voit luoda sen myöhemmin @@ -6019,11 +7966,19 @@ Repeat join request? You can make it visible to your SimpleX contacts via Settings. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ Voit nyt lähettää viestejä %@:lle notification body + + You can send messages to %@ from Archived contacts. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + No comment provided by engineer. + You can set lock screen notification preview via settings. Voit määrittää lukitusnäytön ilmoituksen esikatselun asetuksista. @@ -6039,16 +7994,15 @@ Repeat join request? Voit jakaa tämän osoitteen kontaktiesi kanssa, jotta ne voivat muodostaa yhteyden **%@** kanssa. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - Voit jakaa osoitteesi linkkinä tai QR-koodina - kuka tahansa voi muodostaa yhteyden sinuun. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app Voit aloittaa keskustelun sovelluksen Asetukset / Tietokanta kautta tai käynnistämällä sovelluksen uudelleen No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. Voit ottaa SimpleX Lockin käyttöön Asetusten kautta. @@ -6061,23 +8015,23 @@ Repeat join request? You can view invitation link again in connection details. - No comment provided by engineer. + alert message You can't send messages! Et voi lähettää viestejä! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Sinä hallitset, minkä palvelim(i)en kautta **viestit vastaanotetaan**, kontaktisi - palvelimet, joita käytät viestien lähettämiseen niille. - No comment provided by engineer. - You could not be verified; please try again. Sinua ei voitu todentaa; yritä uudelleen. No comment provided by engineer. + + You decide who can connect. + Kimin bağlanabileceğine siz karar verirsiniz. + No comment provided by engineer. + You have already requested connection via this address! No comment provided by engineer. @@ -6087,11 +8041,6 @@ Repeat join request? Repeat connection request? No comment provided by engineer. - - You have no chats - Sinulla ei ole keskusteluja - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. Sinun on annettava tunnuslause aina, kun sovellus käynnistyy - sitä ei tallenneta laitteeseen. @@ -6112,11 +8061,23 @@ Repeat connection request? Liityit tähän ryhmään. Muodostetaan yhteyttä ryhmän jäsenten kutsumiseksi. No comment provided by engineer. + + You may migrate the exported database. + No comment provided by engineer. + + + You may save the exported archive. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. Sinun tulee käyttää keskustelujen-tietokannan uusinta versiota AINOSTAAN yhdessä laitteessa, muuten saatat lakata vastaanottamasta viestejä joiltakin kontakteilta. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. Sinun on sallittava kontaktiesi lähettää ääniviestejä, jotta voit lähettää niitä. @@ -6132,6 +8093,10 @@ Repeat connection request? Lähetit ryhmäkutsun No comment provided by engineer. + + You should receive notifications. + token info + You will be connected to group when the group host's device is online, please wait or check later! Sinut yhdistetään ryhmään, kun ryhmän isännän laite on online-tilassa, odota tai tarkista myöhemmin! @@ -6165,6 +8130,10 @@ Repeat connection request? Saat edelleen puheluita ja ilmoituksia mykistetyiltä profiileilta, kun ne ovat aktiivisia. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Et enää saa viestejä tästä ryhmästä. Keskusteluhistoria säilytetään. @@ -6185,31 +8154,16 @@ Repeat connection request? Käytät tässä ryhmässä incognito-profiilia. Kontaktien kutsuminen ei ole sallittua, jotta pääprofiilisi ei tule jaetuksi No comment provided by engineer. - - Your %@ servers - %@-palvelimesi - No comment provided by engineer. - Your ICE servers ICE-palvelimesi No comment provided by engineer. - - Your SMP servers - SMP-palvelimesi - No comment provided by engineer. - Your SimpleX address SimpleX-osoitteesi No comment provided by engineer. - - Your XFTP servers - XFTP-palvelimesi - No comment provided by engineer. - Your calls Puhelusi @@ -6225,16 +8179,17 @@ Repeat connection request? Keskustelut-tietokantasi ei ole salattu - aseta tunnuslause sen salaamiseksi. No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles Keskusteluprofiilisi No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Kontaktin tulee olla online-tilassa, jotta yhteys voidaan muodostaa. -Voit peruuttaa tämän yhteyden ja poistaa kontaktin (ja yrittää myöhemmin uudella linkillä). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. No comment provided by engineer. @@ -6252,6 +8207,10 @@ Voit peruuttaa tämän yhteyden ja poistaa kontaktin (ja yrittää myöhemmin uu Kontaktisi pysyvät yhdistettyinä. No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Nykyinen keskustelut-tietokantasi poistetaan ja korvataan tuodulla tietokannalla. @@ -6281,33 +8240,34 @@ Voit peruuttaa tämän yhteyden ja poistaa kontaktin (ja yrittää myöhemmin uu Profiilisi **%@** jaetaan. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Profiilisi tallennetaan laitteeseesi ja jaetaan vain yhteystietojesi kanssa. -SimpleX-palvelimet eivät näe profiiliasi. + + Your profile is stored on your device and only shared with your contacts. + Profiili jaetaan vain kontaktiesi kanssa. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Profiilisi, kontaktisi ja toimitetut viestit tallennetaan laitteellesi. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Profiilisi tallennetaan laitteeseesi ja jaetaan vain yhteystietojesi kanssa. SimpleX-palvelimet eivät näe profiiliasi. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your random profile Satunnainen profiilisi No comment provided by engineer. - - Your server - Palvelimesi - No comment provided by engineer. - Your server address Palvelimesi osoite No comment provided by engineer. + + Your servers + No comment provided by engineer. + Your settings Asetuksesi @@ -6348,11 +8308,19 @@ SimpleX-palvelimet eivät näe profiiliasi. hyväksytty puhelu call status + + accepted invitation + chat list item title + admin ylläpitäjä member role + + admins + feature role + agreeing encryption for %@… salauksesta sovitaan %@:lle… @@ -6363,6 +8331,10 @@ SimpleX-palvelimet eivät näe profiiliasi. hyväksyy salausta… chat item text + + all members + feature role + always aina @@ -6372,6 +8344,14 @@ SimpleX-palvelimet eivät näe profiiliasi. and %lld other events No comment provided by engineer. + + archived report + No comment provided by engineer. + + + attempts + No comment provided by engineer. + audio call (not e2e encrypted) äänipuhelu (ei e2e-salattu) @@ -6401,13 +8381,18 @@ SimpleX-palvelimet eivät näe profiiliasi. blocked by admin - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold lihavoitu No comment provided by engineer. + + call + No comment provided by engineer. + call error soittovirhe @@ -6510,7 +8495,7 @@ SimpleX-palvelimet eivät näe profiiliasi. connecting… yhdistää… - chat list item title + No comment provided by engineer. connection established @@ -6556,10 +8541,15 @@ SimpleX-palvelimet eivät näe profiiliasi. päivää time unit + + decryption errors + No comment provided by engineer. + default (%@) oletusarvo (%@) - pref value + delete after time +pref value default (no) @@ -6605,6 +8595,10 @@ SimpleX-palvelimet eivät näe profiiliasi. päällekkäinen viesti integrity error chat item + + duplicates + No comment provided by engineer. + e2e encrypted e2e-salattu @@ -6680,9 +8674,12 @@ SimpleX-palvelimet eivät näe profiiliasi. virhe No comment provided by engineer. - - event happened - tapahtuma tapahtui + + expired + No comment provided by engineer. + + + forwarded No comment provided by engineer. @@ -6710,6 +8707,10 @@ SimpleX-palvelimet eivät näe profiiliasi. iOS-Avainnippua käytetään tunnuslauseen turvalliseen tallentamiseen sen muuttamisen tai sovelluksen uudelleen käynnistämisen jälkeen - se mahdollistaa push-ilmoitusten vastaanottamisen. No comment provided by engineer. + + inactive + No comment provided by engineer. + incognito via contact address link incognito kontaktilinkin kautta @@ -6750,6 +8751,10 @@ SimpleX-palvelimet eivät näe profiiliasi. kutsu ryhmään %@ group name + + invite + No comment provided by engineer. + invited kutsuttu @@ -6804,6 +8809,10 @@ SimpleX-palvelimet eivät näe profiiliasi. yhdistetty rcv group event chat item + + message + No comment provided by engineer. + message received viesti vastaanotettu @@ -6829,6 +8838,10 @@ SimpleX-palvelimet eivät näe profiiliasi. %@ moderoi marked deleted chat item preview text + + moderator + member role + months kuukautta @@ -6837,7 +8850,7 @@ SimpleX-palvelimet eivät näe profiiliasi. never ei koskaan - No comment provided by engineer. + delete after time new message @@ -6868,8 +8881,8 @@ SimpleX-palvelimet eivät näe profiiliasi. off pois enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -6886,16 +8899,36 @@ SimpleX-palvelimet eivät näe profiiliasi. päällä group pref value + + other + No comment provided by engineer. + + + other errors + No comment provided by engineer. + owner omistaja member role + + owners + feature role + peer-to-peer vertais No comment provided by engineer. + + pending + No comment provided by engineer. + + + pending approval + No comment provided by engineer. + quantum resistant e2e encryption chat item text @@ -6910,6 +8943,10 @@ SimpleX-palvelimet eivät näe profiiliasi. vahvistus saatu… No comment provided by engineer. + + rejected + No comment provided by engineer. + rejected call hylätty puhelu @@ -6938,6 +8975,22 @@ SimpleX-palvelimet eivät näe profiiliasi. poisti sinut rcv group event chat item + + requested to connect + chat list item title + + + saved + No comment provided by engineer. + + + saved from %@ + No comment provided by engineer. + + + search + No comment provided by engineer. + sec sek @@ -6962,6 +9015,12 @@ SimpleX-palvelimet eivät näe profiiliasi. send direct message No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + queue info + set new contact address profile update event chat item @@ -6998,10 +9057,18 @@ SimpleX-palvelimet eivät näe profiiliasi. tuntematon connection info + + unknown servers + No comment provided by engineer. + unknown status No comment provided by engineer. + + unprotected + No comment provided by engineer. + updated group profile päivitetty ryhmäprofiili @@ -7040,6 +9107,10 @@ SimpleX-palvelimet eivät näe profiiliasi. releellä No comment provided by engineer. + + video + No comment provided by engineer. + video call (not e2e encrypted) videopuhelu (ei e2e-salattu) @@ -7065,11 +9136,19 @@ SimpleX-palvelimet eivät näe profiiliasi. viikkoa time unit + + when IP hidden + No comment provided by engineer. + yes kyllä pref value + + you + No comment provided by engineer. + you are invited to group sinut on kutsuttu ryhmään @@ -7142,7 +9221,7 @@ SimpleX-palvelimet eivät näe profiiliasi.
- +
@@ -7178,7 +9257,7 @@ SimpleX-palvelimet eivät näe profiiliasi.
- +
@@ -7198,4 +9277,205 @@ SimpleX-palvelimet eivät näe profiiliasi.
+ +
+ +
+ + + %d new events + notification body + + + From %d chat(s) + notification body + + + From: %@ + notification body + + + New events + notification + + + New messages + notification + + +
+ +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + App is locked! + No comment provided by engineer. + + + Cancel + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Cannot forward message + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database encrypted! + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database passphrase is required to open chat. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Passphrase + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + No comment provided by engineer. + + + Sending a message takes longer than expected. + No comment provided by engineer. + + + Sending message… + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Slow network? + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wait + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/contents.json b/apps/ios/SimpleX Localizations/fi.xcloc/contents.json index 0e3ae6dc56..11f7a4861c 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/fi.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "fi", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff index 70fb3145f9..59bde0650e 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (peut être copié) @@ -109,6 +82,7 @@ %@ downloaded + %@ téléchargé No comment provided by engineer. @@ -126,6 +100,11 @@ %@ est vérifié·e No comment provided by engineer. + + %@ server + Serveur %@ + No comment provided by engineer. + %@ servers Serveurs %@ @@ -133,6 +112,7 @@ %@ uploaded + %@ envoyé No comment provided by engineer. @@ -140,6 +120,11 @@ %@ veut se connecter ! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ et %lld membres @@ -160,11 +145,36 @@ %d jours time interval + + %d file(s) are still being downloaded. + %d fichier(s) en cours de téléchargement. + forward confirmation reason + + + %d file(s) failed to download. + Le téléchargement de %d fichier(s) a échoué. + forward confirmation reason + + + %d file(s) were deleted. + Le(s) fichier(s) %d a(ont) été supprimé(s). + forward confirmation reason + + + %d file(s) were not downloaded. + Le(s) fichier(s) %d n'a (n'ont) pas été téléchargé(s). + forward confirmation reason + %d hours %d heures time interval + + %d messages not forwarded + %d messages non transférés + alert title + %d min %d min @@ -180,6 +190,11 @@ %d sec time interval + + %d seconds(s) + %d seconde(s) + delete after time + %d skipped message(s) %d message·s sauté·s @@ -250,11 +265,6 @@ %lld nouvelles langues d'interface No comment provided by engineer. - - %lld second(s) - %lld seconde·s - No comment provided by engineer. - %lld seconds %lld secondes @@ -305,11 +315,6 @@ %u messages sautés. No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) (nouveau) @@ -320,19 +325,9 @@ (cet appareil v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - **Ajouter un contact** : pour créer un nouveau lien d'invitation ou vous connecter via un lien que vous avez reçu. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Ajouter un nouveau contact** : pour créer un lien ou code QR unique pour votre contact. + + **Create 1-time link**: to create and share a new invitation link. + **Ajouter un contact** : pour créer un nouveau lien d'invitation. No comment provided by engineer. @@ -340,18 +335,19 @@ **Créer un groupe** : pour créer un nouveau groupe. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **Vie privée** : vérification de nouveaux messages toute les 20 minutes. Le token de l'appareil est partagé avec le serveur SimpleX, mais pas le nombre de messages ou de contacts. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **Confidentiel** : ne pas utiliser le serveur de notifications SimpleX, vérification de nouveaux messages periodiquement en arrière plan (dépend de l'utilisation de l'app). No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Remarque** : l'utilisation de la même base de données sur deux appareils interrompt le déchiffrement des messages provenant de vos connexions, par mesure de sécurité. No comment provided by engineer. @@ -359,11 +355,16 @@ **Veuillez noter** : vous NE pourrez PAS récupérer ou modifier votre phrase secrète si vous la perdez. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Recommandé** : le token de l'appareil et les notifications sont envoyés au serveur de notifications SimpleX, mais pas le contenu du message, sa taille ou son auteur. No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + **Scanner / Coller** : pour vous connecter via un lien que vous avez reçu. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **Avertissement** : les notifications push instantanées nécessitent une phrase secrète enregistrée dans la keychain. @@ -371,6 +372,7 @@ **Warning**: the archive will be removed. + **Avertissement** : l'archive sera supprimée. No comment provided by engineer. @@ -388,11 +390,6 @@ \*gras* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -429,11 +426,6 @@ - l'historique de modification. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 sec @@ -447,7 +439,8 @@ 1 day 1 jour - time interval + delete after time +time interval 1 hour @@ -462,12 +455,29 @@ 1 month 1 mois - time interval + delete after time +time interval 1 week 1 semaine - time interval + delete after time +time interval + + + 1 year + 1 an + delete after time + + + 1-time link + Lien unique + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + Le lien unique peut être utilisé *avec un seul contact* - partagez le en personne ou via n'importe quelle messagerie. + No comment provided by engineer. 5 minutes @@ -484,11 +494,6 @@ 30 secondes No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -525,7 +530,7 @@ Abort - Annuler + Abandonner No comment provided by engineer. @@ -538,31 +543,32 @@ Abandonner le changement d'adresse ? No comment provided by engineer. - - About SimpleX - À propos de SimpleX - No comment provided by engineer. - About SimpleX Chat À propos de SimpleX Chat No comment provided by engineer. - - About SimpleX address - À propos de l'adresse SimpleX + + About operators + À propos des opérateurs No comment provided by engineer. - - Accent color - Couleur principale + + Accent + Principale No comment provided by engineer. Accept Accepter accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Accepter les conditions + No comment provided by engineer. Accept connection request? @@ -577,21 +583,47 @@ Accept incognito Accepter en incognito - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + Conditions acceptées + No comment provided by engineer. + + + Acknowledged + Reçu avec accusé de réception + No comment provided by engineer. + + + Acknowledgement errors + Erreur d'accusé de réception + No comment provided by engineer. + + + Active + Actif + token status text + + + Active connections + Connections actives + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Ajoutez une adresse à votre profil, afin que vos contacts puissent la partager avec d'autres personnes. La mise à jour du profil sera envoyée à vos contacts. No comment provided by engineer. - - Add contact - Ajouter le contact + + Add friends + Ajouter des amis No comment provided by engineer. - - Add preset servers - Ajouter des serveurs prédéfinis + + Add list + Ajouter une liste No comment provided by engineer. @@ -599,14 +631,19 @@ Ajouter un profil No comment provided by engineer. + + Add server + Ajouter un serveur + No comment provided by engineer. + Add servers by scanning QR codes. Ajoutez des serveurs en scannant des codes QR. No comment provided by engineer. - - Add server… - Ajouter un serveur… + + Add team members + Ajouter des membres à l'équipe No comment provided by engineer. @@ -614,11 +651,46 @@ Ajouter à un autre appareil No comment provided by engineer. + + Add to list + Ajouter à la liste + No comment provided by engineer. + Add welcome message Ajouter un message d'accueil No comment provided by engineer. + + Add your team members to the conversations. + Ajoutez les membres de votre équipe aux conversations. + No comment provided by engineer. + + + Added media & file servers + Ajout de serveurs de médias et de fichiers + No comment provided by engineer. + + + Added message servers + Ajout de serveurs de messages + No comment provided by engineer. + + + Additional accent + Accent additionnel + No comment provided by engineer. + + + Additional accent 2 + Accent additionnel 2 + No comment provided by engineer. + + + Additional secondary + Accent secondaire + No comment provided by engineer. + Address Adresse @@ -629,8 +701,19 @@ Le changement d'adresse sera annulé. L'ancienne adresse de réception sera utilisée. No comment provided by engineer. + + Address or 1-time link? + Adresse ou lien unique ? + No comment provided by engineer. + + + Address settings + Paramètres de l'adresse + No comment provided by engineer. + Admins can block a member for all. + Les admins peuvent bloquer un membre pour tous. No comment provided by engineer. @@ -643,6 +726,16 @@ Paramètres réseau avancés No comment provided by engineer. + + Advanced settings + Paramètres avancés + No comment provided by engineer. + + + All + Tout + No comment provided by engineer. + All app data is deleted. Toutes les données de l'application sont supprimées. @@ -653,16 +746,31 @@ Toutes les discussions et tous les messages seront supprimés - il est impossible de revenir en arrière ! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + Tous les chats seront supprimés de la liste %@, et la liste sera supprimée. + alert message + All data is erased when it is entered. Toutes les données sont effacées lorsqu'il est saisi. No comment provided by engineer. + + All data is kept private on your device. + Toutes les données restent confinées dans votre appareil. + No comment provided by engineer. + All group members will remain connected. Tous les membres du groupe resteront connectés. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + Tous les messages et fichiers sont envoyés **chiffrés de bout en bout**, avec une sécurité post-quantique dans les messages directs. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! Tous les messages seront supprimés - il n'est pas possible de revenir en arrière ! @@ -678,6 +786,20 @@ Tous les nouveaux messages de %@ seront cachés ! No comment provided by engineer. + + All profiles + Tous les profiles + profile dropdown + + + All reports will be archived for you. + Tous les rapports seront archivés pour vous. + No comment provided by engineer. + + + All servers + No comment provided by engineer. + All your contacts will remain connected. Tous vos contacts resteront connectés. @@ -690,6 +812,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Tous vos contacts, conversations et fichiers seront chiffrés en toute sécurité et transférés par morceaux vers les relais XFTP configurés. No comment provided by engineer. @@ -702,11 +825,21 @@ Autoriser les appels que si votre contact les autorise. No comment provided by engineer. + + Allow calls? + Autoriser les appels ? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Autorise les messages éphémères seulement si votre contact vous l’autorise. No comment provided by engineer. + + Allow downgrade + Autoriser la rétrogradation + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Autoriser la suppression irréversible des messages uniquement si votre contact vous l'autorise. (24 heures) @@ -732,11 +865,26 @@ Autorise l’envoi de messages éphémères. No comment provided by engineer. + + Allow sharing + Autoriser le partage + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Autoriser la suppression irréversible de messages envoyés. (24 heures) No comment provided by engineer. + + Allow to report messsages to moderators. + Permettre de signaler des messages aux modérateurs. + No comment provided by engineer. + + + Allow to send SimpleX links. + Autorise l'envoi de liens SimpleX. + No comment provided by engineer. + Allow to send files and media. Permet l'envoi de fichiers et de médias. @@ -797,6 +945,11 @@ Groupe déjà rejoint ! No comment provided by engineer. + + Always use private routing. + Toujours utiliser le routage privé. + No comment provided by engineer. + Always use relay Se connecter via relais @@ -807,11 +960,21 @@ Un profil de chat vierge portant le nom fourni est créé et l'application s'ouvre normalement. No comment provided by engineer. + + Another reason + Autre raison + report reason + Answer call Répondre à l'appel No comment provided by engineer. + + Anybody can host servers. + N'importe qui peut heberger un serveur. + No comment provided by engineer. + App build: %@ Build de l'app : %@ @@ -819,6 +982,7 @@ App data migration + Transfert des données de l'application No comment provided by engineer. @@ -826,6 +990,10 @@ L'application chiffre les nouveaux fichiers locaux (sauf les vidéos). No comment provided by engineer. + + App group: + No comment provided by engineer. + App icon Icône de l'app @@ -841,6 +1009,11 @@ Le code d'accès de l'application est remplacé par un code d'autodestruction. No comment provided by engineer. + + App session + Session de l'app + No comment provided by engineer. + App version Version de l'app @@ -858,14 +1031,62 @@ Apply + Appliquer + No comment provided by engineer. + + + Apply to + Appliquer à + No comment provided by engineer. + + + Archive + Archiver + No comment provided by engineer. + + + Archive %lld reports? + Archiver les rapports %lld ? + No comment provided by engineer. + + + Archive all reports? + Archiver tous les rapports ? No comment provided by engineer. Archive and upload + Archiver et téléverser + No comment provided by engineer. + + + Archive contacts to chat later. + Archiver les contacts pour discuter plus tard. + No comment provided by engineer. + + + Archive report + Archiver le rapport + No comment provided by engineer. + + + Archive report? + Archiver le rapport ? + No comment provided by engineer. + + + Archive reports + Archiver les rapports + swipe action + + + Archived contacts + Contacts archivés No comment provided by engineer. Archiving database + Archivage de la base de données No comment provided by engineer. @@ -928,11 +1149,21 @@ Images auto-acceptées No comment provided by engineer. + + Auto-accept settings + Paramètres de réception automatique + alert title + Back Retour No comment provided by engineer. + + Background + Fond + No comment provided by engineer. + Bad desktop address Mauvaise adresse de bureau @@ -948,16 +1179,61 @@ Mauvais hash de message No comment provided by engineer. + + Better calls + Appels améliorés + No comment provided by engineer. + Better groups Des groupes plus performants No comment provided by engineer. + + Better groups performance + Meilleure performance des groupes + No comment provided by engineer. + + + Better message dates. + Meilleures dates de messages. + No comment provided by engineer. + Better messages Meilleurs messages No comment provided by engineer. + + Better networking + Meilleure gestion de réseau + No comment provided by engineer. + + + Better notifications + Notifications améliorées + No comment provided by engineer. + + + Better privacy and security + Meilleure protection de la privacité et de la sécurité + No comment provided by engineer. + + + Better security ✅ + Sécurité accrue ✅ + No comment provided by engineer. + + + Better user experience + Une meilleure expérience pour l'utilisateur + No comment provided by engineer. + + + Black + Noir + No comment provided by engineer. + Block Bloquer @@ -993,6 +1269,16 @@ Bloqué par l'administrateur No comment provided by engineer. + + Blur for better privacy. + Rendez les images floues et protégez-les contre les regards indiscrets. + No comment provided by engineer. + + + Blur media + Flouter les médias + No comment provided by engineer. + Both you and your contact can add message reactions. Vous et votre contact pouvez ajouter des réactions aux messages. @@ -1023,11 +1309,35 @@ Bulgare, finnois, thaïlandais et ukrainien - grâce aux utilisateurs et à [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat) ! No comment provided by engineer. + + Business address + Adresse professionnelle + No comment provided by engineer. + + + Business chats + Discussions professionnelles + No comment provided by engineer. + + + Businesses + Entreprises + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). Par profil de chat (par défaut) ou [par connexion](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + En utilisant SimpleX Chat, vous acceptez de : +- n'envoyer que du contenu légal dans les groupes publics. +- respecter les autres utilisateurs - pas de spam. + No comment provided by engineer. + Call already ended! Appel déjà terminé ! @@ -1038,11 +1348,26 @@ Appels No comment provided by engineer. + + Calls prohibited! + Les appels ne sont pas autorisés ! + No comment provided by engineer. + Camera not available Caméra non disponible No comment provided by engineer. + + Can't call contact + Impossible d'appeler le contact + No comment provided by engineer. + + + Can't call member + Impossible d'appeler le membre + No comment provided by engineer. + Can't invite contact! Impossible d'inviter le contact ! @@ -1053,13 +1378,20 @@ Impossible d'inviter les contacts ! No comment provided by engineer. + + Can't message member + Impossible d'envoyer un message à ce membre + No comment provided by engineer. + Cancel Annuler - No comment provided by engineer. + alert action +alert button Cancel migration + Annuler le transfert No comment provided by engineer. @@ -1067,9 +1399,24 @@ Impossible d'accéder à la keychain pour enregistrer le mot de passe de la base de données No comment provided by engineer. + + Cannot forward message + Impossible de transférer le message + No comment provided by engineer. + Cannot receive file Impossible de recevoir le fichier + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Capacité dépassée - le destinataire n'a pas pu recevoir les messages envoyés précédemment. + snd error text + + + Cellular + Cellulaire No comment provided by engineer. @@ -1077,6 +1424,16 @@ Changer No comment provided by engineer. + + Change automatic message deletion? + Modifier la suppression automatique des messages ? + alert title + + + Change chat profiles + Changer de profil de discussion + authentication reason + Change database passphrase? Changer la phrase secrète de la base de données ? @@ -1121,11 +1478,26 @@ Change self-destruct passcode Modifier le code d'autodestruction authentication reason - set passcode view +set passcode view - - Chat archive - Archives du chat + + Chat + Discussions + No comment provided by engineer. + + + Chat already exists + La discussion existe déjà + No comment provided by engineer. + + + Chat already exists! + La discussion existe déjà ! + No comment provided by engineer. + + + Chat colors + Couleurs de chat No comment provided by engineer. @@ -1143,6 +1515,11 @@ Base de données du chat supprimée No comment provided by engineer. + + Chat database exported + Exportation de la base de données des discussions + No comment provided by engineer. + Chat database imported Base de données du chat importée @@ -1163,8 +1540,14 @@ Le chat est arrêté. Si vous avez déjà utilisé cette base de données sur un autre appareil, vous devez la transférer à nouveau avant de démarrer le chat. No comment provided by engineer. + + Chat list + Liste de discussion + No comment provided by engineer. + Chat migrated! + Messagerie transférée ! No comment provided by engineer. @@ -1172,15 +1555,50 @@ Préférences de chat No comment provided by engineer. + + Chat preferences were changed. + Les préférences de discussion ont été modifiées. + alert message + + + Chat profile + Profil d'utilisateur + No comment provided by engineer. + + + Chat theme + Thème de chat + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + La discussion sera supprimé pour tous les membres - cela ne peut pas être annulé ! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + Le discussion sera supprimé pour vous - il n'est pas possible de revenir en arrière ! + No comment provided by engineer. + Chats Discussions No comment provided by engineer. + + Check messages every 20 min. + Consulter les messages toutes les 20 minutes. + No comment provided by engineer. + + + Check messages when allowed. + Consulter les messages quand c'est possible. + No comment provided by engineer. + Check server address and try again. Vérifiez l'adresse du serveur et réessayez. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1189,6 +1607,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + Choisissez _Transferer depuis un autre appareil_ sur le nouvel appareil et scannez le code QR. No comment provided by engineer. @@ -1201,10 +1620,25 @@ Choisir dans la photothèque No comment provided by engineer. + + Chunks deleted + Chunks supprimés + No comment provided by engineer. + + + Chunks downloaded + Chunks téléchargés + No comment provided by engineer. + + + Chunks uploaded + Chunks téléversés + No comment provided by engineer. + Clear Effacer - No comment provided by engineer. + swipe action Clear conversation @@ -1216,6 +1650,16 @@ Effacer la conversation ? No comment provided by engineer. + + Clear group? + Vider le groupe ? + No comment provided by engineer. + + + Clear or delete group? + Vider ou supprimer le groupe ? + No comment provided by engineer. + Clear private notes? Effacer les notes privées ? @@ -1226,11 +1670,21 @@ Retirer la vérification No comment provided by engineer. - - Colors - Couleurs + + Color chats with the new themes. + Colorez vos discussions avec les nouveaux thèmes. No comment provided by engineer. + + Color mode + Mode de couleur + No comment provided by engineer. + + + Community guidelines violation + Infraction aux règles communautaires + report reason + Compare file Comparer le fichier @@ -1241,11 +1695,56 @@ Comparez les codes de sécurité avec vos contacts. No comment provided by engineer. + + Completed + Complétées + No comment provided by engineer. + + + Conditions accepted on: %@. + Conditions acceptées le : %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + Les conditions sont acceptées pour le(s) opérateur(s) : **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + Les conditions sont déjà acceptées pour ces opérateurs : **%@**. + No comment provided by engineer. + + + Conditions of use + Conditions d'utilisation + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + Les conditions seront acceptées pour le(s) opérateur(s) : **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + Les conditions seront acceptées le : %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + Les conditions seront automatiquement acceptées pour les opérateurs activés le : %@. + No comment provided by engineer. + Configure ICE servers Configurer les serveurs ICE No comment provided by engineer. + + Configure server operators + Configurer les opérateurs de serveur + No comment provided by engineer. + Confirm Confirmer @@ -1256,13 +1755,24 @@ Confirmer le code d'accès No comment provided by engineer. + + Confirm contact deletion? + Confirmer la suppression du contact ? + No comment provided by engineer. + Confirm database upgrades Confirmer la mise à niveau de la base de données No comment provided by engineer. + + Confirm files from unknown servers. + Confirmer les fichiers provenant de serveurs inconnus. + No comment provided by engineer. + Confirm network settings + Confirmer les paramètres réseau No comment provided by engineer. @@ -1277,12 +1787,19 @@ Confirm that you remember database passphrase to migrate it. + Confirmer que vous vous souvenez de la phrase secrète de la base de données pour la transférer. No comment provided by engineer. Confirm upload + Confirmer la transmission No comment provided by engineer. + + Confirmed + Confirmé + token status text + Connect Se connecter @@ -1303,6 +1820,11 @@ Connexion au bureau No comment provided by engineer. + + Connect to your friends faster. + Connectez-vous à vos amis plus rapidement. + No comment provided by engineer. + Connect to yourself? Se connecter à soi-même ? @@ -1342,16 +1864,31 @@ Il s'agit de votre propre lien unique ! Se connecter avec %@ No comment provided by engineer. + + Connected + Connecté + No comment provided by engineer. + Connected desktop Bureau connecté No comment provided by engineer. + + Connected servers + Serveurs connectés + No comment provided by engineer. + Connected to desktop Connecté au bureau No comment provided by engineer. + + Connecting + Connexion + No comment provided by engineer. + Connecting to server… Connexion au serveur… @@ -1362,6 +1899,11 @@ Il s'agit de votre propre lien unique ! Connexion au serveur… (erreur : %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + Connexion au contact, veuillez patienter ou vérifier plus tard ! + No comment provided by engineer. + Connecting to desktop Connexion au bureau @@ -1372,6 +1914,16 @@ Il s'agit de votre propre lien unique ! Connexion No comment provided by engineer. + + Connection and servers status. + État de la connexion et des serveurs. + No comment provided by engineer. + + + Connection blocked + Connexion bloquée + No comment provided by engineer. + Connection error Erreur de connexion @@ -1382,11 +1934,38 @@ Il s'agit de votre propre lien unique ! Erreur de connexion (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + La connexion est bloquée par l'opérateur du serveur : +%@ + No comment provided by engineer. + + + Connection not ready. + La connexion n'est pas prête. + No comment provided by engineer. + + + Connection notifications + Notifications de connexion + No comment provided by engineer. + Connection request sent! Demande de connexion envoyée ! No comment provided by engineer. + + Connection requires encryption renegotiation. + La connexion nécessite une renégociation du cryptage. + No comment provided by engineer. + + + Connection security + Sécurité des connexions + No comment provided by engineer. + Connection terminated Connexion terminée @@ -1397,6 +1976,16 @@ Il s'agit de votre propre lien unique ! Délai de connexion No comment provided by engineer. + + Connection with desktop stopped + La connexion avec le bureau s'est arrêtée + No comment provided by engineer. + + + Connections + Connexions + No comment provided by engineer. + Contact allows Votre contact autorise @@ -1407,6 +1996,11 @@ Il s'agit de votre propre lien unique ! Contact déjà existant No comment provided by engineer. + + Contact deleted! + Contact supprimé ! + No comment provided by engineer. + Contact hidden: Contact masqué : @@ -1417,9 +2011,9 @@ Il s'agit de votre propre lien unique ! Le contact est connecté notification - - Contact is not connected yet! - Le contact n'est pas encore connecté ! + + Contact is deleted. + Le contact est supprimé. No comment provided by engineer. @@ -1432,6 +2026,11 @@ Il s'agit de votre propre lien unique ! Préférences de contact No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + Le contact sera supprimé - il n'est pas possible de revenir en arrière ! + No comment provided by engineer. + Contacts Contacts @@ -1442,21 +2041,41 @@ Il s'agit de votre propre lien unique ! Vos contacts peuvent marquer les messages pour les supprimer ; vous pourrez les consulter. No comment provided by engineer. + + Content violates conditions of use + Le contenu enfreint les conditions d'utilisation + blocking reason + Continue Continuer No comment provided by engineer. + + Conversation deleted! + Conversation supprimée ! + No comment provided by engineer. + Copy Copier - chat item action + No comment provided by engineer. + + + Copy error + Erreur de copie + No comment provided by engineer. Core version: v%@ Version du cœur : v%@ No comment provided by engineer. + + Corner + Coin + No comment provided by engineer. + Correct name to %@? Corriger le nom pour %@ ? @@ -1467,6 +2086,11 @@ Il s'agit de votre propre lien unique ! Créer No comment provided by engineer. + + Create 1-time link + Créer un lien unique + No comment provided by engineer. + Create SimpleX address Créer une adresse SimpleX @@ -1477,11 +2101,6 @@ Il s'agit de votre propre lien unique ! Création de groupes via un profil aléatoire. No comment provided by engineer. - - Create an address to let people connect with you. - Vous pouvez créer une adresse pour permettre aux autres utilisateurs de vous contacter. - No comment provided by engineer. - Create file Créer un fichier @@ -1502,6 +2121,11 @@ Il s'agit de votre propre lien unique ! Créer un lien No comment provided by engineer. + + Create list + Créer une liste + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 Créer un nouveau profil sur [l'application de bureau](https://simplex.chat/downloads/). 💻 @@ -1527,6 +2151,11 @@ Il s'agit de votre propre lien unique ! Créez votre profil No comment provided by engineer. + + Created + Créées + No comment provided by engineer. + Created at Créé à @@ -1537,13 +2166,9 @@ Il s'agit de votre propre lien unique ! Créé à : %@ copied message info - - Created on %@ - Créé le %@ - No comment provided by engineer. - Creating archive link + Création d'un lien d'archive No comment provided by engineer. @@ -1556,11 +2181,21 @@ Il s'agit de votre propre lien unique ! Code d'accès actuel No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + Le texte sur les conditions actuelles n'a pas pu être chargé. Vous pouvez consulter les conditions en cliquant sur ce lien : + No comment provided by engineer. + Current passphrase… Phrase secrète actuelle… No comment provided by engineer. + + Current profile + Profil actuel + No comment provided by engineer. + Currently maximum supported file size is %@. Actuellement, la taille maximale des fichiers supportés est de %@. @@ -1571,11 +2206,26 @@ Il s'agit de votre propre lien unique ! Délai personnalisé No comment provided by engineer. + + Customizable message shape. + Forme des messages personnalisable. + No comment provided by engineer. + + + Customize theme + Personnaliser le thème + No comment provided by engineer. + Dark Sombre No comment provided by engineer. + + Dark mode colors + Couleurs en mode sombre + No comment provided by engineer. + Database ID ID de base de données @@ -1674,6 +2324,11 @@ Il s'agit de votre propre lien unique ! La base de données sera migrée lors du redémarrage de l'app No comment provided by engineer. + + Debug delivery + Livraison de débogage + No comment provided by engineer. + Decentralized Décentralisé @@ -1687,18 +2342,19 @@ Il s'agit de votre propre lien unique ! Delete Supprimer - chat item action + alert action +swipe action + + + Delete %lld messages of members? + Supprimer %lld messages de membres ? + No comment provided by engineer. Delete %lld messages? Supprimer %lld messages ? No comment provided by engineer. - - Delete Contact - Supprimer le contact - No comment provided by engineer. - Delete address Supprimer l'adresse @@ -1724,14 +2380,14 @@ Il s'agit de votre propre lien unique ! Supprimer et en informer le contact No comment provided by engineer. - - Delete archive - Supprimer l'archive + + Delete chat + Supprimer la discussion No comment provided by engineer. - - Delete chat archive? - Supprimer l'archive du chat ? + + Delete chat messages from your device. + Supprimer les messages de chat de votre appareil. No comment provided by engineer. @@ -1744,6 +2400,11 @@ Il s'agit de votre propre lien unique ! Supprimer le profil du chat ? No comment provided by engineer. + + Delete chat? + Supprimer la discussion ? + No comment provided by engineer. + Delete connection Supprimer la connexion @@ -1754,11 +2415,9 @@ Il s'agit de votre propre lien unique ! Supprimer le contact No comment provided by engineer. - - Delete contact? -This cannot be undone! - Supprimer le contact ? -Cette opération ne peut être annulée ! + + Delete contact? + Supprimer le contact ? No comment provided by engineer. @@ -1768,6 +2427,7 @@ Cette opération ne peut être annulée ! Delete database from this device + Supprimer la base de données de cet appareil No comment provided by engineer. @@ -1820,6 +2480,11 @@ Cette opération ne peut être annulée ! Supprimer le lien ? No comment provided by engineer. + + Delete list? + Supprimer la liste ? + alert title + Delete member message? Supprimer le message de ce membre ? @@ -1833,7 +2498,7 @@ Cette opération ne peut être annulée ! Delete messages Supprimer les messages - No comment provided by engineer. + alert button Delete messages after @@ -1850,9 +2515,9 @@ Cette opération ne peut être annulée ! Supprimer l'ancienne base de données ? No comment provided by engineer. - - Delete pending connection - Supprimer la connexion en attente + + Delete or moderate up to 200 messages. + Supprimer ou modérer jusqu'à 200 messages. No comment provided by engineer. @@ -1870,11 +2535,31 @@ Cette opération ne peut être annulée ! Supprimer la file d'attente server test step + + Delete report + Supprimer le rapport + No comment provided by engineer. + + + Delete up to 20 messages at once. + Supprimez jusqu'à 20 messages à la fois. + No comment provided by engineer. + Delete user profile? Supprimer le profil utilisateur ? No comment provided by engineer. + + Delete without notification + Supprimer sans notification + No comment provided by engineer. + + + Deleted + Supprimées + No comment provided by engineer. + Deleted at Supprimé à @@ -1885,6 +2570,16 @@ Cette opération ne peut être annulée ! Supprimé à : %@ copied message info + + Deletion errors + Erreurs de suppression + No comment provided by engineer. + + + Delivered even when Apple drops them. + Distribués même quand Apple les oublie. + No comment provided by engineer. + Delivery Distribution @@ -1897,7 +2592,7 @@ Cette opération ne peut être annulée ! Delivery receipts! - Justificatifs de réception! + Justificatifs de réception ! No comment provided by engineer. @@ -1920,11 +2615,41 @@ Cette opération ne peut être annulée ! Appareils de bureau No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + L'adresse du serveur de destination %@ est incompatible avec les paramètres du serveur de redirection %@. + No comment provided by engineer. + + + Destination server error: %@ + Erreur du serveur de destination : %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + La version du serveur de destination %@ est incompatible avec le serveur de redirection %@. + No comment provided by engineer. + + + Detailed statistics + Statistiques détaillées + No comment provided by engineer. + + + Details + Détails + No comment provided by engineer. + Develop Développer No comment provided by engineer. + + Developer options + Options pour les développeurs + No comment provided by engineer. + Developer tools Outils du développeur @@ -1955,8 +2680,13 @@ Cette opération ne peut être annulée ! Messages directs chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + Les messages directs entre membres sont interdits dans cette discussion. + No comment provided by engineer. + + + Direct messages between members are prohibited. Les messages directs entre membres sont interdits dans ce groupe. No comment provided by engineer. @@ -1970,11 +2700,26 @@ Cette opération ne peut être annulée ! Désactiver SimpleX Lock authentication reason + + Disable automatic message deletion? + Désactiver la suppression automatique des messages ? + alert title + + + Disable delete messages + Désactiver la suppression des messages + alert button + Disable for all Désactiver pour tous No comment provided by engineer. + + Disabled + Désactivé + No comment provided by engineer. + Disappearing message Message éphémère @@ -1990,8 +2735,8 @@ Cette opération ne peut être annulée ! Les messages éphémères sont interdits dans cette discussion. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. Les messages éphémères sont interdits dans ce groupe. No comment provided by engineer. @@ -2025,11 +2770,21 @@ Cette opération ne peut être annulée ! Rechercher sur le réseau No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + Ne pas envoyer de messages directement, même si votre serveur ou le serveur de destination ne prend pas en charge le routage privé. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. N'utilisez PAS SimpleX pour les appels d'urgence. No comment provided by engineer. + + Do NOT use private routing. + Ne pas utiliser de routage privé. + No comment provided by engineer. + Do it later Faites-le plus tard @@ -2040,6 +2795,16 @@ Cette opération ne peut être annulée ! Ne pas envoyer d'historique aux nouveaux membres. No comment provided by engineer. + + Do not use credentials with proxy. + Ne pas utiliser d'identifiants avec le proxy. + No comment provided by engineer. + + + Documents: + Documents: + No comment provided by engineer. + Don't create address Ne pas créer d'adresse @@ -2050,18 +2815,40 @@ Cette opération ne peut être annulée ! Ne pas activer No comment provided by engineer. + + Don't miss important messages. + Ne manquez pas les messages importants. + No comment provided by engineer. + Don't show again Ne plus afficher No comment provided by engineer. + + Done + Terminé + No comment provided by engineer. + Downgrade and open chat Rétrograder et ouvrir le chat No comment provided by engineer. + + Download + Télécharger + alert button +chat item action + + + Download errors + Erreurs de téléchargement + No comment provided by engineer. + Download failed + Échec du téléchargement No comment provided by engineer. @@ -2069,12 +2856,29 @@ Cette opération ne peut être annulée ! Télécharger le fichier server test step + + Download files + Télécharger les fichiers + alert action + + + Downloaded + Téléchargé + No comment provided by engineer. + + + Downloaded files + Fichiers téléchargés + No comment provided by engineer. + Downloading archive + Téléchargement de l'archive No comment provided by engineer. Downloading link details + Téléchargement des détails du lien No comment provided by engineer. @@ -2087,6 +2891,11 @@ Cette opération ne peut être annulée ! Durée No comment provided by engineer. + + E2E encrypted notifications. + Notifications chiffrées E2E. + No comment provided by engineer. + Edit Modifier @@ -2107,6 +2916,11 @@ Cette opération ne peut être annulée ! Activer (conserver les remplacements) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + Activez Flux dans les paramètres du réseau et des serveurs pour une meilleure confidentialité des métadonnées. + No comment provided by engineer. + Enable SimpleX Lock Activer SimpleX Lock @@ -2120,7 +2934,7 @@ Cette opération ne peut être annulée ! Enable automatic message deletion? Activer la suppression automatique des messages ? - No comment provided by engineer. + alert title Enable camera access @@ -2134,6 +2948,7 @@ Cette opération ne peut être annulée ! Enable in direct chats (BETA)! + Activer dans les conversations directes (BETA) ! No comment provided by engineer. @@ -2166,6 +2981,16 @@ Cette opération ne peut être annulée ! Activer le code d'autodestruction set passcode view + + Enabled + Activé + No comment provided by engineer. + + + Enabled for + Activé pour + No comment provided by engineer. + Encrypt Chiffrer @@ -2236,6 +3061,11 @@ Cette opération ne peut être annulée ! La renégociation du chiffrement a échoué. No comment provided by engineer. + + Encryption renegotiation in progress. + Renégociation du chiffrement en cours. + No comment provided by engineer. + Enter Passcode Entrer le code d'accès @@ -2253,6 +3083,7 @@ Cette opération ne peut être annulée ! Enter passphrase + Entrer la phrase secrète No comment provided by engineer. @@ -2300,30 +3131,36 @@ Cette opération ne peut être annulée ! Erreur lors de l'annulation du changement d'adresse No comment provided by engineer. + + Error accepting conditions + Erreur lors de la validation des conditions + alert title + Error accepting contact request Erreur de validation de la demande de contact No comment provided by engineer. - - Error accessing database file - Erreur d'accès au fichier de la base de données - No comment provided by engineer. - Error adding member(s) Erreur lors de l'ajout de membre·s No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + Erreur lors de l'ajout du serveur + alert title Error changing address Erreur de changement d'adresse No comment provided by engineer. + + Error changing connection profile + Erreur lors du changement de profil de connexion + No comment provided by engineer. + Error changing role Erreur lors du changement de rôle @@ -2334,6 +3171,21 @@ Cette opération ne peut être annulée ! Erreur de changement de paramètre No comment provided by engineer. + + Error changing to incognito! + Erreur lors du passage en mode incognito ! + No comment provided by engineer. + + + Error checking token status + Erreur lors de la vérification de l'état du jeton (token) + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + Erreur de connexion au serveur de redirection %@. Veuillez réessayer plus tard. + No comment provided by engineer. + Error creating address Erreur lors de la création de l'adresse @@ -2349,6 +3201,11 @@ Cette opération ne peut être annulée ! Erreur lors de la création du lien du groupe No comment provided by engineer. + + Error creating list + Erreur lors de la création de la liste + alert title + Error creating member contact Erreur lors de la création du contact du membre @@ -2364,6 +3221,11 @@ Cette opération ne peut être annulée ! Erreur lors de la création du profil ! No comment provided by engineer. + + Error creating report + Erreur lors de la création du rapport + No comment provided by engineer. + Error decrypting file Erreur lors du déchiffrement du fichier @@ -2384,11 +3246,6 @@ Cette opération ne peut être annulée ! Erreur lors de la suppression de la connexion No comment provided by engineer. - - Error deleting contact - Erreur lors de la suppression du contact - No comment provided by engineer. - Error deleting database Erreur lors de la suppression de la base de données @@ -2411,6 +3268,7 @@ Cette opération ne peut être annulée ! Error downloading the archive + Erreur lors du téléchargement de l'archive No comment provided by engineer. @@ -2433,6 +3291,11 @@ Cette opération ne peut être annulée ! Erreur lors de l'exportation de la base de données du chat No comment provided by engineer. + + Error exporting theme: %@ + Erreur d'exportation du thème : %@ + No comment provided by engineer. + Error importing chat database Erreur lors de l'importation de la base de données du chat @@ -2443,9 +3306,14 @@ Cette opération ne peut être annulée ! Erreur lors de la liaison avec le groupe No comment provided by engineer. - - Error loading %@ servers - Erreur lors du chargement des serveurs %@ + + Error loading servers + Erreur de chargement des serveurs + alert title + + + Error migrating settings + Erreur lors de la migration des paramètres No comment provided by engineer. @@ -2456,16 +3324,36 @@ Cette opération ne peut être annulée ! Error receiving file Erreur lors de la réception du fichier + alert title + + + Error reconnecting server + Erreur de reconnexion du serveur No comment provided by engineer. + + Error reconnecting servers + Erreur de reconnexion des serveurs + No comment provided by engineer. + + + Error registering for notifications + Erreur lors de l'inscription aux notifications + alert title + Error removing member Erreur lors de la suppression d'un membre No comment provided by engineer. - - Error saving %@ servers - Erreur lors de la sauvegarde des serveurs %@ + + Error reordering lists + Erreur lors de la réorganisation des listes + alert title + + + Error resetting statistics + Erreur de réinitialisation des statistiques No comment provided by engineer. @@ -2473,6 +3361,11 @@ Cette opération ne peut être annulée ! Erreur lors de la sauvegarde des serveurs ICE No comment provided by engineer. + + Error saving chat list + Erreur lors de l'enregistrement de la liste des chats + alert title + Error saving group profile Erreur lors de la sauvegarde du profil de groupe @@ -2488,8 +3381,14 @@ Cette opération ne peut être annulée ! Erreur lors de l'enregistrement de la phrase de passe dans la keychain No comment provided by engineer. + + Error saving servers + Erreur d'enregistrement des serveurs + alert title + Error saving settings + Erreur lors de l'enregistrement des paramètres when migrating @@ -2532,16 +3431,26 @@ Cette opération ne peut être annulée ! Erreur lors de l'arrêt du chat No comment provided by engineer. + + Error switching profile + Erreur lors du changement de profil + No comment provided by engineer. + Error switching profile! Erreur lors du changement de profil ! - No comment provided by engineer. + alertTitle Error synchronizing connection Erreur de synchronisation de connexion No comment provided by engineer. + + Error testing server connection + Erreur lors du test de connexion au serveur + No comment provided by engineer. + Error updating group link Erreur lors de la mise à jour du lien de groupe @@ -2552,6 +3461,11 @@ Cette opération ne peut être annulée ! Erreur lors de la mise à jour du message No comment provided by engineer. + + Error updating server + Erreur de mise à jour du serveur + alert title + Error updating settings Erreur lors de la mise à jour des paramètres @@ -2564,10 +3478,12 @@ Cette opération ne peut être annulée ! Error uploading the archive + Erreur lors de l'envoi de l'archive No comment provided by engineer. Error verifying passphrase: + Erreur lors de la vérification de la phrase secrète : No comment provided by engineer. @@ -2578,7 +3494,9 @@ Cette opération ne peut être annulée ! Error: %@ Erreur : %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2590,6 +3508,16 @@ Cette opération ne peut être annulée ! Erreur : pas de fichier de base de données No comment provided by engineer. + + Errors + Erreurs + No comment provided by engineer. + + + Errors in servers configuration. + Erreurs dans la configuration des serveurs. + servers error + Even when disabled in the conversation. Même s'il est désactivé dans la conversation. @@ -2605,6 +3533,11 @@ Cette opération ne peut être annulée ! Étendre chat item action + + Expired + Expiré + token status text + Export database Exporter la base de données @@ -2615,6 +3548,11 @@ Cette opération ne peut être annulée ! Erreur lors de l'exportation : No comment provided by engineer. + + Export theme + Exporter le thème + No comment provided by engineer. + Exported database archive. Archive de la base de données exportée. @@ -2622,6 +3560,7 @@ Cette opération ne peut être annulée ! Exported file doesn't exist + Le fichier exporté n'existe pas No comment provided by engineer. @@ -2639,16 +3578,70 @@ Cette opération ne peut être annulée ! Rapide et ne nécessitant pas d'attendre que l'expéditeur soit en ligne ! No comment provided by engineer. + + Faster deletion of groups. + Suppression plus rapide des groupes. + No comment provided by engineer. + Faster joining and more reliable messages. Connexion plus rapide et messages plus fiables. No comment provided by engineer. + + Faster sending messages. + Envoi plus rapide des messages. + No comment provided by engineer. + Favorite Favoris + swipe action + + + Favorites + Favoris No comment provided by engineer. + + File error + Erreur de fichier + file error alert title + + + File errors: +%@ + Erreurs de fichier : +%@ + alert message + + + File is blocked by server operator: +%@. + Le fichier est bloqué par l'opérateur du serveur : +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + Fichier introuvable - le fichier a probablement été supprimé ou annulé. + file error text + + + File server error: %@ + Erreur de serveur de fichiers : %@ + file error text + + + File status + Statut du fichier + No comment provided by engineer. + + + File status: %@ + Statut du fichier : %@ + copied message info + File will be deleted from servers. Le fichier sera supprimé des serveurs. @@ -2669,6 +3662,11 @@ Cette opération ne peut être annulée ! Fichier : %@ No comment provided by engineer. + + Files + Fichiers + No comment provided by engineer. + Files & media Fichiers & médias @@ -2679,11 +3677,16 @@ Cette opération ne peut être annulée ! Fichiers et médias chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. Les fichiers et les médias sont interdits dans ce groupe. No comment provided by engineer. + + Files and media not allowed + Fichiers et médias non autorisés + No comment provided by engineer. + Files and media prohibited! Fichiers et médias interdits ! @@ -2696,10 +3699,12 @@ Cette opération ne peut être annulée ! Finalize migration + Finaliser le transfert No comment provided by engineer. Finalize migration on another device. + Finalisez le transfert sur l'autre appareil. No comment provided by engineer. @@ -2724,7 +3729,7 @@ Cette opération ne peut être annulée ! Fix connection? - Réparer la connexion? + Réparer la connexion ? No comment provided by engineer. @@ -2742,11 +3747,113 @@ Cette opération ne peut être annulée ! Correction non prise en charge par un membre du groupe No comment provided by engineer. + + For all moderators + No comment provided by engineer. + + + For chat profile %@: + Pour le profil de discussion %@ : + servers error + For console Pour la console No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + Par exemple, si votre contact reçoit des messages via un serveur SimpleX Chat, votre application les transmettra via un serveur Flux. + No comment provided by engineer. + + + For me + No comment provided by engineer. + + + For private routing + Pour le routage privé + No comment provided by engineer. + + + For social media + Pour les réseaux sociaux + No comment provided by engineer. + + + Forward + Transférer + chat item action + + + Forward %d message(s)? + Transférer %d message(s) ? + alert title + + + Forward and save messages + Transférer et sauvegarder des messages + No comment provided by engineer. + + + Forward messages + Transférer les messages + alert action + + + Forward messages without files? + Transférer les messages sans les fichiers ? + alert message + + + Forward up to 20 messages at once. + Transférez jusqu'à 20 messages à la fois. + No comment provided by engineer. + + + Forwarded + Transféré + No comment provided by engineer. + + + Forwarded from + Transféré depuis + No comment provided by engineer. + + + Forwarding %lld messages + Transfert des %lld messages + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + Le serveur de redirection %@ n'a pas réussi à se connecter au serveur de destination %@. Veuillez réessayer plus tard. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + L'adresse du serveur de redirection est incompatible avec les paramètres du réseau : %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + La version du serveur de redirection est incompatible avec les paramètres du réseau : %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + Serveur de transfert : %1$@ +Erreur du serveur de destination : %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + Serveur de transfert : %1$@ +Erreur : %2$@ + snd error text + Found desktop Bureau trouvé @@ -2767,11 +3874,6 @@ Cette opération ne peut être annulée ! Nom complet (optionnel) No comment provided by engineer. - - Full name: - Nom complet : - No comment provided by engineer. - Fully decentralized – visible only to members. Entièrement décentralisé – visible que par ses membres. @@ -2792,6 +3894,20 @@ Cette opération ne peut être annulée ! GIFs et stickers No comment provided by engineer. + + Get notified when mentioned. + No comment provided by engineer. + + + Good afternoon! + Bonjour ! + message preview + + + Good morning! + Bonjour ! + message preview + Group Groupe @@ -2847,36 +3963,6 @@ Cette opération ne peut être annulée ! Liens de groupe No comment provided by engineer. - - Group members can add message reactions. - Les membres du groupe peuvent ajouter des réactions aux messages. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Les membres du groupe peuvent supprimer de manière irréversible les messages envoyés. (24 heures) - No comment provided by engineer. - - - Group members can send direct messages. - Les membres du groupe peuvent envoyer des messages directs. - No comment provided by engineer. - - - Group members can send disappearing messages. - Les membres du groupes peuvent envoyer des messages éphémères. - No comment provided by engineer. - - - Group members can send files and media. - Les membres du groupe peuvent envoyer des fichiers et des médias. - No comment provided by engineer. - - - Group members can send voice messages. - Les membres du groupe peuvent envoyer des messages vocaux. - No comment provided by engineer. - Group message: Message du groupe : @@ -2917,11 +4003,19 @@ Cette opération ne peut être annulée ! Le groupe va être supprimé pour vous - impossible de revenir en arrière ! No comment provided by engineer. + + Groups + No comment provided by engineer. + Help Aide No comment provided by engineer. + + Help admins moderating their groups. + No comment provided by engineer. + Hidden Caché @@ -2972,10 +4066,19 @@ Cette opération ne peut être annulée ! Comment SimpleX fonctionne No comment provided by engineer. + + How it affects privacy + L'impact sur la vie privée + No comment provided by engineer. + + + How it helps privacy + Comment il contribue à la protection de la vie privée + No comment provided by engineer. + How it works - Comment ça fonctionne - No comment provided by engineer. + alert button How to @@ -2994,6 +4097,7 @@ Cette opération ne peut être annulée ! Hungarian interface + Interface en hongrois No comment provided by engineer. @@ -3001,6 +4105,11 @@ Cette opération ne peut être annulée ! Serveurs ICE (un par ligne) No comment provided by engineer. + + IP address + Adresse IP + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Si vous ne pouvez pas vous rencontrer en personne, montrez le code QR lors d'un appel vidéo ou partagez le lien. @@ -3041,8 +4150,8 @@ Cette opération ne peut être annulée ! Immédiatement No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Protégé du spam et des abus No comment provided by engineer. @@ -3063,10 +4172,24 @@ Cette opération ne peut être annulée ! Import failed + Échec de l'importation + No comment provided by engineer. + + + Import theme + Importer un thème No comment provided by engineer. Importing archive + Importation de l'archive + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Amélioration de la distribution, réduction de l'utilisation du trafic. +D'autres améliorations sont à venir ! No comment provided by engineer. @@ -3086,6 +4209,7 @@ Cette opération ne peut être annulée ! In order to continue, chat should be stopped. + Pour continuer, le chat doit être interrompu. No comment provided by engineer. @@ -3093,6 +4217,19 @@ Cette opération ne peut être annulée ! En réponse à No comment provided by engineer. + + In-call sounds + Sons d'appel + No comment provided by engineer. + + + Inappropriate content + report reason + + + Inappropriate profile + report reason + Incognito Incognito @@ -3163,6 +4300,11 @@ Cette opération ne peut être annulée ! Installer [SimpleX Chat pour terminal](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Instantané + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3170,16 +4312,36 @@ Cette opération ne peut être annulée ! No comment provided by engineer. - - Instantly - Instantané - No comment provided by engineer. - Interface Interface No comment provided by engineer. + + Interface colors + Couleurs d'interface + No comment provided by engineer. + + + Invalid + token status text + + + Invalid (bad token) + token status text + + + Invalid (expired) + token status text + + + Invalid (unregistered) + token status text + + + Invalid (wrong topic) + token status text + Invalid QR code Code QR invalide @@ -3202,6 +4364,7 @@ Cette opération ne peut être annulée ! Invalid migration confirmation + Confirmation de migration invalide No comment provided by engineer. @@ -3217,7 +4380,7 @@ Cette opération ne peut être annulée ! Invalid server address! Adresse de serveur invalide ! - No comment provided by engineer. + alert title Invalid status @@ -3239,6 +4402,11 @@ Cette opération ne peut être annulée ! Inviter des membres No comment provided by engineer. + + Invite to chat + Inviter à discuter + No comment provided by engineer. + Invite to group Inviter au groupe @@ -3254,8 +4422,8 @@ Cette opération ne peut être annulée ! La suppression irréversible de message est interdite dans ce chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. La suppression irréversible de messages est interdite dans ce groupe. No comment provided by engineer. @@ -3280,6 +4448,11 @@ Cette opération ne peut être annulée ! 3. La connexion a été compromise. No comment provided by engineer. + + It protects your IP address and connections. + Il protège votre adresse IP et vos connexions. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Il semblerait que vous êtes déjà connecté via ce lien. Si ce n'est pas le cas, il y a eu une erreur (%@). @@ -3298,7 +4471,7 @@ Cette opération ne peut être annulée ! Join Rejoindre - No comment provided by engineer. + swipe action Join group @@ -3340,6 +4513,11 @@ Voici votre lien pour le groupe %@ ! Keep Conserver + alert action + + + Keep conversation + Garder la conversation No comment provided by engineer. @@ -3350,7 +4528,7 @@ Voici votre lien pour le groupe %@ ! Keep unused invitation? Conserver l'invitation inutilisée ? - No comment provided by engineer. + alert title Keep your connections @@ -3385,6 +4563,16 @@ Voici votre lien pour le groupe %@ ! Leave Quitter + swipe action + + + Leave chat + Quitter la discussion + No comment provided by engineer. + + + Leave chat? + Quitter la discussion ? No comment provided by engineer. @@ -3427,6 +4615,18 @@ Voici votre lien pour le groupe %@ ! Bureaux liés No comment provided by engineer. + + List + swipe action + + + List name and emoji should be different for all lists. + No comment provided by engineer. + + + List name... + No comment provided by engineer. + Live message! Message dynamique ! @@ -3437,11 +4637,6 @@ Voici votre lien pour le groupe %@ ! Messages dynamiques No comment provided by engineer. - - Local - Local - No comment provided by engineer. - Local name Nom local @@ -3462,11 +4657,6 @@ Voici votre lien pour le groupe %@ ! Mode de verrouillage No comment provided by engineer. - - Make a private connection - Établir une connexion privée - No comment provided by engineer. - Make one message disappear Rendre un message éphémère @@ -3477,21 +4667,11 @@ Voici votre lien pour le groupe %@ ! Rendre un profil privé ! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Assurez-vous que les adresses des serveurs %@ sont au bon format et ne sont pas dupliquées, un par ligne (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Assurez-vous que les adresses des serveurs WebRTC ICE sont au bon format et ne sont pas dupliquées, un par ligne. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Beaucoup se demandent : *si SimpleX n'a pas d'identifiant d'utilisateur, comment peut-il délivrer des messages ?* - No comment provided by engineer. - Mark deleted for everyone Marquer comme supprimé pour tout le monde @@ -3517,11 +4697,35 @@ Voici votre lien pour le groupe %@ ! Max 30 secondes, réception immédiate. No comment provided by engineer. + + Media & file servers + Serveurs de fichiers et de médias + No comment provided by engineer. + + + Medium + Modéré + blur media + Member Membre No comment provided by engineer. + + Member inactive + Membre inactif + item status text + + + Member reports + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + Le rôle du membre sera modifié pour « %@ ». Tous les membres du chat seront notifiés. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. Le rôle du membre sera changé pour "%@". Tous les membres du groupe en seront informés. @@ -3532,11 +4736,64 @@ Voici votre lien pour le groupe %@ ! Le rôle du membre sera changé pour "%@". Ce membre recevra une nouvelle invitation. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + Le membre sera retiré de la discussion - cela ne peut pas être annulé ! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! Ce membre sera retiré du groupe - impossible de revenir en arrière ! No comment provided by engineer. + + Members can add message reactions. + Les membres du groupe peuvent ajouter des réactions aux messages. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + Les membres du groupe peuvent supprimer de manière irréversible les messages envoyés. (24 heures) + No comment provided by engineer. + + + Members can report messsages to moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + Les membres du groupe peuvent envoyer des liens SimpleX. + No comment provided by engineer. + + + Members can send direct messages. + Les membres du groupe peuvent envoyer des messages directs. + No comment provided by engineer. + + + Members can send disappearing messages. + Les membres du groupes peuvent envoyer des messages éphémères. + No comment provided by engineer. + + + Members can send files and media. + Les membres du groupe peuvent envoyer des fichiers et des médias. + No comment provided by engineer. + + + Members can send voice messages. + Les membres du groupe peuvent envoyer des messages vocaux. + No comment provided by engineer. + + + Mention members 👋 + No comment provided by engineer. + + + Menus + Menus + No comment provided by engineer. + Message delivery error Erreur de distribution du message @@ -3547,11 +4804,31 @@ Voici votre lien pour le groupe %@ ! Accusés de réception des messages ! No comment provided by engineer. + + Message delivery warning + Avertissement sur la distribution des messages + item status text + Message draft Brouillon de message No comment provided by engineer. + + Message forwarded + Message transféré + item status text + + + Message may be delivered later if member becomes active. + Le message peut être transmis plus tard si le membre devient actif. + item status description + + + Message queue info + Informations sur la file d'attente des messages + No comment provided by engineer. + Message reactions Réactions aux messages @@ -3562,11 +4839,41 @@ Voici votre lien pour le groupe %@ ! Les réactions aux messages sont interdites dans ce chat. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. Les réactions aux messages sont interdites dans ce groupe. No comment provided by engineer. + + Message reception + Réception de message + No comment provided by engineer. + + + Message servers + Serveurs de messages + No comment provided by engineer. + + + Message shape + Forme du message + No comment provided by engineer. + + + Message source remains private. + La source du message reste privée. + No comment provided by engineer. + + + Message status + Statut du message + No comment provided by engineer. + + + Message status: %@ + Statut du message : %@ + copied message info + Message text Texte du message @@ -3574,6 +4881,7 @@ Voici votre lien pour le groupe %@ ! Message too large + Message trop volumineux No comment provided by engineer. @@ -3591,36 +4899,63 @@ Voici votre lien pour le groupe %@ ! Les messages de %@ seront affichés ! No comment provided by engineer. + + Messages in this chat will never be deleted. + alert message + + + Messages received + Messages reçus + No comment provided by engineer. + + + Messages sent + Messages envoyés + No comment provided by engineer. + + + Messages were deleted after you selected them. + Les messages ont été supprimés après avoir été sélectionnés. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + Les messages, fichiers et appels sont protégés par un chiffrement **de bout en bout** avec une confidentialité persistante, une répudiation et une récupération en cas d'effraction. No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + Les messages, fichiers et appels sont protégés par un chiffrement **e2e résistant post-quantique** avec une confidentialité persistante, une répudiation et une récupération en cas d'effraction. No comment provided by engineer. Migrate device + Transférer l'appareil No comment provided by engineer. Migrate from another device + Transférer depuis un autre appareil No comment provided by engineer. Migrate here + Transférer ici No comment provided by engineer. Migrate to another device + Transférer vers un autre appareil No comment provided by engineer. Migrate to another device via QR code. + Transférer vers un autre appareil via un code QR. No comment provided by engineer. Migrating + Transfert No comment provided by engineer. @@ -3630,6 +4965,7 @@ Voici votre lien pour le groupe %@ ! Migration complete + Transfert terminé No comment provided by engineer. @@ -3647,9 +4983,9 @@ Voici votre lien pour le groupe %@ ! La migration est terminée No comment provided by engineer. - - Migrations: %@ - Migrations : %@ + + Migrations: + Migrations : No comment provided by engineer. @@ -3667,21 +5003,30 @@ Voici votre lien pour le groupe %@ ! Modéré à : %@ copied message info + + More + swipe action + More improvements are coming soon! Plus d'améliorations à venir ! No comment provided by engineer. + + More reliable network connection. + Connexion réseau plus fiable. + No comment provided by engineer. + + + More reliable notifications + Notifications plus fiables + No comment provided by engineer. + Most likely this connection is deleted. Connexion probablement supprimée. item status description - - Most likely this contact has deleted the connection with you. - Il est fort probable que ce contact ait supprimé la connexion avec vous. - No comment provided by engineer. - Multiple chat profiles Différents profils de chat @@ -3690,7 +5035,11 @@ Voici votre lien pour le groupe %@ ! Mute Muet - No comment provided by engineer. + notification label action + + + Mute all + notification label action Muted when inactive! @@ -3700,13 +5049,38 @@ Voici votre lien pour le groupe %@ ! Name Nom - No comment provided by engineer. + swipe action Network & servers Réseau et serveurs No comment provided by engineer. + + Network connection + Connexion au réseau + No comment provided by engineer. + + + Network decentralization + Décentralisation du réseau + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + Problèmes de réseau - le message a expiré après plusieurs tentatives d'envoi. + snd error text + + + Network management + Gestion du réseau + No comment provided by engineer. + + + Network operator + Opérateur de réseau + No comment provided by engineer. + Network settings Paramètres réseau @@ -3717,16 +5091,35 @@ Voici votre lien pour le groupe %@ ! État du réseau No comment provided by engineer. + + New + token status text + New Passcode Nouveau code d'accès No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + De nouveaux identifiants SOCKS seront utilisés chaque fois que vous démarrerez l'application. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + De nouveaux identifiants SOCKS seront utilisées pour chaque serveur. + No comment provided by engineer. + New chat Nouveau chat No comment provided by engineer. + + New chat experience 🎉 + Nouvelle expérience de discussion 🎉 + No comment provided by engineer. + New contact request Nouvelle demande de contact @@ -3737,11 +5130,6 @@ Voici votre lien pour le groupe %@ ! Nouveau contact : notification - - New database archive - Nouvelle archive de base de données - No comment provided by engineer. - New desktop app! Nouvelle application de bureau ! @@ -3752,11 +5140,21 @@ Voici votre lien pour le groupe %@ ! Nouveau nom d'affichage No comment provided by engineer. + + New events + Nouveaux événements + notification + New in %@ Nouveautés de la %@ No comment provided by engineer. + + New media options + Nouvelles options de médias + No comment provided by engineer. + New member role Nouveau rôle @@ -3772,6 +5170,11 @@ Voici votre lien pour le groupe %@ ! Nouvelle phrase secrète… No comment provided by engineer. + + New server + Nouveau serveur + No comment provided by engineer. + No Non @@ -3782,6 +5185,18 @@ Voici votre lien pour le groupe %@ ! Pas de mot de passe pour l'app Authentication unavailable + + No chats + No comment provided by engineer. + + + No chats found + No comment provided by engineer. + + + No chats in list %@ + No comment provided by engineer. + No contacts selected Aucun contact sélectionné @@ -3802,6 +5217,11 @@ Voici votre lien pour le groupe %@ ! Pas de token d'appareil ! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + Pas de connexion directe pour l'instant, le message est transmis par l'administrateur. + item status description + No filtered chats Aucune discussion filtrés @@ -3817,21 +5237,107 @@ Voici votre lien pour le groupe %@ ! Aucun historique No comment provided by engineer. + + No info, try to reload + Pas d'info, essayez de recharger + No comment provided by engineer. + + + No media & file servers. + Pas de serveurs de médias et de fichiers. + servers error + + + No message + No comment provided by engineer. + + + No message servers. + Pas de serveurs de messages. + servers error + + + No network connection + Pas de connexion au réseau + No comment provided by engineer. + + + No permission to record speech + Enregistrement des conversations non autorisé + No comment provided by engineer. + + + No permission to record video + Enregistrement de la vidéo non autorisé + No comment provided by engineer. + No permission to record voice message Pas l'autorisation d'enregistrer un message vocal No comment provided by engineer. + + No push server + No push server + No comment provided by engineer. + No received or sent files Aucun fichier reçu ou envoyé No comment provided by engineer. + + No servers for private message routing. + Pas de serveurs pour le routage privé des messages. + servers error + + + No servers to receive files. + Pas de serveurs pour recevoir des fichiers. + servers error + + + No servers to receive messages. + Pas de serveurs pour recevoir des messages. + servers error + + + No servers to send files. + Pas de serveurs pour envoyer des fichiers. + servers error + + + No token! + alert title + + + No unread chats + No comment provided by engineer. + + + No user identifiers. + Aucun identifiant d'utilisateur. + No comment provided by engineer. + Not compatible! Non compatible ! No comment provided by engineer. + + Notes + No comment provided by engineer. + + + Nothing selected + Aucune sélection + No comment provided by engineer. + + + Nothing to forward! + Rien à transférer ! + alert title + Notifications Notifications @@ -3842,6 +5348,19 @@ Voici votre lien pour le groupe %@ ! Les notifications sont désactivées ! No comment provided by engineer. + + Notifications error + alert title + + + Notifications privacy + Notifications sécurisées + No comment provided by engineer. + + + Notifications status + alert title + Now admins can: - delete members' messages. @@ -3859,36 +5378,35 @@ Voici votre lien pour le groupe %@ ! Off Off - No comment provided by engineer. + blur media Ok Ok - No comment provided by engineer. + alert button Old database Ancienne base de données No comment provided by engineer. - - Old database archive - Archives de l'ancienne base de données - No comment provided by engineer. - One-time invitation link Lien d'invitation unique No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Les hôtes .onion seront nécessaires pour la connexion. Nécessite l'activation d'un VPN. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Les hôtes .onion seront **nécessaires** pour la connexion. +Nécessite l'activation d'un VPN. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Les hôtes .onion seront utilisés dès que possible. Nécessite l'activation d'un VPN. + + Onion hosts will be used when available. +Requires compatible VPN. + Les hôtes .onion seront utilisés dès que possible. +Nécessite l'activation d'un VPN. No comment provided by engineer. @@ -3896,11 +5414,21 @@ Voici votre lien pour le groupe %@ ! Les hôtes .onion ne seront pas utilisés. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + Seuls les propriétaires peuvent modifier les préférences. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. Seuls les appareils clients stockent les profils des utilisateurs, les contacts, les groupes et les messages envoyés avec un **chiffrement de bout en bout à deux couches**. No comment provided by engineer. + + Only delete conversation + Ne supprimer que la conversation + No comment provided by engineer. + Only group owners can change group preferences. Seuls les propriétaires du groupe peuvent modifier les préférences du groupe. @@ -3916,6 +5444,14 @@ Voici votre lien pour le groupe %@ ! Seuls les propriétaires de groupes peuvent activer les messages vocaux. No comment provided by engineer. + + Only sender and moderators see it + No comment provided by engineer. + + + Only you and moderators see it + No comment provided by engineer. + Only you can add message reactions. Vous seul pouvez ajouter des réactions aux messages. @@ -3969,13 +5505,18 @@ Voici votre lien pour le groupe %@ ! Open Ouvrir - No comment provided by engineer. + alert action Open Settings Ouvrir les Paramètres No comment provided by engineer. + + Open changes + Ouvrir les modifications + No comment provided by engineer. + Open chat Ouvrir le chat @@ -3986,32 +5527,48 @@ Voici votre lien pour le groupe %@ ! Ouvrir la console du chat authentication reason + + Open conditions + Ouvrir les conditions + No comment provided by engineer. + Open group Ouvrir le groupe No comment provided by engineer. + + Open link? + alert title + Open migration to another device + Ouvrir le transfert vers un autre appareil authentication reason - - Open user profiles - Ouvrir les profils d'utilisateurs - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Protocole et code open-source – n'importe qui peut heberger un serveur. - No comment provided by engineer. - Opening app… Ouverture de l'app… No comment provided by engineer. + + Operator + Opérateur + No comment provided by engineer. + + + Operator server + Serveur de l'opérateur + alert title + + + Or import archive file + Ou importer un fichier d'archive + No comment provided by engineer. + Or paste archive link + Ou coller le lien de l'archive No comment provided by engineer. @@ -4021,13 +5578,35 @@ Voici votre lien pour le groupe %@ ! Or securely share this file link + Ou partagez en toute sécurité le lien de ce fichier No comment provided by engineer. Or show this code - Ou présenter ce code + Ou montrez ce code No comment provided by engineer. + + Or to share privately + Ou à partager en privé + No comment provided by engineer. + + + Organize chats into lists + No comment provided by engineer. + + + Other + Autres + No comment provided by engineer. + + + Other file errors: +%@ + Autres erreurs de fichiers : +%@ + alert message + PING count Nombre de PING @@ -4063,6 +5642,11 @@ Voici votre lien pour le groupe %@ ! Code d'accès défini ! No comment provided by engineer. + + Password + Mot de passe + No comment provided by engineer. + Password to show Mot de passe à entrer @@ -4093,13 +5677,13 @@ Voici votre lien pour le groupe %@ ! Collez le lien que vous avez reçu No comment provided by engineer. - - People can connect to you only via the links you share. - On ne peut se connecter à vous qu’avec les liens que vous partagez. + + Pending + En attente No comment provided by engineer. - - Periodically + + Periodic Périodique No comment provided by engineer. @@ -4110,6 +5694,17 @@ Voici votre lien pour le groupe %@ ! Picture-in-picture calls + Appels picture-in-picture + No comment provided by engineer. + + + Play from the chat list. + Aperçu depuis la liste de conversation. + No comment provided by engineer. + + + Please ask your contact to enable calls. + Veuillez demander à votre contact d'autoriser les appels. No comment provided by engineer. @@ -4117,6 +5712,13 @@ Voici votre lien pour le groupe %@ ! Veuillez demander à votre contact de permettre l'envoi de messages vocaux. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + Veuillez vérifier que le téléphone portable et l'ordinateur de bureau sont connectés au même réseau local et que le pare-feu de l'ordinateur de bureau autorise la connexion. +Veuillez faire part de tout autre problème aux développeurs. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Veuillez vérifier que vous avez utilisé le bon lien ou demandez à votre contact de vous en envoyer un autre. @@ -4134,6 +5736,7 @@ Voici votre lien pour le groupe %@ ! Please confirm that network settings are correct for this device. + Veuillez confirmer que les paramètres réseau de cet appareil sont corrects. No comment provided by engineer. @@ -4183,60 +5786,115 @@ Erreur : %@ Veuillez conserver votre phrase secrète en lieu sûr, vous NE pourrez PAS la changer si vous la perdez. No comment provided by engineer. + + Please try to disable and re-enable notfications. + token info + + + Please wait for token activation to complete. + token info + + + Please wait for token to be registered. + token info + Polish interface Interface en polonais No comment provided by engineer. + + Port + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Il est possible que l'empreinte du certificat dans l'adresse du serveur soit incorrecte server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Conserver le brouillon du dernier message, avec les pièces jointes. No comment provided by engineer. - - Preset server - Serveur prédéfini - No comment provided by engineer. - Preset server address Adresse du serveur prédéfinie No comment provided by engineer. + + Preset servers + Serveurs prédéfinis + No comment provided by engineer. + Preview Aperçu No comment provided by engineer. + + Previously connected servers + Serveurs précédemment connectés + No comment provided by engineer. + Privacy & security Vie privée et sécurité No comment provided by engineer. + + Privacy for your customers. + Respect de la vie privée de vos clients. + No comment provided by engineer. + + + Privacy policy and conditions of use. + No comment provided by engineer. + Privacy redefined La vie privée redéfinie No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + No comment provided by engineer. + Private filenames Noms de fichiers privés No comment provided by engineer. + + Private media file names. + No comment provided by engineer. + + + Private message routing + Routage privé des messages + No comment provided by engineer. + + + Private message routing 🚀 + Routage privé des messages 🚀 + No comment provided by engineer. + Private notes Notes privées name of notes to self + + Private routing + Routage privé + No comment provided by engineer. + + + Private routing error + Erreur de routage privé + No comment provided by engineer. + Profile and server connections Profil et connexions au serveur @@ -4247,14 +5905,9 @@ Erreur : %@ Image de profil No comment provided by engineer. - - Profile name - Nom du profil - No comment provided by engineer. - - - Profile name: - Nom du profil : + + Profile images + Images de profil No comment provided by engineer. @@ -4262,10 +5915,15 @@ Erreur : %@ Mot de passe de profil No comment provided by engineer. + + Profile theme + Thème de profil + No comment provided by engineer. + Profile update will be sent to your contacts. La mise à jour du profil sera envoyée à vos contacts. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4287,6 +5945,15 @@ Erreur : %@ Interdire les réactions aux messages. No comment provided by engineer. + + Prohibit reporting messages to moderators. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + Interdire l'envoi de liens SimpleX. + No comment provided by engineer. + Prohibit sending direct messages to members. Interdire l'envoi de messages directs aux membres. @@ -4307,11 +5974,23 @@ Erreur : %@ Interdire l'envoi de messages vocaux. No comment provided by engineer. + + Protect IP address + Protéger l'adresse IP + No comment provided by engineer. + Protect app screen Protéger l'écran de l'app No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + Protégez votre adresse IP des relais de messagerie choisis par vos contacts. +Activez-le dans les paramètres *Réseau et serveurs*. + No comment provided by engineer. + Protect your chat profiles with a password! Protégez vos profils de chat par un mot de passe ! @@ -4327,6 +6006,21 @@ Erreur : %@ Délai d'attente du protocole par KB No comment provided by engineer. + + Proxied + Routé via un proxy + No comment provided by engineer. + + + Proxied servers + Serveurs routés via des proxy + No comment provided by engineer. + + + Proxy requires password + Le proxy est protégé par un mot de passe + No comment provided by engineer. + Push notifications Notifications push @@ -4334,10 +6028,12 @@ Erreur : %@ Push server + Serveur Push No comment provided by engineer. Quantum resistant encryption + Chiffrement résistant post-quantique No comment provided by engineer. @@ -4345,6 +6041,11 @@ Erreur : %@ Évaluer l'app No comment provided by engineer. + + Reachable chat toolbar + Barre d'outils accessible + No comment provided by engineer. + React… Réagissez… @@ -4353,33 +6054,28 @@ Erreur : %@ Read Lire - No comment provided by engineer. + swipe action Read more En savoir plus No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Pour en savoir plus, consultez le [Guide de l'utilisateur](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). Pour en savoir plus, consultez le [Guide de l'utilisateur](https ://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Pour en savoir plus, consultez le [Guide de l'utilisateur](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). Pour en savoir plus, consultez le [Guide de l'utilisateur](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - - Read more in our GitHub repository. - Plus d'informations sur notre GitHub. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). Pour en savoir plus, consultez notre [dépôt GitHub](https://github.com/simplex-chat/simplex-chat#readme). @@ -4390,6 +6086,11 @@ Erreur : %@ Les accusés de réception sont désactivés No comment provided by engineer. + + Receive errors + Erreurs reçues + No comment provided by engineer. + Received at Reçu à @@ -4410,6 +6111,21 @@ Erreur : %@ Message reçu message info title + + Received messages + Messages reçus + No comment provided by engineer. + + + Received reply + Réponse reçue + No comment provided by engineer. + + + Received total + Total reçu + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. L'adresse de réception sera changée pour un autre serveur. Le changement d'adresse sera terminé lorsque l'expéditeur sera en ligne. @@ -4430,19 +6146,49 @@ Erreur : %@ Historique récent et amélioration du [bot annuaire](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + Le(s) destinataire(s) ne peut(vent) pas voir de qui provient ce message. + No comment provided by engineer. + Recipients see updates as you type them. Les destinataires voient les mises à jour au fur et à mesure que vous leur écrivez. No comment provided by engineer. + + Reconnect + Reconnecter + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Reconnecter tous les serveurs connectés pour forcer la livraison des messages. Cette méthode utilise du trafic supplémentaire. No comment provided by engineer. + + Reconnect all servers + Reconnecter tous les serveurs + No comment provided by engineer. + + + Reconnect all servers? + Reconnecter tous les serveurs ? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + Reconnecter le serveur pour forcer la livraison des messages. Utilise du trafic supplémentaire. + No comment provided by engineer. + + + Reconnect server? + Reconnecter le serveur ? + No comment provided by engineer. + Reconnect servers? - Reconnecter les serveurs? + Reconnecter les serveurs ? No comment provided by engineer. @@ -4460,10 +6206,23 @@ Erreur : %@ Réduction de la consommation de batterie No comment provided by engineer. + + Register + No comment provided by engineer. + + + Register notification token? + token info + + + Registered + token status text + Reject Rejeter - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4490,6 +6249,16 @@ Erreur : %@ Supprimer No comment provided by engineer. + + Remove archive? + Supprimer l'archive ? + No comment provided by engineer. + + + Remove image + Enlever l'image + No comment provided by engineer. + Remove member Retirer le membre @@ -4517,7 +6286,7 @@ Erreur : %@ Renegotiate encryption? - Renégocier le chiffrement? + Renégocier le chiffrement ? No comment provided by engineer. @@ -4527,10 +6296,12 @@ Erreur : %@ Repeat download + Répéter le téléchargement No comment provided by engineer. Repeat import + Répéter l'importation No comment provided by engineer. @@ -4540,6 +6311,7 @@ Erreur : %@ Repeat upload + Répéter l'envoi No comment provided by engineer. @@ -4547,6 +6319,46 @@ Erreur : %@ Répondre chat item action + + Report + chat item action + + + Report content: only group moderators will see it. + report reason + + + Report member profile: only group moderators will see it. + report reason + + + Report other: only group moderators will see it. + report reason + + + Report reason? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + report reason + + + Report violation: only group moderators will see it. + report reason + + + Report: %@ + report in notification + + + Reporting messages to moderators is prohibited. + No comment provided by engineer. + + + Reports + No comment provided by engineer. + Required Requis @@ -4557,16 +6369,41 @@ Erreur : %@ Réinitialisation No comment provided by engineer. + + Reset all hints + Rétablir tous les conseils + No comment provided by engineer. + + + Reset all statistics + Réinitialiser toutes les statistiques + No comment provided by engineer. + + + Reset all statistics? + Réinitialiser toutes les statistiques ? + No comment provided by engineer. + Reset colors Réinitialisation des couleurs No comment provided by engineer. + + Reset to app theme + Réinitialisation au thème de l'appli + No comment provided by engineer. + Reset to defaults Réinitialisation des valeurs par défaut No comment provided by engineer. + + Reset to user theme + Réinitialisation au thème de l'utilisateur + No comment provided by engineer. + Restart the app to create a new chat profile Redémarrez l'application pour créer un nouveau profil de chat @@ -4607,9 +6444,9 @@ Erreur : %@ Révéler chat item action - - Revert - Revenir en arrière + + Review conditions + Vérifier les conditions No comment provided by engineer. @@ -4637,55 +6474,66 @@ Erreur : %@ Exécuter le chat No comment provided by engineer. - - SMP servers - Serveurs SMP + + SMP server + Serveur SMP + No comment provided by engineer. + + + SOCKS proxy + proxy SOCKS + No comment provided by engineer. + + + Safely receive files + Réception de fichiers en toute sécurité No comment provided by engineer. Safer groups + Groupes plus sûrs No comment provided by engineer. Save Enregistrer - chat item action + alert button +chat item action Save (and notify contacts) Enregistrer (et en informer les contacts) - No comment provided by engineer. + alert button Save and notify contact Enregistrer et en informer le contact - No comment provided by engineer. + alert button Save and notify group members Enregistrer et en informer les membres du groupe No comment provided by engineer. + + Save and reconnect + Sauvegarder et se reconnecter + No comment provided by engineer. + Save and update group profile Enregistrer et mettre à jour le profil du groupe No comment provided by engineer. - - Save archive - Enregistrer l'archive - No comment provided by engineer. - - - Save auto-accept settings - Enregistrer les paramètres de validation automatique - No comment provided by engineer. - Save group profile Enregistrer le profil du groupe No comment provided by engineer. + + Save list + No comment provided by engineer. + Save passphrase and open chat Enregistrer la phrase secrète et ouvrir le chat @@ -4699,7 +6547,7 @@ Erreur : %@ Save preferences? Enregistrer les préférences ? - No comment provided by engineer. + alert title Save profile password @@ -4714,28 +6562,53 @@ Erreur : %@ Save servers? Enregistrer les serveurs ? - No comment provided by engineer. - - - Save settings? - Enregistrer les paramètres ? - No comment provided by engineer. + alert title Save welcome message? Enregistrer le message d'accueil ? No comment provided by engineer. + + Save your profile? + Sauvegarder votre profil ? + alert title + + + Saved + Enregistré + No comment provided by engineer. + Saved WebRTC ICE servers will be removed Les serveurs WebRTC ICE sauvegardés seront supprimés No comment provided by engineer. + + Saved from + Enregistré depuis + No comment provided by engineer. + Saved message Message enregistré message info title + + Saving %lld messages + Sauvegarde de %lld messages + No comment provided by engineer. + + + Scale + Échelle + No comment provided by engineer. + + + Scan / Paste link + Scanner / Coller un lien + No comment provided by engineer. + Scan QR code Scanner un code QR @@ -4776,11 +6649,21 @@ Erreur : %@ Rechercher ou coller un lien SimpleX No comment provided by engineer. + + Secondary + Secondaire + No comment provided by engineer. + Secure queue File d'attente sécurisée server test step + + Secured + Sécurisées + No comment provided by engineer. + Security assessment Évaluation de sécurité @@ -4794,6 +6677,21 @@ Erreur : %@ Select Choisir + chat item action + + + Select chat profile + Sélectionner un profil de discussion + No comment provided by engineer. + + + Selected %lld + %lld sélectionné(s) + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Les préférences de chat sélectionnées interdisent ce message. No comment provided by engineer. @@ -4831,11 +6729,6 @@ Erreur : %@ Envoyer les accusés de réception à No comment provided by engineer. - - Send direct message - Envoyer un message direct - No comment provided by engineer. - Send direct message to connect Envoyer un message direct pour vous connecter @@ -4846,9 +6739,14 @@ Erreur : %@ Envoyer un message éphémère No comment provided by engineer. + + Send errors + Erreurs d'envoi + No comment provided by engineer. + Send link previews - Envoi d'aperçus de liens + Aperçu des liens No comment provided by engineer. @@ -4856,14 +6754,28 @@ Erreur : %@ Envoyer un message dynamique No comment provided by engineer. + + Send message to enable calls. + Envoyer un message pour activer les appels. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + Envoyer les messages de manière directe lorsque l'adresse IP est protégée et que votre serveur ou le serveur de destination ne prend pas en charge le routage privé. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + Envoyez les messages de manière directe lorsque votre serveur ou le serveur de destination ne prend pas en charge le routage privé. + No comment provided by engineer. + Send notifications Envoi de notifications No comment provided by engineer. - - Send notifications: - Envoi de notifications : + + Send private reports No comment provided by engineer. @@ -4889,7 +6801,7 @@ Erreur : %@ Sender cancelled file transfer. L'expéditeur a annulé le transfert de fichiers. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4946,6 +6858,11 @@ Erreur : %@ Envoyé le : %@ copied message info + + Sent directly + Envoyé directement + No comment provided by engineer. + Sent file event Événement de fichier envoyé @@ -4956,11 +6873,71 @@ Erreur : %@ Message envoyé message info title + + Sent messages + Messages envoyés + No comment provided by engineer. + Sent messages will be deleted after set time. Les messages envoyés seront supprimés après une durée déterminée. No comment provided by engineer. + + Sent reply + Réponse envoyée + No comment provided by engineer. + + + Sent total + Total envoyé + No comment provided by engineer. + + + Sent via proxy + Envoyé via le proxy + No comment provided by engineer. + + + Server + Serveur + No comment provided by engineer. + + + Server added to operator %@. + Serveur ajouté à l'opérateur %@. + alert message + + + Server address + Adresse du serveur + No comment provided by engineer. + + + Server address is incompatible with network settings. + L'adresse du serveur est incompatible avec les paramètres du réseau. + srv error text. + + + Server address is incompatible with network settings: %@. + L'adresse du serveur est incompatible avec les paramètres réseau : %@. + No comment provided by engineer. + + + Server operator changed. + L'opérateur du serveur a changé. + alert title + + + Server operators + Opérateurs de serveur + No comment provided by engineer. + + + Server protocol changed. + Le protocole du serveur a été modifié. + alert title + Server requires authorization to create queues, check password Le serveur requiert une autorisation pour créer des files d'attente, vérifiez le mot de passe @@ -4968,7 +6945,7 @@ Erreur : %@ Server requires authorization to upload, check password - Le serveur requiert une autorisation pour uploader, vérifiez le mot de passe + Le serveur requiert une autorisation pour téléverser, vérifiez le mot de passe server test error @@ -4976,11 +6953,36 @@ Erreur : %@ Échec du test du serveur ! No comment provided by engineer. + + Server type + Type de serveur + No comment provided by engineer. + + + Server version is incompatible with network settings. + La version du serveur est incompatible avec les paramètres du réseau. + srv error text + + + Server version is incompatible with your app: %@. + La version du serveur est incompatible avec votre appli : %@. + No comment provided by engineer. + Servers Serveurs No comment provided by engineer. + + Servers info + Infos serveurs + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + Les statistiques des serveurs seront réinitialisées - il n'est pas possible de revenir en arrière ! + No comment provided by engineer. + Session code Code de session @@ -4991,11 +6993,20 @@ Erreur : %@ Définir 1 jour No comment provided by engineer. + + Set chat name… + No comment provided by engineer. + Set contact name… Définir le nom du contact… No comment provided by engineer. + + Set default theme + Définir le thème par défaut + No comment provided by engineer. + Set group preferences Définir les préférences du groupe @@ -5006,6 +7017,10 @@ Erreur : %@ Il permet de remplacer l'authentification du système. No comment provided by engineer. + + Set message expiration in chats. + No comment provided by engineer. + Set passcode Définir le code d'accès @@ -5013,6 +7028,7 @@ Erreur : %@ Set passphrase + Définir une phrase secrète No comment provided by engineer. @@ -5035,24 +7051,55 @@ Erreur : %@ Paramètres No comment provided by engineer. + + Settings were changed. + Les paramètres ont été modifiés. + alert message + + + Shape profile images + Images de profil modelable + No comment provided by engineer. + Share Partager - chat item action + alert action +chat item action Share 1-time link Partager un lien unique No comment provided by engineer. + + Share 1-time link with a friend + Partager un lien unique avec un ami + No comment provided by engineer. + + + Share SimpleX address on social media. + Partagez votre adresse SimpleX sur les réseaux sociaux. + No comment provided by engineer. + Share address Partager l'adresse No comment provided by engineer. + + Share address publicly + Partager publiquement votre adresse + No comment provided by engineer. + Share address with contacts? Partager l'adresse avec vos contacts ? + alert title + + + Share from other apps. + Partager depuis d'autres applications. No comment provided by engineer. @@ -5060,9 +7107,19 @@ Erreur : %@ Partager le lien No comment provided by engineer. + + Share profile + Partager le profil + No comment provided by engineer. + Share this 1-time invite link - Partager ce lien d'invitation unique + Partagez ce lien d'invitation unique + No comment provided by engineer. + + + Share to SimpleX + Partager sur SimpleX No comment provided by engineer. @@ -5070,8 +7127,13 @@ Erreur : %@ Partager avec vos contacts No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code + Afficher le code QR No comment provided by engineer. @@ -5086,12 +7148,27 @@ Erreur : %@ Show last messages - Voir les derniers messages + Aperçu des derniers messages + No comment provided by engineer. + + + Show message status + Afficher le statut du message + No comment provided by engineer. + + + Show percentage + Afficher le pourcentage No comment provided by engineer. Show preview - Afficher l'aperçu + Aperçu affiché + No comment provided by engineer. + + + Show → on messages sent via private routing. + Afficher → sur les messages envoyés via le routage privé. No comment provided by engineer. @@ -5099,11 +7176,21 @@ Erreur : %@ Afficher : No comment provided by engineer. + + SimpleX + SimpleX + No comment provided by engineer. + SimpleX Address Adresse SimpleX No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + SimpleX Chat et Flux ont conclu un accord pour inclure les serveurs exploités par Flux dans l'application. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. La sécurité de SimpleX Chat a été auditée par Trail of Bits. @@ -5134,6 +7221,20 @@ Erreur : %@ Adresse SimpleX No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + Les adresses SimpleX et les liens à usage unique peuvent être partagés en toute sécurité via n'importe quelle messagerie. + No comment provided by engineer. + + + SimpleX address or 1-time link? + Adresse SimpleX ou lien unique ? + No comment provided by engineer. + + + SimpleX channel link + simplex link type + SimpleX contact address Adresse de contact SimpleX @@ -5152,6 +7253,16 @@ Erreur : %@ SimpleX links Liens SimpleX + chat feature + + + SimpleX links are prohibited. + Les liens SimpleX sont interdits dans ce groupe. + No comment provided by engineer. + + + SimpleX links not allowed + Les liens SimpleX ne sont pas autorisés No comment provided by engineer. @@ -5159,11 +7270,21 @@ Erreur : %@ Invitation unique SimpleX simplex link type + + SimpleX protocols reviewed by Trail of Bits. + Protocoles SimpleX audité par Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Mode incognito simplifié No comment provided by engineer. + + Size + Taille + No comment provided by engineer. + Skip Passer @@ -5179,16 +7300,53 @@ Erreur : %@ Petits groupes (max 20) No comment provided by engineer. + + Soft + Léger + blur media + + + Some app settings were not migrated. + Certains paramètres de l'application n'ont pas été migrés. + No comment provided by engineer. + + + Some file(s) were not exported: + Certains fichiers n'ont pas été exportés : + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. Des erreurs non fatales se sont produites lors de l'importation - vous pouvez consulter la console de chat pour plus de détails. No comment provided by engineer. + + Some non-fatal errors occurred during import: + L'importation a entraîné des erreurs non fatales : + No comment provided by engineer. + + + Some servers failed the test: +%@ + Certains serveurs ont échoué le test : +%@ + alert message + Somebody Quelqu'un notification title + + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + Carré, circulaire, ou toute autre forme intermédiaire. + No comment provided by engineer. + Start chat Démarrer le chat @@ -5204,6 +7362,16 @@ Erreur : %@ Démarrer la migration No comment provided by engineer. + + Starting from %@. + À partir de %@. + No comment provided by engineer. + + + Statistics + Statistiques + No comment provided by engineer. + Stop Arrêter @@ -5216,11 +7384,7 @@ Erreur : %@ Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - Arrêter le chat pour permettre des actions sur la base de données + Arrêter le chat No comment provided by engineer. @@ -5251,27 +7415,62 @@ Erreur : %@ Stop sharing Cesser le partage - No comment provided by engineer. + alert action Stop sharing address? Cesser le partage d'adresse ? - No comment provided by engineer. + alert title Stopping chat + Arrêt du chat No comment provided by engineer. + + Storage + No comment provided by engineer. + + + Strong + Fort + blur media + Submit Soumettre No comment provided by engineer. + + Subscribed + Inscriptions + No comment provided by engineer. + + + Subscription errors + Erreurs d'inscription + No comment provided by engineer. + + + Subscriptions ignored + Inscriptions ignorées + No comment provided by engineer. + Support SimpleX Chat Supporter SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Passer de l'audio à la vidéo pendant l'appel. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Changer de profil de chat pour les invitations à usage unique. + No comment provided by engineer. + System Système @@ -5282,11 +7481,20 @@ Erreur : %@ Authentification du système No comment provided by engineer. + + TCP connection + Connexion TCP + No comment provided by engineer. + TCP connection timeout Délai de connexion TCP No comment provided by engineer. + + TCP port for messaging + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5302,11 +7510,21 @@ Erreur : %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + Queue + No comment provided by engineer. + Take picture Prendre une photo No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + Appuyez sur Créer une adresse SimpleX dans le menu pour la créer ultérieurement. + No comment provided by engineer. + Tap button Appuyez sur le bouton @@ -5342,16 +7560,20 @@ Erreur : %@ Appuyez pour scanner No comment provided by engineer. - - Tap to start a new chat - Appuyez ici pour démarrer une nouvelle discussion - No comment provided by engineer. + + Temporary file error + Erreur de fichier temporaire + file error alert title Test failed at step %@. Échec du test à l'étape %@. server test failure + + Test notifications + No comment provided by engineer. + Test server Tester le serveur @@ -5365,7 +7587,7 @@ Erreur : %@ Tests failed! Échec des tests ! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5382,11 +7604,6 @@ Erreur : %@ Merci aux utilisateurs - contribuez via Weblate ! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - La 1ère plateforme sans aucun identifiant d'utilisateur – privée par design. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5399,6 +7616,16 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. L'application peut vous avertir lorsque vous recevez des messages ou des demandes de contact - veuillez ouvrir les paramètres pour les activer. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + L'application protège votre vie privée en utilisant des opérateurs différents pour chaque conversation. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + L'application demandera de confirmer les téléchargements à partir de serveurs de fichiers inconnus (sauf .onion). + No comment provided by engineer. + The attempt to change database passphrase was not completed. La tentative de modification de la phrase secrète de la base de données n'a pas abouti. @@ -5409,6 +7636,11 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. Le code scanné n'est pas un code QR de lien SimpleX. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + La connexion a atteint la limite des messages non délivrés, votre contact est peut-être hors ligne. + No comment provided by engineer. + The connection you accepted will be cancelled! La connexion que vous avez acceptée sera annulée ! @@ -5429,6 +7661,11 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. Le chiffrement fonctionne et le nouvel accord de chiffrement n'est pas nécessaire. Cela peut provoquer des erreurs de connexion ! No comment provided by engineer. + + The future of messaging + La nouvelle génération de messagerie privée + No comment provided by engineer. + The hash of the previous message is different. Le hash du message précédent est différent. @@ -5444,9 +7681,14 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. Le message sera marqué comme modéré pour tous les membres. No comment provided by engineer. - - The next generation of private messaging - La nouvelle génération de messagerie privée + + The messages will be deleted for all members. + Les messages seront supprimés pour tous les membres. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. + Les messages seront marqués comme modérés pour tous les membres. No comment provided by engineer. @@ -5454,9 +7696,14 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. L'ancienne base de données n'a pas été supprimée lors de la migration, elle peut être supprimée. No comment provided by engineer. - - The profile is only shared with your contacts. - Le profil n'est partagé qu'avec vos contacts. + + The same conditions will apply to operator **%@**. + Les mêmes conditions s'appliquent à l'opérateur **%@**. + No comment provided by engineer. + + + The second preset operator in the app! + Le deuxième opérateur prédéfini de l'application ! No comment provided by engineer. @@ -5474,14 +7721,29 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. Les serveurs pour les nouvelles connexions de votre profil de chat actuel **%@**. No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + Les serveurs pour les nouveaux fichiers de votre profil de chat actuel **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. Le texte collé n'est pas un lien SimpleX. No comment provided by engineer. - - Theme - Thème + + The uploaded database archive will be permanently removed from the servers. + L'archive de la base de données envoyée sera définitivement supprimée des serveurs. + No comment provided by engineer. + + + Themes + Thèmes + No comment provided by engineer. + + + These conditions will also apply for: **%@**. + Ces conditions s'appliquent également aux : **%@**. No comment provided by engineer. @@ -5504,6 +7766,10 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. Cette action ne peut être annulée - les messages envoyés et reçus avant la date sélectionnée seront supprimés. Cela peut prendre plusieurs minutes. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. Cette action ne peut être annulée - votre profil, vos contacts, vos messages et vos fichiers seront irréversiblement perdus. @@ -5511,10 +7777,12 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. This chat is protected by end-to-end encryption. + Cette discussion est protégée par un chiffrement de bout en bout. E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + Cette discussion est protégée par un chiffrement de bout en bout résistant aux technologies quantiques. E2EE info chat item @@ -5547,11 +7815,29 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. Voici votre propre lien unique ! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + Ce lien a été utilisé avec un autre appareil mobile, veuillez créer un nouveau lien sur le bureau. + No comment provided by engineer. + + + This message was deleted or not received yet. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. Ce paramètre s'applique aux messages de votre profil de chat actuel **%@**. No comment provided by engineer. + + Title + Titre + No comment provided by engineer. + To ask any questions and to receive updates: Si vous avez des questions et que vous souhaitez des réponses : @@ -5559,7 +7845,7 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. To connect, your contact can scan QR code or use the link in the app. - Pour se connecter, votre contact peut scanner le code QR ou utiliser le lien dans l'application. + Pour se connecter, votre contact peut scanner un code QR ou utiliser un lien dans l'app. No comment provided by engineer. @@ -5572,9 +7858,9 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. Pour établir une nouvelle connexion No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Pour protéger votre vie privée, au lieu d’IDs utilisés par toutes les autres plateformes, SimpleX a des IDs pour les queues de messages, distinctes pour chacun de vos contacts. + + To protect against your link being replaced, you can compare contact security codes. + Pour vous protéger contre le remplacement de votre lien, vous pouvez comparer les codes de sécurité des contacts. No comment provided by engineer. @@ -5582,6 +7868,11 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. Pour préserver le fuseau horaire, les fichiers image/voix utilisent le système UTC. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + Pour protéger votre adresse IP, le routage privé utilise vos serveurs SMP pour délivrer les messages. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5589,6 +7880,26 @@ You will be prompted to complete authentication before this feature is enabled.< Vous serez invité à confirmer l'authentification avant que cette fonction ne soit activée. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Pour protéger votre vie privée, au lieu d’IDs utilisés par toutes les autres plateformes, SimpleX a des IDs pour les queues de messages, distinctes pour chacun de vos contacts. + No comment provided by engineer. + + + To receive + Pour recevoir + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + Si vous souhaitez enregistrer une conversation, veuillez autoriser l'utilisation du microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Si vous souhaitez enregistrer une vidéo, veuillez autoriser l'utilisation de la caméra. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Pour enregistrer un message vocal, veuillez accorder la permission d'utiliser le microphone. @@ -5599,26 +7910,60 @@ Vous serez invité à confirmer l'authentification avant que cette fonction ne s Pour révéler votre profil caché, entrez le mot de passe dans le champ de recherche de la page **Vos profils de chat**. No comment provided by engineer. + + To send + Pour envoyer + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. Pour prendre en charge les notifications push instantanées, la base de données du chat doit être migrée. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + Pour utiliser les serveurs de **%@**, acceptez les conditions d'utilisation. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. Pour vérifier le chiffrement de bout en bout avec votre contact, comparez (ou scannez) le code sur vos appareils. No comment provided by engineer. + + Toggle chat list: + Afficher la liste des conversations : + No comment provided by engineer. + Toggle incognito when connecting. Basculer en mode incognito lors de la connexion. No comment provided by engineer. + + Token status: %@. + token status + + + Toolbar opacity + Opacité de la barre d'outils + No comment provided by engineer. + + + Total + Total + No comment provided by engineer. + Transport isolation Transport isolé No comment provided by engineer. + + Transport sessions + Sessions de transport + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Tentative de connexion au serveur utilisé pour recevoir les messages de ce contact (erreur : %@). @@ -5674,10 +8019,10 @@ Vous serez invité à confirmer l'authentification avant que cette fonction ne s Débloquer ce membre ? No comment provided by engineer. - - Unexpected error: %@ - Erreur inattendue : %@ - item status description + + Undelivered messages + Messages non distribués + No comment provided by engineer. Unexpected migration state @@ -5687,7 +8032,7 @@ Vous serez invité à confirmer l'authentification avant que cette fonction ne s Unfav. Unfav. - No comment provided by engineer. + swipe action Unhide @@ -5724,6 +8069,11 @@ Vous serez invité à confirmer l'authentification avant que cette fonction ne s Erreur inconnue No comment provided by engineer. + + Unknown servers! + Serveurs inconnus ! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. À moins que vous utilisiez l'interface d'appel d'iOS, activez le mode "Ne pas déranger" pour éviter les interruptions. @@ -5759,11 +8109,15 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Unmute Démute - No comment provided by engineer. + notification label action Unread Non lu + swipe action + + + Unsupported connection link No comment provided by engineer. @@ -5776,11 +8130,6 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Mise à jour No comment provided by engineer. - - Update .onion hosts setting? - Mettre à jour le paramètre des hôtes .onion ? - No comment provided by engineer. - Update database passphrase Mise à jour de la phrase secrète de la base de données @@ -5791,9 +8140,13 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Mettre à jour les paramètres réseau ? No comment provided by engineer. - - Update transport isolation mode? - Mettre à jour le mode d'isolement du transport ? + + Update settings? + Mettre à jour les paramètres ? + No comment provided by engineer. + + + Updated conditions No comment provided by engineer. @@ -5801,27 +8154,44 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien La mise à jour des ces paramètres reconnectera le client à tous les serveurs. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - La mise à jour de ce paramètre reconnectera le client à tous les serveurs. - No comment provided by engineer. - Upgrade and open chat Mettre à niveau et ouvrir le chat No comment provided by engineer. + + Upload errors + Erreurs de téléversement + No comment provided by engineer. + Upload failed + Échec de l'envoi No comment provided by engineer. Upload file - Transférer le fichier + Téléverser le fichier server test step + + Uploaded + Téléversé + No comment provided by engineer. + + + Uploaded files + Fichiers téléversés + No comment provided by engineer. + Uploading archive + Envoi de l'archive + No comment provided by engineer. + + + Use %@ + Utiliser %@ No comment provided by engineer. @@ -5829,11 +8199,24 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Utiliser les hôtes .onions No comment provided by engineer. + + Use SOCKS proxy + Utiliser un proxy SOCKS + No comment provided by engineer. + Use SimpleX Chat servers? Utiliser les serveurs SimpleX Chat ? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat Utiliser le chat @@ -5844,6 +8227,16 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Utiliser le profil actuel No comment provided by engineer. + + Use for files + Utiliser pour les fichiers + No comment provided by engineer. + + + Use for messages + Utiliser pour les messages + No comment provided by engineer. + Use for new connections Utiliser pour les nouvelles connexions @@ -5869,23 +8262,52 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Utilisation de notifications locales uniquement ? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + Utiliser le routage privé avec des serveurs inconnus lorsque l'adresse IP n'est pas protégée. + No comment provided by engineer. + + + Use private routing with unknown servers. + Utiliser le routage privé avec des serveurs inconnus. + No comment provided by engineer. + Use server Utiliser ce serveur No comment provided by engineer. + + Use servers + Utiliser les serveurs + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. + Utiliser l'application pendant l'appel. No comment provided by engineer. - - User profile - Profil d'utilisateur + + Use the app with one hand. + Utiliser l'application d'une main. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - L'utilisation des hôtes .onion nécessite un fournisseur VPN compatible. + + Use web port + No comment provided by engineer. + + + User selection + Sélection de l'utilisateur + No comment provided by engineer. + + + Username + Nom d'utilisateur No comment provided by engineer. @@ -5915,10 +8337,12 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Verify database passphrase + Vérifier la phrase secrète de la base de données No comment provided by engineer. Verify passphrase + Vérifier la phrase secrète No comment provided by engineer. @@ -5943,7 +8367,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Video will be received when your contact completes uploading it. - La vidéo ne sera reçue que lorsque votre contact aura fini de la transférer. + La vidéo ne sera reçue que lorsque votre contact aura fini la mettre en ligne. No comment provided by engineer. @@ -5956,11 +8380,21 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Vidéos et fichiers jusqu'à 1Go No comment provided by engineer. + + View conditions + Voir les conditions + No comment provided by engineer. + View security code Afficher le code de sécurité No comment provided by engineer. + + View updated conditions + Voir les conditions mises à jour + No comment provided by engineer. + Visible history Historique visible @@ -5976,11 +8410,16 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Les messages vocaux sont interdits dans ce chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. Les messages vocaux sont interdits dans ce groupe. No comment provided by engineer. + + Voice messages not allowed + Les messages vocaux ne sont pas autorisés + No comment provided by engineer. + Voice messages prohibited! Messages vocaux interdits ! @@ -6011,8 +8450,19 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien En attente de la vidéo No comment provided by engineer. + + Wallpaper accent + Accentuation du papier-peint + No comment provided by engineer. + + + Wallpaper background + Fond d'écran + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures + Attention : démarrer une session de chat sur plusieurs appareils n'est pas pris en charge et entraînera des dysfonctionnements au niveau de la transmission des messages No comment provided by engineer. @@ -6037,6 +8487,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Welcome message is too long + Le message de bienvenue est trop long No comment provided by engineer. @@ -6049,9 +8500,14 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Quand disponible No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Vous pouvez accepter ou refuser les demandes de contacts. + + When connecting audio and video calls. + Lors des appels audio et vidéo. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. + Lorsque plusieurs opérateurs sont activés, aucun d'entre eux ne dispose de métadonnées permettant de savoir qui communique avec qui. No comment provided by engineer. @@ -6059,6 +8515,21 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Lorsque vous partagez un profil incognito avec quelqu'un, ce profil sera utilisé pour les groupes auxquels il vous invite. No comment provided by engineer. + + WiFi + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + Activé dans les discussions directes ! + No comment provided by engineer. + + + Wired ethernet + Ethernet câblé + No comment provided by engineer. + With encrypted files and media. Avec les fichiers et les médias chiffrés. @@ -6074,28 +8545,44 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Consommation réduite de la batterie. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + Sans Tor ou un VPN, votre adresse IP sera visible par les serveurs de fichiers. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + Sans Tor ni VPN, votre adresse IP sera visible par ces relais XFTP : %@. + alert message + Wrong database passphrase Mauvaise phrase secrète pour la base de données No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + Clé erronée ou connexion non identifiée - il est très probable que cette connexion soit supprimée. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + Mauvaise clé ou adresse inconnue du bloc de données du fichier - le fichier est probablement supprimé. + file error text + Wrong passphrase! Mauvaise phrase secrète ! No comment provided by engineer. - - XFTP servers - Serveurs XFTP - No comment provided by engineer. - - - You - Vous + + XFTP server + Serveur XFTP No comment provided by engineer. You **must not** use the same database on two devices. + Vous **ne devez pas** utiliser la même base de données sur deux appareils. No comment provided by engineer. @@ -6118,6 +8605,11 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Vous êtes déjà connecté·e à %@ via ce lien. No comment provided by engineer. + + You are already connected with %@. + Vous êtes déjà connecté avec %@. + No comment provided by engineer. + You are already connecting to %@. Vous êtes déjà en train de vous connecter à %@. @@ -6165,11 +8657,26 @@ Répéter la demande d'adhésion ? Vous êtes invité·e au groupe No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + Vous n'êtes pas connecté à ces serveurs. Le routage privé est utilisé pour leur délivrer des messages. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. Vous pouvez accepter des appels à partir de l'écran de verrouillage, sans authentification de l'appareil ou de l'application. No comment provided by engineer. + + You can change it in Appearance settings. + Vous pouvez choisir de le modifier dans les paramètres d'apparence. + No comment provided by engineer. + + + You can configure servers via settings. + Vous pouvez configurer les serveurs via les paramètres. + No comment provided by engineer. + You can create it later Vous pouvez la créer plus tard @@ -6187,6 +8694,7 @@ Répéter la demande d'adhésion ? You can give another try. + Vous pouvez faire un nouvel essai. No comment provided by engineer. @@ -6199,11 +8707,21 @@ Répéter la demande d'adhésion ? Vous pouvez le rendre visible à vos contacts SimpleX via Paramètres. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ Vous pouvez maintenant envoyer des messages à %@ notification body + + You can send messages to %@ from Archived contacts. + Vous pouvez envoyer des messages à %@ à partir des contacts archivés. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + Vous pouvez définir un nom de connexion pour vous rappeler avec qui le lien a été partagé. + No comment provided by engineer. + You can set lock screen notification preview via settings. Vous pouvez configurer l'aperçu des notifications sur l'écran de verrouillage via les paramètres. @@ -6219,16 +8737,16 @@ Répéter la demande d'adhésion ? Vous pouvez partager cette adresse avec vos contacts pour leur permettre de se connecter avec **%@**. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - Vous pouvez partager votre adresse sous la forme d'un lien ou d'un code QR - tout le monde peut l'utiliser pour vous contacter. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app Vous pouvez lancer le chat via Paramètres / Base de données ou en redémarrant l'app No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + Vous pouvez toujours voir la conversation avec %@ dans la liste des discussions. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. Vous pouvez activer SimpleX Lock dans les Paramètres. @@ -6242,23 +8760,23 @@ Répéter la demande d'adhésion ? You can view invitation link again in connection details. Vous pouvez à nouveau consulter le lien d'invitation dans les détails de la connexion. - No comment provided by engineer. + alert message You can't send messages! Vous ne pouvez pas envoyer de messages ! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Vous contrôlez par quel·s serveur·s vous pouvez **transmettre** ainsi que par quel·s serveur·s vous pouvez **recevoir** les messages de vos contacts. - No comment provided by engineer. - You could not be verified; please try again. Vous n'avez pas pu être vérifié·e ; veuillez réessayer. No comment provided by engineer. + + You decide who can connect. + Vous choisissez qui peut se connecter. + No comment provided by engineer. + You have already requested connection via this address! Vous avez déjà demandé une connexion via cette adresse ! @@ -6271,11 +8789,6 @@ Repeat connection request? Répéter la demande de connexion ? No comment provided by engineer. - - You have no chats - Vous n'avez aucune discussion - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. Vous devez saisir la phrase secrète à chaque fois que l'application démarre - elle n'est pas stockée sur l'appareil. @@ -6296,11 +8809,26 @@ Répéter la demande de connexion ? Vous avez rejoint ce groupe. Connexion à l'invitation d'un membre du groupe. No comment provided by engineer. + + You may migrate the exported database. + Vous pouvez migrer la base de données exportée. + No comment provided by engineer. + + + You may save the exported archive. + Vous pouvez enregistrer l'archive exportée. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. Vous devez utiliser la version la plus récente de votre base de données de chat sur un seul appareil UNIQUEMENT, sinon vous risquez de ne plus recevoir les messages de certains contacts. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + Vous devez autoriser votre contact à appeler pour pouvoir l'appeler. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. Vous devez autoriser votre contact à envoyer des messages vocaux pour pouvoir en envoyer. @@ -6316,6 +8844,10 @@ Répéter la demande de connexion ? Vous avez envoyé une invitation de groupe No comment provided by engineer. + + You should receive notifications. + token info + You will be connected to group when the group host's device is online, please wait or check later! Vous serez connecté·e au groupe lorsque l'appareil de l'hôte sera en ligne, veuillez attendre ou vérifier plus tard ! @@ -6351,6 +8883,11 @@ Répéter la demande de connexion ? Vous continuerez à recevoir des appels et des notifications des profils mis en sourdine lorsqu'ils sont actifs. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + Vous ne recevrez plus de messages de cette discussion. L'historique sera préservé. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Vous ne recevrez plus de messages de ce groupe. L'historique du chat sera conservé. @@ -6371,31 +8908,16 @@ Répéter la demande de connexion ? Vous utilisez un profil incognito pour ce groupe - pour éviter de partager votre profil principal ; inviter des contacts n'est pas possible No comment provided by engineer. - - Your %@ servers - Vos serveurs %@ - No comment provided by engineer. - Your ICE servers Vos serveurs ICE No comment provided by engineer. - - Your SMP servers - Vos serveurs SMP - No comment provided by engineer. - Your SimpleX address Votre adresse SimpleX No comment provided by engineer. - - Your XFTP servers - Vos serveurs XFTP - No comment provided by engineer. - Your calls Vos appels @@ -6411,16 +8933,19 @@ Répéter la demande de connexion ? Votre base de données de chat n'est pas chiffrée - définisez une phrase secrète. No comment provided by engineer. + + Your chat preferences + Vos préférences de discussion + alert title + Your chat profiles Vos profils de chat No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Votre contact a besoin d'être en ligne pour completer la connexion. -Vous pouvez annuler la connexion et supprimer le contact (et réessayer plus tard avec un autre lien). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Votre connexion a été déplacée vers %@ mais une erreur inattendue s'est produite lors de la redirection vers le profil. No comment provided by engineer. @@ -6438,6 +8963,11 @@ Vous pouvez annuler la connexion et supprimer le contact (et réessayer plus tar Vos contacts resteront connectés. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Vos informations d'identification peuvent être envoyées non chiffrées. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Votre base de données de chat actuelle va être SUPPRIMEE et REMPLACEE par celle importée. @@ -6468,33 +8998,36 @@ Vous pouvez annuler la connexion et supprimer le contact (et réessayer plus tar Votre profil **%@** sera partagé. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Votre profil est stocké sur votre appareil et est seulement partagé avec vos contacts. -Les serveurs SimpleX ne peuvent pas voir votre profil. + + Your profile is stored on your device and only shared with your contacts. + Le profil n'est partagé qu'avec vos contacts. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Votre profil, vos contacts et les messages reçus sont stockés sur votre appareil. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Votre profil est stocké sur votre appareil et est seulement partagé avec vos contacts. Les serveurs SimpleX ne peuvent pas voir votre profil. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Votre profil a été modifié. Si vous l'enregistrez, le profil mis à jour sera envoyé à tous vos contacts. + alert message + Your random profile Votre profil aléatoire No comment provided by engineer. - - Your server - Votre serveur - No comment provided by engineer. - Your server address Votre adresse de serveur No comment provided by engineer. + + Your servers + Vos serveurs + No comment provided by engineer. + Your settings Vos paramètres @@ -6535,11 +9068,21 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. appel accepté call status + + accepted invitation + invitation acceptée + chat list item title + admin admin member role + + admins + admins + feature role + agreeing encryption for %@… négociation du chiffrement avec %@… @@ -6550,6 +9093,11 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. négociation du chiffrement… chat item text + + all members + tous les membres + feature role + always toujours @@ -6560,6 +9108,15 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. et %lld autres événements No comment provided by engineer. + + archived report + No comment provided by engineer. + + + attempts + tentatives + No comment provided by engineer. + audio call (not e2e encrypted) appel audio (sans chiffrement) @@ -6593,13 +9150,19 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. blocked by admin bloqué par l'administrateur - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold gras No comment provided by engineer. + + call + appeler + No comment provided by engineer. + call error erreur d'appel @@ -6703,7 +9266,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. connecting… connexion… - chat list item title + No comment provided by engineer. connection established @@ -6750,10 +9313,16 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. jours time unit + + decryption errors + Erreurs de déchiffrement + No comment provided by engineer. + default (%@) défaut (%@) - pref value + delete after time +pref value default (no) @@ -6800,6 +9369,11 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. message dupliqué integrity error chat item + + duplicates + doublons + No comment provided by engineer. + e2e encrypted chiffré de bout en bout @@ -6875,9 +9449,14 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. erreur No comment provided by engineer. - - event happened - event happened + + expired + expiré + No comment provided by engineer. + + + forwarded + transféré No comment provided by engineer. @@ -6905,6 +9484,11 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. La keychain d'iOS sera utilisée pour stocker en toute sécurité la phrase secrète après le redémarrage de l'app ou la modification de la phrase secrète - il permettra de recevoir les notifications push. No comment provided by engineer. + + inactive + inactif + No comment provided by engineer. + incognito via contact address link mode incognito via le lien d'adresse du contact @@ -6945,6 +9529,11 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. invitation au groupe %@ group name + + invite + inviter + No comment provided by engineer. + invited invité·e @@ -7000,6 +9589,11 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. est connecté·e rcv group event chat item + + message + message + No comment provided by engineer. + message received message reçu @@ -7025,6 +9619,10 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. modéré par %@ marked deleted chat item preview text + + moderator + member role + months mois @@ -7033,7 +9631,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. never jamais - No comment provided by engineer. + delete after time new message @@ -7064,8 +9662,8 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. off off enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7082,18 +9680,42 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. on group pref value + + other + autre + No comment provided by engineer. + + + other errors + autres erreurs + No comment provided by engineer. + owner propriétaire member role + + owners + propriétaires + feature role + peer-to-peer pair-à-pair No comment provided by engineer. + + pending + No comment provided by engineer. + + + pending approval + No comment provided by engineer. + quantum resistant e2e encryption + chiffrement e2e résistant post-quantique chat item text @@ -7106,6 +9728,10 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. confimation reçu… No comment provided by engineer. + + rejected + No comment provided by engineer. + rejected call appel rejeté @@ -7136,6 +9762,26 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. vous a retiré rcv group event chat item + + requested to connect + demande à se connecter + chat list item title + + + saved + enregistré + No comment provided by engineer. + + + saved from %@ + enregistré à partir de %@ + No comment provided by engineer. + + + search + rechercher + No comment provided by engineer. + sec sec @@ -7161,6 +9807,15 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. envoyer un message direct No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + info sur la file d'attente du serveur : %1$@ + +dernier message reçu : %2$@ + queue info + set new contact address a changé d'adresse de contact @@ -7173,6 +9828,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. standard end-to-end encryption + chiffrement de bout en bout standard chat item text @@ -7200,11 +9856,21 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. inconnu connection info + + unknown servers + relais inconnus + No comment provided by engineer. + unknown status statut inconnu No comment provided by engineer. + + unprotected + non protégé + No comment provided by engineer. + updated group profile mise à jour du profil de groupe @@ -7245,6 +9911,11 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. via relais No comment provided by engineer. + + video + vidéo + No comment provided by engineer. + video call (not e2e encrypted) appel vidéo (sans chiffrement) @@ -7270,11 +9941,21 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. semaines time unit + + when IP hidden + lorsque l'IP est masquée + No comment provided by engineer. + yes oui pref value + + you + vous + No comment provided by engineer. + you are invited to group vous êtes invité·e au groupe @@ -7349,7 +10030,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.
- +
@@ -7386,7 +10067,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.
- +
@@ -7406,4 +10087,249 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.
+ +
+ +
+ + + %d new events + %d nouveaux événements + notification body + + + From %d chat(s) + notification body + + + From: %@ + De : %@ + notification body + + + New events + Nouveaux événements + notification + + + New messages + Nouveaux messages + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright © 2024 SimpleX Chat. Tous droits réservés. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + L'app est verrouillée ! + No comment provided by engineer. + + + Cancel + Annuler + No comment provided by engineer. + + + Cannot access keychain to save database password + Impossible d'accéder à la keychain pour enregistrer le mot de passe de la base de données + No comment provided by engineer. + + + Cannot forward message + Impossible de transférer le message + No comment provided by engineer. + + + Comment + Commenter + No comment provided by engineer. + + + Currently maximum supported file size is %@. + Actuellement, la taille maximale des fichiers supportés est de %@. + No comment provided by engineer. + + + Database downgrade required + Mise à jour de la base de données nécessaire + No comment provided by engineer. + + + Database encrypted! + Base de données chiffrée ! + No comment provided by engineer. + + + Database error + Erreur de base de données + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + La phrase secrète de la base de données est différente de celle enregistrée dans la keychain. + No comment provided by engineer. + + + Database passphrase is required to open chat. + La phrase secrète de la base de données est nécessaire pour ouvrir le chat. + No comment provided by engineer. + + + Database upgrade required + Mise à niveau de la base de données nécessaire + No comment provided by engineer. + + + Error preparing file + Erreur lors de la préparation du fichier + No comment provided by engineer. + + + Error preparing message + Erreur lors de la préparation du message + No comment provided by engineer. + + + Error: %@ + Erreur : %@ + No comment provided by engineer. + + + File error + Erreur de fichier + No comment provided by engineer. + + + Incompatible database version + Version de la base de données incompatible + No comment provided by engineer. + + + Invalid migration confirmation + Confirmation de migration invalide + No comment provided by engineer. + + + Keychain error + Erreur de la keychain + No comment provided by engineer. + + + Large file! + Fichier trop lourd ! + No comment provided by engineer. + + + No active profile + Pas de profil actif + No comment provided by engineer. + + + Ok + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + Ouvrez l'app pour rétrograder la base de données. + No comment provided by engineer. + + + Open the app to upgrade the database. + Ouvrez l'app pour mettre à jour la base de données. + No comment provided by engineer. + + + Passphrase + Phrase secrète + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Veuillez créer un profil dans l'app SimpleX + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Les paramètres de chat sélectionnés ne permettent pas l'envoi de ce message. + No comment provided by engineer. + + + Sending a message takes longer than expected. + L'envoi d'un message prend plus de temps que prévu. + No comment provided by engineer. + + + Sending message… + Envoi du message… + No comment provided by engineer. + + + Share + Partager + No comment provided by engineer. + + + Slow network? + Réseau lent ? + No comment provided by engineer. + + + Unknown database error: %@ + Erreur inconnue de la base de données : %@ + No comment provided by engineer. + + + Unsupported format + Format non pris en charge + No comment provided by engineer. + + + Wait + Attendez + No comment provided by engineer. + + + Wrong database passphrase + Mauvaise phrase secrète pour la base de données + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + Vous pouvez autoriser le partage dans les paramètres Confidentialité et sécurité / SimpleX Lock. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/contents.json b/apps/ios/SimpleX Localizations/fr.xcloc/contents.json index 7df7c8ed26..d026c874ec 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/fr.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "fr", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/he.xcloc/Localized Contents/he.xliff b/apps/ios/SimpleX Localizations/he.xcloc/Localized Contents/he.xliff index ac71ed26bc..f76d7eba1e 100644 --- a/apps/ios/SimpleX Localizations/he.xcloc/Localized Contents/he.xliff +++ b/apps/ios/SimpleX Localizations/he.xcloc/Localized Contents/he.xliff @@ -217,23 +217,18 @@ Available in v5.1 ) No comment provided by engineer.
- - **Add new contact**: to create your one-time QR Code or link for your contact. - **הוסיפו איש קשר חדש**: ליצירת קוד QR או קישור חד־פעמיים עבור איש הקשר שלכם. - No comment provided by engineer. - **Create link / QR code** for your contact to use. **צור קישור / קוד QR** לשימוש איש הקשר שלך. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **יותר פרטי**: בדוק הודעות חדשות כל 20 דקות. אסימון המכשיר משותף עם שרת SimpleX Chat, אך לא כמה אנשי קשר או הודעות יש לך. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **הכי פרטי**: אל תשתמש בשרת ההתראות של SimpleX Chat, בדוק הודעות מעת לעת ברקע (תלוי בתדירות השימוש באפליקציה). No comment provided by engineer. @@ -247,8 +242,8 @@ Available in v5.1 **שימו לב**: לא ניתן יהיה לשחזר או לשנות את הסיסמה אם תאבדו אותה. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **מומלץ**: אסימון מכשיר והתראות נשלחים לשרת ההתראות של SimpleX Chat, אך לא תוכן ההודעה, גודלה או ממי היא. No comment provided by engineer. @@ -403,9 +398,9 @@ Available in v5.1 הוספת שרתים על ידי סריקת קוד QR. No comment provided by engineer. - - Add server… - הוסף שרת… + + Add server + הוסף שרת No comment provided by engineer. @@ -1391,8 +1386,8 @@ Available in v5.1 הודעות ישירות chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited. הודעות ישירות בין חברי קבוצה אסורות בקבוצה זו. No comment provided by engineer. @@ -1411,8 +1406,8 @@ Available in v5.1 הודעות נעלמות אסורות בצ׳אט זה. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. הודעות נעלמות אסורות בקבוצה זו. No comment provided by engineer. @@ -1956,18 +1951,18 @@ Available in v5.1 חברי הקבוצה יכולים למחוק באופן בלתי הפיך הודעות שנשלחו. No comment provided by engineer. - - Group members can send direct messages. + + Members can send direct messages. חברי הקבוצה יכולים לשלוח הודעות ישירות. No comment provided by engineer. - - Group members can send disappearing messages. + + Members can send disappearing messages. חברי הקבוצה יכולים לשלוח הודעות נעלמות. No comment provided by engineer. - - Group members can send voice messages. + + Members can send voice messages. חברי הקבוצה יכולים לשלוח הודעות קוליות. No comment provided by engineer. @@ -2115,8 +2110,8 @@ Available in v5.1 מיד No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam חסין מפני ספאם ושימוש לרעה No comment provided by engineer. @@ -2257,8 +2252,8 @@ Available in v5.1 מחיקה בלתי הפיכה של הודעות אסורה בצ׳אט זה. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. מחיקה בלתי הפיכה של הודעות אסורה בקבוצה זו. No comment provided by engineer. @@ -2502,9 +2497,9 @@ Available in v5.1 ההעברה הושלמה No comment provided by engineer. - - Migrations: %@ - העברות: %@ + + Migrations: + העברות: No comment provided by engineer. @@ -2701,8 +2696,8 @@ Available in v5.1 לא ייעשה שימוש במארחי Onion. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only client devices store user profiles, contacts, groups, and messages. No comment provided by engineer. @@ -2761,8 +2756,8 @@ Available in v5.1 Open user profiles authentication reason - - Open-source protocol and code – anybody can run the servers. + + Anybody can host servers. No comment provided by engineer. @@ -2817,8 +2812,8 @@ Available in v5.1 Paste the link you received into the box below to connect with your contact. No comment provided by engineer. - - People can connect to you only via the links you share. + + You decide who can connect. No comment provided by engineer. @@ -3521,8 +3516,8 @@ Available in v5.1 Thanks to the users – contribute via Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. + + No user identifiers. No comment provided by engineer. @@ -3566,16 +3561,16 @@ It can happen because of some bug or when the connection is compromised.The message will be marked as moderated for all members. No comment provided by engineer. - - The next generation of private messaging + + The future of messaging No comment provided by engineer. The old database was not removed during the migration, it can be deleted. No comment provided by engineer. - - The profile is only shared with your contacts. + + Your profile is stored on your device and only shared with your contacts. No comment provided by engineer. @@ -3638,8 +3633,8 @@ It can happen because of some bug or when the connection is compromised.To make a new connection No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. No comment provided by engineer. @@ -3864,8 +3859,8 @@ To connect, please ask your contact to create another connection link and check Voice messages are prohibited in this chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. No comment provided by engineer. @@ -4005,10 +4000,6 @@ SimpleX Lock must be enabled. You can't send messages! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - You could not be verified; please try again. No comment provided by engineer. @@ -4967,8 +4958,8 @@ SimpleX servers cannot see your profile. נמחק No comment provided by engineer. - - Files and media are prohibited in this group. + + Files and media are prohibited. קבצים ומדיה אסורים בקבוצה זו. No comment provided by engineer. @@ -5027,13 +5018,13 @@ SimpleX servers cannot see your profile. הזמן חברים No comment provided by engineer. - - Group members can add message reactions. + + Members can add message reactions. חברי הקבוצה יכולים להוסיף תגובות אמוג׳י להודעות. No comment provided by engineer. - - Group members can send files and media. + + Members can send files and media. חברי הקבוצה יכולים לשלוח קבצים ומדיה. No comment provided by engineer. @@ -5231,8 +5222,8 @@ SimpleX servers cannot see your profile. תגובות אמוג׳י להודעות אסורות בצ׳אט זה. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. תגובות אמוג׳י להודעות אסורות בקבוצה זו. No comment provided by engineer. @@ -5311,6 +5302,283 @@ SimpleX servers cannot see your profile. ללא היסטוריה No comment provided by engineer. + + %@ and %@ + %@ ו-%@ + No comment provided by engineer. + + + Connect automatically + התבר אוטומטי + + + Create profile + צור פרופיל + + + Created at: %@ + נוצר ב:%@ + + + Desktop devices + מכשירי מחשב + + + Discover via local network + גלה באמצעות הרשת המקומית + + + Forward + העבר + + + Group already exists + קבוצה כבר קיימת + + + Connected to desktop + מחובר למחשב + + + Group already exists! + קבוצה כבר קיימת! + + + Confirm upload + אשר ההעלאה + + + Block for all + חסום לכולם + + + Blocked by admin + נחסם ע"י מנהל + + + Block member for all? + לחסום את החבר לכולם? + + + Camera not available + מצלמה לא זמינה + + + Connect to desktop + חבר למחשב + + + Created at + נוצר ב + + + (new) + (חדש) + + + Block member + חבר חסום + + + Block member? + לחסום חבר? + + + Creating link… + יוצר קישור… + + + Files + קבצים + + + Disabled + מושבת + + + Enter passphrase + הכנס סיסמא + + + Apply + החל + + + Apply to + החל ל + + + Background + ברקע + + + Black + שחור + + + Blur media + טשטש מדיה + + + Chat theme + צבע ערכת נושא + + + Completed + הושלם + + + Connected + מחובר + + + Connection notifications + התראות חיבור + + + Connections + חיבורים + + + Current profile + פרופיל נוכחי + + + Disconnect desktop? + להתנתק מהמחשב? + + + Discover and join groups + גלה והצטרף לקבוצות + + + Enabled + מופעל + + + Error opening chat + שגיאה בפתיחת הצ'אט + + + Good morning! + בוקר טוב! + + + Connect to yourself? +This is your own SimpleX address! + להתחבר אליך? +זו כתובת הSimpleX שלך! + + + Connect to yourself? + להתחבר אליך? + + + Connect to yourself? +This is your own one-time link! + להתחבר אליך? +זו כתובת ההזמנה החד-פעמי שלך! + + + Connected desktop + מחשב מחובר + + + Connected servers + שרתים מחוברים + + + Enter group name… + הכנס שם לקבוצה… + + + Enter this device name… + הכנס שם למכשיר הזה… + + + Enter your name… + הכנס את השם שלך… + + + Error decrypting file + שגיאה בפענוח הקובץ + + + Errors + שגיאות + + + File status + מצב הקובץ + + + Connecting + מתחבר + + + Connecting to desktop + מתחבר למחשב + + + Deleted + נמחק + + + Deletion errors + שגיאות במחיקה + + + Details + פרטים + + + Forwarded + הועבר + + + Found desktop + נמצא מחשב + + + Good afternoon! + אחר צהריים טובים! + + + Desktop address + כתובת מחשב + + + Forwarded from + הועבר מ + + + History is not sent to new members. + היסטוריה לא נשלחת לחברים חדשים. + + + Created + נוצר + + + Copy error + שגיאת העתקה + + + Create group + צור קבוצה + + + Enabled for + מופעל עבור + + + Error creating message + שגיאה ביצירת הודעה + + + File error + שגיאה בקובץ + diff --git a/apps/ios/SimpleX Localizations/hi.xcloc/Localized Contents/hi.xliff b/apps/ios/SimpleX Localizations/hi.xcloc/Localized Contents/hi.xliff deleted file mode 100644 index 31746eccd9..0000000000 --- a/apps/ios/SimpleX Localizations/hi.xcloc/Localized Contents/hi.xliff +++ /dev/null @@ -1,3554 +0,0 @@ - - - -
- -
- - - - - No comment provided by engineer. - - - - No comment provided by engineer. - - - - No comment provided by engineer. - - - - No comment provided by engineer. - - - ( - No comment provided by engineer. - - - (can be copied) - No comment provided by engineer. - - - !1 colored! - No comment provided by engineer. - - - #secret# - No comment provided by engineer. - - - %@ - No comment provided by engineer. - - - %@ %@ - No comment provided by engineer. - - - %@ / %@ - No comment provided by engineer. - - - %@ is connected! - notification title - - - %@ is not verified - No comment provided by engineer. - - - %@ is verified - No comment provided by engineer. - - - %@ wants to connect! - notification title - - - %d days - message ttl - - - %d hours - message ttl - - - %d min - message ttl - - - %d months - message ttl - - - %d sec - message ttl - - - %d skipped message(s) - integrity error chat item - - - %lld - No comment provided by engineer. - - - %lld %@ - No comment provided by engineer. - - - %lld contact(s) selected - No comment provided by engineer. - - - %lld file(s) with total size of %@ - No comment provided by engineer. - - - %lld members - No comment provided by engineer. - - - %lld second(s) - No comment provided by engineer. - - - %lldd - No comment provided by engineer. - - - %lldh - No comment provided by engineer. - - - %lldk - No comment provided by engineer. - - - %lldm - No comment provided by engineer. - - - %lldmth - No comment provided by engineer. - - - %llds - No comment provided by engineer. - - - %lldw - No comment provided by engineer. - - - ( - No comment provided by engineer. - - - ) - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - No comment provided by engineer. - - - **Create link / QR code** for your contact to use. - No comment provided by engineer. - - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. - No comment provided by engineer. - - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). - No comment provided by engineer. - - - **Paste received link** or open it in the browser and tap **Open in mobile app**. - No comment provided by engineer. - - - **Please note**: you will NOT be able to recover or change passphrase if you lose it. - No comment provided by engineer. - - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. - No comment provided by engineer. - - - **Scan QR code**: to connect to your contact in person or via video call. - No comment provided by engineer. - - - **Warning**: Instant push notifications require passphrase saved in Keychain. - No comment provided by engineer. - - - **e2e encrypted** audio call - No comment provided by engineer. - - - **e2e encrypted** video call - No comment provided by engineer. - - - \*bold* - No comment provided by engineer. - - - , - No comment provided by engineer. - - - . - No comment provided by engineer. - - - 1 day - message ttl - - - 1 hour - message ttl - - - 1 month - message ttl - - - 1 week - message ttl - - - 2 weeks - message ttl - - - 6 - No comment provided by engineer. - - - : - No comment provided by engineer. - - - A new contact - notification title - - - A random profile will be sent to the contact that you received this link from - No comment provided by engineer. - - - A random profile will be sent to your contact - No comment provided by engineer. - - - A separate TCP connection will be used **for each chat profile you have in the app**. - No comment provided by engineer. - - - A separate TCP connection will be used **for each contact and group member**. -**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail. - No comment provided by engineer. - - - About SimpleX - No comment provided by engineer. - - - About SimpleX Chat - No comment provided by engineer. - - - Accent color - No comment provided by engineer. - - - Accept - accept contact request via notification - accept incoming call via notification - - - Accept contact - No comment provided by engineer. - - - Accept contact request from %@? - notification body - - - Accept incognito - No comment provided by engineer. - - - Accept requests - No comment provided by engineer. - - - Add preset servers - No comment provided by engineer. - - - Add profile - No comment provided by engineer. - - - Add servers by scanning QR codes. - No comment provided by engineer. - - - Add server… - No comment provided by engineer. - - - Add to another device - No comment provided by engineer. - - - Admins can create the links to join groups. - No comment provided by engineer. - - - Advanced network settings - No comment provided by engineer. - - - All chats and messages will be deleted - this cannot be undone! - No comment provided by engineer. - - - All group members will remain connected. - No comment provided by engineer. - - - All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. - No comment provided by engineer. - - - All your contacts will remain connected - No comment provided by engineer. - - - Allow - No comment provided by engineer. - - - Allow disappearing messages only if your contact allows it to you. - No comment provided by engineer. - - - Allow irreversible message deletion only if your contact allows it to you. - No comment provided by engineer. - - - Allow sending direct messages to members. - No comment provided by engineer. - - - Allow sending disappearing messages. - No comment provided by engineer. - - - Allow to irreversibly delete sent messages. - No comment provided by engineer. - - - Allow to send voice messages. - No comment provided by engineer. - - - Allow voice messages only if your contact allows them. - No comment provided by engineer. - - - Allow voice messages? - No comment provided by engineer. - - - Allow your contacts to irreversibly delete sent messages. - No comment provided by engineer. - - - Allow your contacts to send disappearing messages. - No comment provided by engineer. - - - Allow your contacts to send voice messages. - No comment provided by engineer. - - - Already connected? - No comment provided by engineer. - - - Answer call - No comment provided by engineer. - - - App build: %@ - No comment provided by engineer. - - - App icon - No comment provided by engineer. - - - App version - No comment provided by engineer. - - - App version: v%@ - No comment provided by engineer. - - - Appearance - No comment provided by engineer. - - - Attach - No comment provided by engineer. - - - Audio & video calls - No comment provided by engineer. - - - Authentication failed - No comment provided by engineer. - - - Authentication unavailable - No comment provided by engineer. - - - Auto-accept contact requests - No comment provided by engineer. - - - Auto-accept images - No comment provided by engineer. - - - Automatically - No comment provided by engineer. - - - Back - No comment provided by engineer. - - - Both you and your contact can irreversibly delete sent messages. - No comment provided by engineer. - - - Both you and your contact can send disappearing messages. - No comment provided by engineer. - - - Both you and your contact can send voice messages. - No comment provided by engineer. - - - Call already ended! - No comment provided by engineer. - - - Calls - No comment provided by engineer. - - - Can't invite contact! - No comment provided by engineer. - - - Can't invite contacts! - No comment provided by engineer. - - - Cancel - No comment provided by engineer. - - - Cannot access keychain to save database password - No comment provided by engineer. - - - Cannot receive file - No comment provided by engineer. - - - Change - No comment provided by engineer. - - - Change database passphrase? - No comment provided by engineer. - - - Change member role? - No comment provided by engineer. - - - Change receiving address - No comment provided by engineer. - - - Change receiving address? - No comment provided by engineer. - - - Change role - No comment provided by engineer. - - - Chat archive - No comment provided by engineer. - - - Chat console - No comment provided by engineer. - - - Chat database - No comment provided by engineer. - - - Chat database deleted - No comment provided by engineer. - - - Chat database imported - No comment provided by engineer. - - - Chat is running - No comment provided by engineer. - - - Chat is stopped - No comment provided by engineer. - - - Chat preferences - No comment provided by engineer. - - - Chats - No comment provided by engineer. - - - Check server address and try again. - No comment provided by engineer. - - - Choose file - No comment provided by engineer. - - - Choose from library - No comment provided by engineer. - - - Clear - No comment provided by engineer. - - - Clear conversation - No comment provided by engineer. - - - Clear conversation? - No comment provided by engineer. - - - Clear verification - No comment provided by engineer. - - - Colors - No comment provided by engineer. - - - Compare security codes with your contacts. - No comment provided by engineer. - - - Configure ICE servers - No comment provided by engineer. - - - Confirm - No comment provided by engineer. - - - Confirm new passphrase… - No comment provided by engineer. - - - Connect - server test step - - - Connect via contact link? - No comment provided by engineer. - - - Connect via group link? - No comment provided by engineer. - - - Connect via link - No comment provided by engineer. - - - Connect via link / QR code - No comment provided by engineer. - - - Connect via one-time link? - No comment provided by engineer. - - - Connect via relay - No comment provided by engineer. - - - Connecting to server… - No comment provided by engineer. - - - Connecting to server… (error: %@) - No comment provided by engineer. - - - Connection - No comment provided by engineer. - - - Connection error - No comment provided by engineer. - - - Connection error (AUTH) - No comment provided by engineer. - - - Connection request - No comment provided by engineer. - - - Connection request sent! - No comment provided by engineer. - - - Connection timeout - No comment provided by engineer. - - - Contact allows - No comment provided by engineer. - - - Contact already exists - No comment provided by engineer. - - - Contact and all messages will be deleted - this cannot be undone! - No comment provided by engineer. - - - Contact hidden: - notification - - - Contact is connected - notification - - - Contact is not connected yet! - No comment provided by engineer. - - - Contact name - No comment provided by engineer. - - - Contact preferences - No comment provided by engineer. - - - Contact requests - No comment provided by engineer. - - - Contacts can mark messages for deletion; you will be able to view them. - No comment provided by engineer. - - - Copy - chat item action - - - Core built at: %@ - No comment provided by engineer. - - - Core version: v%@ - No comment provided by engineer. - - - Create - No comment provided by engineer. - - - Create address - No comment provided by engineer. - - - Create group link - No comment provided by engineer. - - - Create link - No comment provided by engineer. - - - Create one-time invitation link - No comment provided by engineer. - - - Create queue - server test step - - - Create secret group - No comment provided by engineer. - - - Create your profile - No comment provided by engineer. - - - Created on %@ - No comment provided by engineer. - - - Current passphrase… - No comment provided by engineer. - - - Currently maximum supported file size is %@. - No comment provided by engineer. - - - Dark - No comment provided by engineer. - - - Data - No comment provided by engineer. - - - Database ID - No comment provided by engineer. - - - Database encrypted! - No comment provided by engineer. - - - Database encryption passphrase will be updated and stored in the keychain. - - No comment provided by engineer. - - - Database encryption passphrase will be updated. - - No comment provided by engineer. - - - Database error - No comment provided by engineer. - - - Database is encrypted using a random passphrase, you can change it. - No comment provided by engineer. - - - Database is encrypted using a random passphrase. Please change it before exporting. - No comment provided by engineer. - - - Database passphrase - No comment provided by engineer. - - - Database passphrase & export - No comment provided by engineer. - - - Database passphrase is different from saved in the keychain. - No comment provided by engineer. - - - Database passphrase is required to open chat. - No comment provided by engineer. - - - Database will be encrypted and the passphrase stored in the keychain. - - No comment provided by engineer. - - - Database will be encrypted. - - No comment provided by engineer. - - - Database will be migrated when the app restarts - No comment provided by engineer. - - - Decentralized - No comment provided by engineer. - - - Delete - chat item action - - - Delete Contact - No comment provided by engineer. - - - Delete address - No comment provided by engineer. - - - Delete address? - No comment provided by engineer. - - - Delete after - No comment provided by engineer. - - - Delete all files - No comment provided by engineer. - - - Delete archive - No comment provided by engineer. - - - Delete chat archive? - No comment provided by engineer. - - - Delete chat profile? - No comment provided by engineer. - - - Delete connection - No comment provided by engineer. - - - Delete contact - No comment provided by engineer. - - - Delete contact? - No comment provided by engineer. - - - Delete database - No comment provided by engineer. - - - Delete files & media - No comment provided by engineer. - - - Delete files and media? - No comment provided by engineer. - - - Delete files for all chat profiles - No comment provided by engineer. - - - Delete for everyone - chat feature - - - Delete for me - No comment provided by engineer. - - - Delete group - No comment provided by engineer. - - - Delete group? - No comment provided by engineer. - - - Delete invitation - No comment provided by engineer. - - - Delete link - No comment provided by engineer. - - - Delete link? - No comment provided by engineer. - - - Delete message? - No comment provided by engineer. - - - Delete messages - No comment provided by engineer. - - - Delete messages after - No comment provided by engineer. - - - Delete old database - No comment provided by engineer. - - - Delete old database? - No comment provided by engineer. - - - Delete pending connection - No comment provided by engineer. - - - Delete pending connection? - No comment provided by engineer. - - - Delete queue - server test step - - - Delete user profile? - No comment provided by engineer. - - - Description - No comment provided by engineer. - - - Develop - No comment provided by engineer. - - - Developer tools - No comment provided by engineer. - - - Device - No comment provided by engineer. - - - Device authentication is disabled. Turning off SimpleX Lock. - No comment provided by engineer. - - - Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication. - No comment provided by engineer. - - - Direct messages - chat feature - - - Direct messages between members are prohibited in this group. - No comment provided by engineer. - - - Disable SimpleX Lock - authentication reason - - - Disappearing messages - chat feature - - - Disappearing messages are prohibited in this chat. - No comment provided by engineer. - - - Disappearing messages are prohibited in this group. - No comment provided by engineer. - - - Disconnect - server test step - - - Display name - No comment provided by engineer. - - - Display name: - No comment provided by engineer. - - - Do NOT use SimpleX for emergency calls. - No comment provided by engineer. - - - Do it later - No comment provided by engineer. - - - Edit - chat item action - - - Edit group profile - No comment provided by engineer. - - - Enable - No comment provided by engineer. - - - Enable SimpleX Lock - authentication reason - - - Enable TCP keep-alive - No comment provided by engineer. - - - Enable automatic message deletion? - No comment provided by engineer. - - - Enable instant notifications? - No comment provided by engineer. - - - Enable notifications - No comment provided by engineer. - - - Enable periodic notifications? - No comment provided by engineer. - - - Encrypt - No comment provided by engineer. - - - Encrypt database? - No comment provided by engineer. - - - Encrypted database - No comment provided by engineer. - - - Encrypted message or another event - notification - - - Encrypted message: database error - notification - - - Encrypted message: keychain error - notification - - - Encrypted message: no passphrase - notification - - - Encrypted message: unexpected error - notification - - - Enter correct passphrase. - No comment provided by engineer. - - - Enter passphrase… - No comment provided by engineer. - - - Enter server manually - No comment provided by engineer. - - - Error - No comment provided by engineer. - - - Error accepting contact request - No comment provided by engineer. - - - Error accessing database file - No comment provided by engineer. - - - Error adding member(s) - No comment provided by engineer. - - - Error changing address - No comment provided by engineer. - - - Error changing role - No comment provided by engineer. - - - Error changing setting - No comment provided by engineer. - - - Error creating address - No comment provided by engineer. - - - Error creating group - No comment provided by engineer. - - - Error creating group link - No comment provided by engineer. - - - Error deleting chat database - No comment provided by engineer. - - - Error deleting chat! - No comment provided by engineer. - - - Error deleting connection - No comment provided by engineer. - - - Error deleting contact - No comment provided by engineer. - - - Error deleting database - No comment provided by engineer. - - - Error deleting old database - No comment provided by engineer. - - - Error deleting token - No comment provided by engineer. - - - Error deleting user profile - No comment provided by engineer. - - - Error enabling notifications - No comment provided by engineer. - - - Error encrypting database - No comment provided by engineer. - - - Error exporting chat database - No comment provided by engineer. - - - Error importing chat database - No comment provided by engineer. - - - Error joining group - No comment provided by engineer. - - - Error receiving file - No comment provided by engineer. - - - Error removing member - No comment provided by engineer. - - - Error saving ICE servers - No comment provided by engineer. - - - Error saving SMP servers - No comment provided by engineer. - - - Error saving group profile - No comment provided by engineer. - - - Error saving passphrase to keychain - No comment provided by engineer. - - - Error sending message - No comment provided by engineer. - - - Error starting chat - No comment provided by engineer. - - - Error stopping chat - No comment provided by engineer. - - - Error updating message - No comment provided by engineer. - - - Error updating settings - No comment provided by engineer. - - - Error: %@ - No comment provided by engineer. - - - Error: URL is invalid - No comment provided by engineer. - - - Error: no database file - No comment provided by engineer. - - - Exit without saving - No comment provided by engineer. - - - Export database - No comment provided by engineer. - - - Export error: - No comment provided by engineer. - - - Exported database archive. - No comment provided by engineer. - - - Exporting database archive... - No comment provided by engineer. - - - Failed to remove passphrase - No comment provided by engineer. - - - File will be received when your contact is online, please wait or check later! - No comment provided by engineer. - - - File: %@ - No comment provided by engineer. - - - Files & media - No comment provided by engineer. - - - For console - No comment provided by engineer. - - - Full link - No comment provided by engineer. - - - Full name (optional) - No comment provided by engineer. - - - Full name: - No comment provided by engineer. - - - GIFs and stickers - No comment provided by engineer. - - - Group - No comment provided by engineer. - - - Group display name - No comment provided by engineer. - - - Group full name (optional) - No comment provided by engineer. - - - Group image - No comment provided by engineer. - - - Group invitation - No comment provided by engineer. - - - Group invitation expired - No comment provided by engineer. - - - Group invitation is no longer valid, it was removed by sender. - No comment provided by engineer. - - - Group link - No comment provided by engineer. - - - Group links - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. - No comment provided by engineer. - - - Group members can send direct messages. - No comment provided by engineer. - - - Group members can send disappearing messages. - No comment provided by engineer. - - - Group members can send voice messages. - No comment provided by engineer. - - - Group message: - notification - - - Group preferences - No comment provided by engineer. - - - Group profile - No comment provided by engineer. - - - Group profile is stored on members' devices, not on the servers. - No comment provided by engineer. - - - Group will be deleted for all members - this cannot be undone! - No comment provided by engineer. - - - Group will be deleted for you - this cannot be undone! - No comment provided by engineer. - - - Help - No comment provided by engineer. - - - Hidden - No comment provided by engineer. - - - Hide - chat item action - - - Hide app screen in the recent apps. - No comment provided by engineer. - - - How SimpleX works - No comment provided by engineer. - - - How it works - No comment provided by engineer. - - - How to - No comment provided by engineer. - - - How to use it - No comment provided by engineer. - - - How to use your servers - No comment provided by engineer. - - - ICE servers (one per line) - No comment provided by engineer. - - - If the video fails to connect, flip the camera to resolve it. - No comment provided by engineer. - - - If you can't meet in person, **show QR code in the video call**, or share the link. - No comment provided by engineer. - - - If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link. - No comment provided by engineer. - - - If you need to use the chat now tap **Do it later** below (you will be offered to migrate the database when you restart the app). - No comment provided by engineer. - - - Ignore - No comment provided by engineer. - - - Image will be received when your contact is online, please wait or check later! - No comment provided by engineer. - - - Immune to spam and abuse - No comment provided by engineer. - - - Import - No comment provided by engineer. - - - Import chat database? - No comment provided by engineer. - - - Import database - No comment provided by engineer. - - - Improved privacy and security - No comment provided by engineer. - - - Improved server configuration - No comment provided by engineer. - - - Incognito - No comment provided by engineer. - - - Incognito mode - No comment provided by engineer. - - - Incognito mode is not supported here - your main profile will be sent to group members - No comment provided by engineer. - - - Incognito mode protects the privacy of your main profile name and image — for each new contact a new random profile is created. - No comment provided by engineer. - - - Incoming audio call - notification - - - Incoming call - notification - - - Incoming video call - notification - - - Incorrect security code! - No comment provided by engineer. - - - Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) - No comment provided by engineer. - - - Instant push notifications will be hidden! - - No comment provided by engineer. - - - Instantly - No comment provided by engineer. - - - Invalid connection link - No comment provided by engineer. - - - Invalid server address! - No comment provided by engineer. - - - Invitation expired! - No comment provided by engineer. - - - Invite members - No comment provided by engineer. - - - Invite to group - No comment provided by engineer. - - - Irreversible message deletion - No comment provided by engineer. - - - Irreversible message deletion is prohibited in this chat. - No comment provided by engineer. - - - Irreversible message deletion is prohibited in this group. - No comment provided by engineer. - - - It allows having many anonymous connections without any shared data between them in a single chat profile. - No comment provided by engineer. - - - It can happen when: -1. The messages expire on the server if they were not received for 30 days, -2. The server you use to receive the messages from this contact was updated and restarted. -3. The connection is compromised. -Please connect to the developers via Settings to receive the updates about the servers. -We will be adding server redundancy to prevent lost messages. - No comment provided by engineer. - - - It seems like you are already connected via this link. If it is not the case, there was an error (%@). - No comment provided by engineer. - - - Join - No comment provided by engineer. - - - Join group - No comment provided by engineer. - - - Join incognito - No comment provided by engineer. - - - Joining group - No comment provided by engineer. - - - Keychain error - No comment provided by engineer. - - - LIVE - No comment provided by engineer. - - - Large file! - No comment provided by engineer. - - - Leave - No comment provided by engineer. - - - Leave group - No comment provided by engineer. - - - Leave group? - No comment provided by engineer. - - - Light - No comment provided by engineer. - - - Limitations - No comment provided by engineer. - - - Live message! - No comment provided by engineer. - - - Live messages - No comment provided by engineer. - - - Local name - No comment provided by engineer. - - - Local profile data only - No comment provided by engineer. - - - Make a private connection - No comment provided by engineer. - - - Make sure SMP server addresses are in correct format, line separated and are not duplicated (%@). - No comment provided by engineer. - - - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. - No comment provided by engineer. - - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - No comment provided by engineer. - - - Mark deleted for everyone - No comment provided by engineer. - - - Mark read - No comment provided by engineer. - - - Mark verified - No comment provided by engineer. - - - Markdown in messages - No comment provided by engineer. - - - Max 30 seconds, received instantly. - No comment provided by engineer. - - - Member - No comment provided by engineer. - - - Member role will be changed to "%@". All group members will be notified. - No comment provided by engineer. - - - Member role will be changed to "%@". The member will receive a new invitation. - No comment provided by engineer. - - - Member will be removed from group - this cannot be undone! - No comment provided by engineer. - - - Message delivery error - No comment provided by engineer. - - - Message text - No comment provided by engineer. - - - Messages - No comment provided by engineer. - - - Migrating database archive... - No comment provided by engineer. - - - Migration error: - No comment provided by engineer. - - - Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat). - No comment provided by engineer. - - - Migration is completed - No comment provided by engineer. - - - Most likely this contact has deleted the connection with you. - No comment provided by engineer. - - - Mute - No comment provided by engineer. - - - Name - No comment provided by engineer. - - - Network & servers - No comment provided by engineer. - - - Network settings - No comment provided by engineer. - - - Network status - No comment provided by engineer. - - - New contact request - notification - - - New contact: - notification - - - New database archive - No comment provided by engineer. - - - New in %@ - No comment provided by engineer. - - - New member role - No comment provided by engineer. - - - New message - notification - - - New passphrase… - No comment provided by engineer. - - - No - No comment provided by engineer. - - - No contacts selected - No comment provided by engineer. - - - No contacts to add - No comment provided by engineer. - - - No device token! - No comment provided by engineer. - - - Group not found! - No comment provided by engineer. - - - No permission to record voice message - No comment provided by engineer. - - - No received or sent files - No comment provided by engineer. - - - Notifications - No comment provided by engineer. - - - Notifications are disabled! - No comment provided by engineer. - - - Off (Local) - No comment provided by engineer. - - - Ok - No comment provided by engineer. - - - Old database - No comment provided by engineer. - - - Old database archive - No comment provided by engineer. - - - One-time invitation link - No comment provided by engineer. - - - Onion hosts will be required for connection. Requires enabling VPN. - No comment provided by engineer. - - - Onion hosts will be used when available. Requires enabling VPN. - No comment provided by engineer. - - - Onion hosts will not be used. - No comment provided by engineer. - - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. - No comment provided by engineer. - - - Only group owners can change group preferences. - No comment provided by engineer. - - - Only group owners can enable voice messages. - No comment provided by engineer. - - - Only you can irreversibly delete messages (your contact can mark them for deletion). - No comment provided by engineer. - - - Only you can send disappearing messages. - No comment provided by engineer. - - - Only you can send voice messages. - No comment provided by engineer. - - - Only your contact can irreversibly delete messages (you can mark them for deletion). - No comment provided by engineer. - - - Only your contact can send disappearing messages. - No comment provided by engineer. - - - Only your contact can send voice messages. - No comment provided by engineer. - - - Open Settings - No comment provided by engineer. - - - Open chat - No comment provided by engineer. - - - Open chat console - authentication reason - - - Open-source protocol and code – anybody can run the servers. - No comment provided by engineer. - - - Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red. - No comment provided by engineer. - - - PING count - No comment provided by engineer. - - - PING interval - No comment provided by engineer. - - - Paste - No comment provided by engineer. - - - Paste image - No comment provided by engineer. - - - Paste received link - No comment provided by engineer. - - - Paste the link you received into the box below to connect with your contact. - No comment provided by engineer. - - - People can connect to you only via the links you share. - No comment provided by engineer. - - - Periodically - No comment provided by engineer. - - - Please ask your contact to enable sending voice messages. - No comment provided by engineer. - - - Please check that you used the correct link or ask your contact to send you another one. - No comment provided by engineer. - - - Please check your network connection with %@ and try again. - No comment provided by engineer. - - - Please check yours and your contact preferences. - No comment provided by engineer. - - - Please enter correct current passphrase. - No comment provided by engineer. - - - Please enter the previous password after restoring database backup. This action can not be undone. - No comment provided by engineer. - - - Please restart the app and migrate the database to enable push notifications. - No comment provided by engineer. - - - Please store passphrase securely, you will NOT be able to access chat if you lose it. - No comment provided by engineer. - - - Please store passphrase securely, you will NOT be able to change it if you lose it. - No comment provided by engineer. - - - Possibly, certificate fingerprint in server address is incorrect - server test error - - - Preset server - No comment provided by engineer. - - - Preset server address - No comment provided by engineer. - - - Privacy & security - No comment provided by engineer. - - - Privacy redefined - No comment provided by engineer. - - - Profile and server connections - No comment provided by engineer. - - - Profile image - No comment provided by engineer. - - - Prohibit irreversible message deletion. - No comment provided by engineer. - - - Prohibit sending direct messages to members. - No comment provided by engineer. - - - Prohibit sending disappearing messages. - No comment provided by engineer. - - - Prohibit sending voice messages. - No comment provided by engineer. - - - Protect app screen - No comment provided by engineer. - - - Protocol timeout - No comment provided by engineer. - - - Push notifications - No comment provided by engineer. - - - Rate the app - No comment provided by engineer. - - - Read - No comment provided by engineer. - - - Read more in our GitHub repository. - No comment provided by engineer. - - - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). - No comment provided by engineer. - - - Received file event - notification - - - Receiving via - No comment provided by engineer. - - - Recipients see updates as you type them. - No comment provided by engineer. - - - Reject - reject incoming call via notification - - - Reject contact (sender NOT notified) - No comment provided by engineer. - - - Reject contact request - No comment provided by engineer. - - - Relay server is only used if necessary. Another party can observe your IP address. - No comment provided by engineer. - - - Relay server protects your IP address, but it can observe the duration of the call. - No comment provided by engineer. - - - Remove - No comment provided by engineer. - - - Remove member - No comment provided by engineer. - - - Remove member? - No comment provided by engineer. - - - Remove passphrase from keychain? - No comment provided by engineer. - - - Reply - chat item action - - - Required - No comment provided by engineer. - - - Reset - No comment provided by engineer. - - - Reset colors - No comment provided by engineer. - - - Reset to defaults - No comment provided by engineer. - - - Restart the app to create a new chat profile - No comment provided by engineer. - - - Restart the app to use imported chat database - No comment provided by engineer. - - - Restore - No comment provided by engineer. - - - Restore database backup - No comment provided by engineer. - - - Restore database backup? - No comment provided by engineer. - - - Restore database error - No comment provided by engineer. - - - Reveal - chat item action - - - Revert - No comment provided by engineer. - - - Role - No comment provided by engineer. - - - Run chat - No comment provided by engineer. - - - SMP servers - No comment provided by engineer. - - - Save - chat item action - - - Save (and notify contacts) - No comment provided by engineer. - - - Save and notify contact - No comment provided by engineer. - - - Save and notify group members - No comment provided by engineer. - - - Save archive - No comment provided by engineer. - - - Save group profile - No comment provided by engineer. - - - Save passphrase and open chat - No comment provided by engineer. - - - Save passphrase in Keychain - No comment provided by engineer. - - - Save preferences? - No comment provided by engineer. - - - Save servers - No comment provided by engineer. - - - Saved WebRTC ICE servers will be removed - No comment provided by engineer. - - - Scan QR code - No comment provided by engineer. - - - Scan code - No comment provided by engineer. - - - Scan security code from your contact's app. - No comment provided by engineer. - - - Scan server QR code - No comment provided by engineer. - - - Search - No comment provided by engineer. - - - Secure queue - server test step - - - Security assessment - No comment provided by engineer. - - - Security code - No comment provided by engineer. - - - Send - No comment provided by engineer. - - - Send a live message - it will update for the recipient(s) as you type it - No comment provided by engineer. - - - Send direct message - No comment provided by engineer. - - - Send link previews - No comment provided by engineer. - - - Send live message - No comment provided by engineer. - - - Send notifications - No comment provided by engineer. - - - Send notifications: - No comment provided by engineer. - - - Send questions and ideas - No comment provided by engineer. - - - Send them from gallery or custom keyboards. - No comment provided by engineer. - - - Sender cancelled file transfer. - No comment provided by engineer. - - - Sender may have deleted the connection request. - No comment provided by engineer. - - - Sending via - No comment provided by engineer. - - - Sent file event - notification - - - Sent messages will be deleted after set time. - No comment provided by engineer. - - - Server requires authorization to create queues, check password - server test error - - - Server test failed! - No comment provided by engineer. - - - Servers - No comment provided by engineer. - - - Set 1 day - No comment provided by engineer. - - - Set contact name… - No comment provided by engineer. - - - Set group preferences - No comment provided by engineer. - - - Set passphrase to export - No comment provided by engineer. - - - Set timeouts for proxy/VPN - No comment provided by engineer. - - - Settings - No comment provided by engineer. - - - Share - chat item action - - - Share invitation link - No comment provided by engineer. - - - Share link - No comment provided by engineer. - - - Share one-time invitation link - No comment provided by engineer. - - - Show QR code - No comment provided by engineer. - - - Show preview - No comment provided by engineer. - - - SimpleX Chat security was audited by Trail of Bits. - No comment provided by engineer. - - - SimpleX Lock - No comment provided by engineer. - - - SimpleX Lock turned on - No comment provided by engineer. - - - SimpleX contact address - simplex link type - - - SimpleX encrypted message or connection event - notification - - - SimpleX group link - simplex link type - - - SimpleX links - No comment provided by engineer. - - - SimpleX one-time invitation - simplex link type - - - Skip - No comment provided by engineer. - - - Skipped messages - No comment provided by engineer. - - - Somebody - notification title - - - Start a new chat - No comment provided by engineer. - - - Start chat - No comment provided by engineer. - - - Start migration - No comment provided by engineer. - - - Stop - No comment provided by engineer. - - - Stop SimpleX - authentication reason - - - Stop chat to enable database actions - No comment provided by engineer. - - - Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. - No comment provided by engineer. - - - Stop chat? - No comment provided by engineer. - - - Support SimpleX Chat - No comment provided by engineer. - - - System - No comment provided by engineer. - - - TCP connection timeout - No comment provided by engineer. - - - TCP_KEEPCNT - No comment provided by engineer. - - - TCP_KEEPIDLE - No comment provided by engineer. - - - TCP_KEEPINTVL - No comment provided by engineer. - - - Take picture - No comment provided by engineer. - - - Tap button - No comment provided by engineer. - - - Tap to join - No comment provided by engineer. - - - Tap to join incognito - No comment provided by engineer. - - - Tap to start a new chat - No comment provided by engineer. - - - Test failed at step %@. - server test failure - - - Test server - No comment provided by engineer. - - - Test servers - No comment provided by engineer. - - - Tests failed! - No comment provided by engineer. - - - Thank you for installing SimpleX Chat! - No comment provided by engineer. - - - The 1st platform without any user identifiers – private by design. - No comment provided by engineer. - - - The app can notify you when you receive messages or contact requests - please open settings to enable. - No comment provided by engineer. - - - The attempt to change database passphrase was not completed. - No comment provided by engineer. - - - The connection you accepted will be cancelled! - No comment provided by engineer. - - - The contact you shared this link with will NOT be able to connect! - No comment provided by engineer. - - - The created archive is available via app Settings / Database / Old database archive. - No comment provided by engineer. - - - The group is fully decentralized – it is visible only to the members. - No comment provided by engineer. - - - The microphone does not work when the app is in the background. - No comment provided by engineer. - - - The next generation of private messaging - No comment provided by engineer. - - - The old database was not removed during the migration, it can be deleted. - No comment provided by engineer. - - - The profile is only shared with your contacts. - No comment provided by engineer. - - - The sender will NOT be notified - No comment provided by engineer. - - - The servers for new connections of your current chat profile **%@**. - No comment provided by engineer. - - - Theme - No comment provided by engineer. - - - This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain. - No comment provided by engineer. - - - This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes. - No comment provided by engineer. - - - This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. - No comment provided by engineer. - - - This feature is experimental! It will only work if the other client has version 4.2 installed. You should see the message in the conversation once the address change is completed – please check that you can still receive messages from this contact (or group member). - No comment provided by engineer. - - - This group no longer exists. - No comment provided by engineer. - - - This setting applies to messages in your current chat profile **%@**. - No comment provided by engineer. - - - To ask any questions and to receive updates: - No comment provided by engineer. - - - To find the profile used for an incognito connection, tap the contact or group name on top of the chat. - No comment provided by engineer. - - - To make a new connection - No comment provided by engineer. - - - To prevent the call interruption, enable Do Not Disturb mode. - No comment provided by engineer. - - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - No comment provided by engineer. - - - To protect your information, turn on SimpleX Lock. -You will be prompted to complete authentication before this feature is enabled. - No comment provided by engineer. - - - To record voice message please grant permission to use Microphone. - No comment provided by engineer. - - - To support instant push notifications the chat database has to be migrated. - No comment provided by engineer. - - - To verify end-to-end encryption with your contact compare (or scan) the code on your devices. - No comment provided by engineer. - - - Transfer images faster - No comment provided by engineer. - - - Transport isolation - No comment provided by engineer. - - - Trying to connect to the server used to receive messages from this contact (error: %@). - No comment provided by engineer. - - - Trying to connect to the server used to receive messages from this contact. - No comment provided by engineer. - - - Turn off - No comment provided by engineer. - - - Turn off notifications? - No comment provided by engineer. - - - Turn on - No comment provided by engineer. - - - Unable to record voice message - No comment provided by engineer. - - - Unexpected error: %@ - No comment provided by engineer. - - - Unexpected migration state - No comment provided by engineer. - - - Unknown database error: %@ - No comment provided by engineer. - - - Unknown error - No comment provided by engineer. - - - Unless your contact deleted the connection or this link was already used, it might be a bug - please report it. -To connect, please ask your contact to create another connection link and check that you have a stable network connection. - No comment provided by engineer. - - - Unlock - authentication reason - - - Unmute - No comment provided by engineer. - - - Unread - No comment provided by engineer. - - - Update - No comment provided by engineer. - - - Update .onion hosts setting? - No comment provided by engineer. - - - Update database passphrase - No comment provided by engineer. - - - Update network settings? - No comment provided by engineer. - - - Update transport isolation mode? - No comment provided by engineer. - - - Updating settings will re-connect the client to all servers. - No comment provided by engineer. - - - Updating this setting will re-connect the client to all servers. - No comment provided by engineer. - - - Use .onion hosts - No comment provided by engineer. - - - Use SimpleX Chat servers? - No comment provided by engineer. - - - Use chat - No comment provided by engineer. - - - Use for new connections - No comment provided by engineer. - - - Use server - No comment provided by engineer. - - - User profile - No comment provided by engineer. - - - Using .onion hosts requires compatible VPN provider. - No comment provided by engineer. - - - Using SimpleX Chat servers. - No comment provided by engineer. - - - Verify connection security - No comment provided by engineer. - - - Verify security code - No comment provided by engineer. - - - Via browser - No comment provided by engineer. - - - Video call - No comment provided by engineer. - - - View security code - No comment provided by engineer. - - - Voice messages - chat feature - - - Voice messages are prohibited in this chat. - No comment provided by engineer. - - - Voice messages are prohibited in this group. - No comment provided by engineer. - - - Voice messages prohibited! - No comment provided by engineer. - - - Voice message… - No comment provided by engineer. - - - Waiting for file - No comment provided by engineer. - - - Waiting for image - No comment provided by engineer. - - - WebRTC ICE servers - No comment provided by engineer. - - - Welcome %@! - No comment provided by engineer. - - - Welcome message - No comment provided by engineer. - - - What's new - No comment provided by engineer. - - - When available - No comment provided by engineer. - - - When you share an incognito profile with somebody, this profile will be used for the groups they invite you to. - No comment provided by engineer. - - - With optional welcome message. - No comment provided by engineer. - - - Wrong database passphrase - No comment provided by engineer. - - - Wrong passphrase! - No comment provided by engineer. - - - You - No comment provided by engineer. - - - You accepted connection - No comment provided by engineer. - - - You allow - No comment provided by engineer. - - - You are already connected to %@. - No comment provided by engineer. - - - You are connected to the server used to receive messages from this contact. - No comment provided by engineer. - - - You are invited to group - No comment provided by engineer. - - - You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button. - No comment provided by engineer. - - - You can now send messages to %@ - notification body - - - You can set lock screen notification preview via settings. - No comment provided by engineer. - - - You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it. - No comment provided by engineer. - - - You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it. - No comment provided by engineer. - - - You can start chat via app Settings / Database or by restarting the app - No comment provided by engineer. - - - You can use markdown to format messages: - No comment provided by engineer. - - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - - - You could not be verified; please try again. - No comment provided by engineer. - - - You have no chats - No comment provided by engineer. - - - You have to enter passphrase every time the app starts - it is not stored on the device. - No comment provided by engineer. - - - You invited your contact - No comment provided by engineer. - - - You joined this group - No comment provided by engineer. - - - You joined this group. Connecting to inviting group member. - No comment provided by engineer. - - - You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. - No comment provided by engineer. - - - You need to allow your contact to send voice messages to be able to send them. - No comment provided by engineer. - - - You rejected group invitation - No comment provided by engineer. - - - You sent group invitation - No comment provided by engineer. - - - You will be connected to group when the group host's device is online, please wait or check later! - No comment provided by engineer. - - - You will be connected when your connection request is accepted, please wait or check later! - No comment provided by engineer. - - - You will be connected when your contact's device is online, please wait or check later! - No comment provided by engineer. - - - You will be required to authenticate when you start or resume the app after 30 seconds in background. - No comment provided by engineer. - - - You will join a group this link refers to and connect to its group members. - No comment provided by engineer. - - - You will stop receiving messages from this group. Chat history will be preserved. - No comment provided by engineer. - - - You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile - No comment provided by engineer. - - - You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed - No comment provided by engineer. - - - Your ICE servers - No comment provided by engineer. - - - Your SMP servers - No comment provided by engineer. - - - Your SimpleX contact address - No comment provided by engineer. - - - Your calls - No comment provided by engineer. - - - Your chat database - No comment provided by engineer. - - - Your chat database is not encrypted - set passphrase to encrypt it. - No comment provided by engineer. - - - Your chat profile - No comment provided by engineer. - - - Your chat profile will be sent to group members - No comment provided by engineer. - - - Your chat profile will be sent to your contact - No comment provided by engineer. - - - Your chat profiles - No comment provided by engineer. - - - Your chat profiles are stored locally, only on your device. - No comment provided by engineer. - - - Your chats - No comment provided by engineer. - - - Your contact address - No comment provided by engineer. - - - Your contact can scan it from the app. - No comment provided by engineer. - - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - No comment provided by engineer. - - - Your contact sent a file that is larger than currently supported maximum size (%@). - No comment provided by engineer. - - - Your contacts can allow full message deletion. - No comment provided by engineer. - - - Your current chat database will be DELETED and REPLACED with the imported one. - No comment provided by engineer. - - - Your current profile - No comment provided by engineer. - - - Your preferences - No comment provided by engineer. - - - Your privacy - No comment provided by engineer. - - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - No comment provided by engineer. - - - Your profile will be sent to the contact that you received this link from - No comment provided by engineer. - - - Your profile, contacts and delivered messages are stored on your device. - No comment provided by engineer. - - - Your random profile - No comment provided by engineer. - - - Your server - No comment provided by engineer. - - - Your server address - No comment provided by engineer. - - - Your settings - No comment provided by engineer. - - - [Contribute](https://github.com/simplex-chat/simplex-chat#contribute) - No comment provided by engineer. - - - [Send us email](mailto:chat@simplex.chat) - No comment provided by engineer. - - - [Star on GitHub](https://github.com/simplex-chat/simplex-chat) - No comment provided by engineer. - - - \_italic_ - No comment provided by engineer. - - - \`a + b` - No comment provided by engineer. - - - above, then choose: - No comment provided by engineer. - - - accepted call - call status - - - admin - member role - - - always - pref value - - - audio call (not e2e encrypted) - No comment provided by engineer. - - - bad message ID - integrity error chat item - - - bad message hash - integrity error chat item - - - bold - No comment provided by engineer. - - - call error - call status - - - call in progress - call status - - - calling… - call status - - - cancelled %@ - feature offered item - - - changed address for you - chat item text - - - changed role of %1$@ to %2$@ - rcv group event chat item - - - changed your role to %@ - rcv group event chat item - - - changing address for %@... - chat item text - - - changing address... - chat item text - - - colored - No comment provided by engineer. - - - complete - No comment provided by engineer. - - - connect to SimpleX Chat developers. - No comment provided by engineer. - - - connected - No comment provided by engineer. - - - connecting - No comment provided by engineer. - - - connecting (accepted) - No comment provided by engineer. - - - connecting (announced) - No comment provided by engineer. - - - connecting (introduced) - No comment provided by engineer. - - - connecting (introduction invitation) - No comment provided by engineer. - - - connecting call… - call status - - - connecting… - chat list item title - - - connection established - chat list item title (it should not be shown - - - connection:%@ - connection information - - - contact has e2e encryption - No comment provided by engineer. - - - contact has no e2e encryption - No comment provided by engineer. - - - creator - No comment provided by engineer. - - - default (%@) - pref value - - - deleted - deleted chat item - - - deleted group - rcv group event chat item - - - direct - connection level description - - - duplicate message - integrity error chat item - - - e2e encrypted - No comment provided by engineer. - - - enabled - enabled status - - - enabled for contact - enabled status - - - enabled for you - enabled status - - - ended - No comment provided by engineer. - - - ended call %@ - call status - - - error - No comment provided by engineer. - - - group deleted - No comment provided by engineer. - - - group profile updated - snd group event chat item - - - iOS Keychain is used to securely store passphrase - it allows receiving push notifications. - No comment provided by engineer. - - - iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications. - No comment provided by engineer. - - - incognito via contact address link - chat list item description - - - incognito via group link - chat list item description - - - incognito via one-time link - chat list item description - - - indirect (%d) - connection level description - - - invalid chat - invalid chat data - - - invalid chat data - No comment provided by engineer. - - - invalid data - invalid chat item - - - invitation to group %@ - group name - - - invited - No comment provided by engineer. - - - invited %@ - rcv group event chat item - - - invited to connect - chat list item title - - - invited via your group link - rcv group event chat item - - - italic - No comment provided by engineer. - - - join as %@ - No comment provided by engineer. - - - left - rcv group event chat item - - - marked deleted - marked deleted chat item preview text - - - member - member role - - - connected - rcv group event chat item - - - message received - notification - - - missed call - call status - - - never - No comment provided by engineer. - - - new message - notification - - - no - pref value - - - no e2e encryption - No comment provided by engineer. - - - off - enabled status - group pref value - - - offered %@ - feature offered item - - - offered %1$@: %2$@ - feature offered item - - - on - group pref value - - - or chat with the developers - No comment provided by engineer. - - - owner - member role - - - peer-to-peer - No comment provided by engineer. - - - received answer… - No comment provided by engineer. - - - received confirmation… - No comment provided by engineer. - - - rejected call - call status - - - removed - No comment provided by engineer. - - - removed %@ - rcv group event chat item - - - removed you - rcv group event chat item - - - sec - network option - - - secret - No comment provided by engineer. - - - starting… - No comment provided by engineer. - - - strike - No comment provided by engineer. - - - this contact - notification title - - - unknown - connection info - - - updated group profile - rcv group event chat item - - - v%@ (%@) - No comment provided by engineer. - - - via contact address link - chat list item description - - - via group link - chat list item description - - - via one-time link - chat list item description - - - via relay - No comment provided by engineer. - - - video call (not e2e encrypted) - No comment provided by engineer. - - - waiting for answer… - No comment provided by engineer. - - - waiting for confirmation… - No comment provided by engineer. - - - wants to connect to you! - No comment provided by engineer. - - - yes - pref value - - - you are invited to group - No comment provided by engineer. - - - you changed address - chat item text - - - you changed address for %@ - chat item text - - - you changed role for yourself to %@ - snd group event chat item - - - you changed role of %1$@ to %2$@ - snd group event chat item - - - you left - snd group event chat item - - - you removed %@ - snd group event chat item - - - you shared one-time link - chat list item description - - - you shared one-time link incognito - chat list item description - - - you: - No comment provided by engineer. - - - \~strike~ - No comment provided by engineer. - - -
- -
- -
- - - SimpleX - Bundle name - - - SimpleX needs camera access to scan QR codes to connect to other users and for video calls. - Privacy - Camera Usage Description - - - SimpleX uses Face ID for local authentication - Privacy - Face ID Usage Description - - - SimpleX needs microphone access for audio and video calls, and to record voice messages. - Privacy - Microphone Usage Description - - - SimpleX needs access to Photo Library for saving captured and received media - Privacy - Photo Library Additions Usage Description - - -
- -
- -
- - - SimpleX NSE - Bundle display name - - - SimpleX NSE - Bundle name - - - Copyright © 2022 SimpleX Chat. All rights reserved. - Copyright (human-readable) - - -
-
diff --git a/apps/ios/SimpleX Localizations/hr.xcloc/Localized Contents/hr.xliff b/apps/ios/SimpleX Localizations/hr.xcloc/Localized Contents/hr.xliff index abf15ee42d..6ad4d159c7 100644 --- a/apps/ios/SimpleX Localizations/hr.xcloc/Localized Contents/hr.xliff +++ b/apps/ios/SimpleX Localizations/hr.xcloc/Localized Contents/hr.xliff @@ -114,12 +114,12 @@
%lld - + No comment provided by engineer. %lld %@ - + No comment provided by engineer. @@ -144,12 +144,12 @@ %lldd - + No comment provided by engineer. %lldh - + No comment provided by engineer. @@ -158,7 +158,7 @@ %lldm - + No comment provided by engineer. @@ -173,17 +173,14 @@ %lldw No comment provided by engineer. - + ( + ( No comment provided by engineer. - + ) - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Dodajte novi kontakt**: da biste stvorili svoj jednokratni QR kôd ili vezu za svoj kontakt. + ) No comment provided by engineer. @@ -191,13 +188,13 @@ **Stvorite vezu / QR kôd** za vaš kontakt. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **Privatnije**: provjeravajte nove poruke svakih 20 minuta. Token uređaja dijeli se s SimpleX Chat poslužiteljem, ali ne i s brojem kontakata ili poruka koje imate. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **Najprivatniji**: nemojte koristiti SimpleX Chat poslužitelj obavijesti, povremeno provjeravajte poruke u pozadini (ovisi o tome koliko često koristite aplikaciju). No comment provided by engineer. @@ -211,8 +208,8 @@ **Imajte na umu**: NEĆETE moći oporaviti ili promijeniti pristupni izraz ako ga izgubite. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Preporučeno**: token uređaja i obavijesti šalju se na poslužitelj obavijesti SimpleX Chata, ali ne i sadržaj poruke, veličinu ili od koga je. No comment provided by engineer. @@ -253,22 +250,22 @@ 1 day - 1 dan + 1 dan message ttl 1 hour - 1 sat + 1 sat message ttl 1 month - 1 mjesec + 1 mesec message ttl 1 week - 1 tjedan + 1 nedelja message ttl @@ -367,8 +364,8 @@ Add servers by scanning QR codes. No comment provided by engineer. - - Add server… + + Add server No comment provided by engineer. @@ -1039,8 +1036,8 @@ Direct messages chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited. No comment provided by engineer. @@ -1055,8 +1052,8 @@ Disappearing messages are prohibited in this chat. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. No comment provided by engineer. @@ -1419,16 +1416,16 @@ Group members can irreversibly delete sent messages. No comment provided by engineer. - - Group members can send direct messages. + + Members can send direct messages. No comment provided by engineer. - - Group members can send disappearing messages. + + Members can send disappearing messages. No comment provided by engineer. - - Group members can send voice messages. + + Members can send voice messages. No comment provided by engineer. @@ -1519,20 +1516,23 @@ Image will be received when your contact is online, please wait or check later! No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam No comment provided by engineer. - + Import + Uvesti No comment provided by engineer. - + Import chat database? + Uvesti data bazu razgovora? No comment provided by engineer. - + Import database + Uvesti data bazu No comment provided by engineer. @@ -1616,8 +1616,8 @@ Irreversible message deletion is prohibited in this chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. No comment provided by engineer. @@ -1693,12 +1693,14 @@ We will be adding server redundancy to prevent lost messages. Live message! No comment provided by engineer. - + Live messages + Žive poruke No comment provided by engineer. - + Local name + Lokalno ime No comment provided by engineer. @@ -1917,8 +1919,8 @@ We will be adding server redundancy to prevent lost messages. Onion hosts will not be used. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only client devices store user profiles, contacts, groups, and messages. No comment provided by engineer. @@ -1965,8 +1967,8 @@ We will be adding server redundancy to prevent lost messages. Open chat console authentication reason - - Open-source protocol and code – anybody can run the servers. + + Anybody can host servers. No comment provided by engineer. @@ -1997,8 +1999,8 @@ We will be adding server redundancy to prevent lost messages. Paste the link you received into the box below to connect with your contact. No comment provided by engineer. - - People can connect to you only via the links you share. + + You decide who can connect. No comment provided by engineer. @@ -2577,8 +2579,8 @@ We will be adding server redundancy to prevent lost messages. Thanks to the users – contribute via Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. + + No user identifiers. No comment provided by engineer. @@ -2609,16 +2611,16 @@ We will be adding server redundancy to prevent lost messages. The microphone does not work when the app is in the background. No comment provided by engineer. - - The next generation of private messaging + + The future of messaging No comment provided by engineer. The old database was not removed during the migration, it can be deleted. No comment provided by engineer. - - The profile is only shared with your contacts. + + Your profile is stored on your device and only shared with your contacts. No comment provided by engineer. @@ -2673,8 +2675,8 @@ We will be adding server redundancy to prevent lost messages. To prevent the call interruption, enable Do Not Disturb mode. No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. No comment provided by engineer. @@ -2847,8 +2849,8 @@ To connect, please ask your contact to create another connection link and check Voice messages are prohibited in this chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. No comment provided by engineer. @@ -2959,10 +2961,6 @@ To connect, please ask your contact to create another connection link and check You can use markdown to format messages: No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - You could not be verified; please try again. No comment provided by engineer. @@ -3161,8 +3159,9 @@ SimpleX servers cannot see your profile. \_italic_ No comment provided by engineer. - + \`a + b` + \`a + b` No comment provided by engineer. @@ -3622,6 +3621,110 @@ SimpleX servers cannot see your profile. \~strike~ No comment provided by engineer. + + # %@ + # %@ + + + %@ server + %@ server + + + %@ servers + %@ serveri + + + Import failed + Uvoz neuspešan + + + %@ downloaded + %@ preuzeto + + + %@ uploaded + %@ otpremljeno + + + 1 minute + 1 minut + + + Password + Šifra + + + ## History + ## Istorija + + + %@ (current) + %@ (trenutan) + + + %@ and %@ + %@ i %@ + + + %@ connected + %@ povezan + + + 0 sec + 0 sek + + + 5 minutes + 5 minuta + + + %@ (current): + %@ (trenutan): + + + %@ and %@ connected + %@ i %@ su povezani + + + %@: + %@: + + + %1$@ at %2$@: + %1$@ u %2$@: + + + 30 seconds + 30 sekundi + + + Password to show + Prikazati šifru + + + %1$@, %2$@ + %1$@, %2$@ + + + 0s + 0s + + + Import theme + Uvesti temu + + + Immediately + Odmah + + + Address settings + Podešavanje adrese + + + Admins can block a member for all. + Administratori mogu da blokiraju + diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff index 546bdf23d3..78bee138e4 100644 --- a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (másolható) @@ -59,7 +32,7 @@ #secret# - #titkos# + #titok# No comment provided by engineer. @@ -94,36 +67,42 @@ %@ and %@ connected - %@ és %@ csatlakozott + %@ és %@ kapcsolódott No comment provided by engineer. %1$@ at %2$@: - %1$@ %2$@-kor: + %1$@ ekkor: %2$@: copied message info, <sender> at <time> %@ connected - %@ csatlakozott + %@ kapcsolódott No comment provided by engineer. %@ downloaded + %@ letöltve No comment provided by engineer. %@ is connected! - %@ csatlakozott! + %@ kapcsolódott! notification title %@ is not verified - %@ nem ellenőrzött + %@ nincs hitelesítve No comment provided by engineer. %@ is verified - %@ ellenőrizve + %@ hitelesítve + No comment provided by engineer. + + + %@ server + %@ kiszolgáló No comment provided by engineer. @@ -133,13 +112,19 @@ %@ uploaded + %@ feltöltve No comment provided by engineer. %@ wants to connect! - %@ csatlakozni szeretne! + %@ kapcsolódni szeretne! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ és további %lld tag @@ -147,7 +132,7 @@ %@, %@ and %lld other members connected - %@, %@ és további %lld tag csatlakozott + %@, %@ és további %lld tag kapcsolódott No comment provided by engineer. @@ -160,11 +145,36 @@ %d nap time interval + + %d file(s) are still being downloaded. + %d fájl letöltése még folyamatban van. + forward confirmation reason + + + %d file(s) failed to download. + Nem sikerült letölteni %d fájlt. + forward confirmation reason + + + %d file(s) were deleted. + %d fájl törölve lett. + forward confirmation reason + + + %d file(s) were not downloaded. + %d fájl nem lett letöltve. + forward confirmation reason + %d hours %d óra time interval + + %d messages not forwarded + %d üzenet nem lett továbbítva + alert title + %d min %d perc @@ -180,9 +190,14 @@ %d mp time interval + + %d seconds(s) + %d másodperc + delete after time + %d skipped message(s) - %d kihagyott üzenet + %d üzenet kihagyva integrity error chat item @@ -202,12 +217,12 @@ %lld contact(s) selected - %lld ismerős kiválasztva + %lld partner kijelölve No comment provided by engineer. %lld file(s) with total size of %@ - %lld fájl, amely(ek)nek teljes mérete: %@ + %lld fájl, %@ összméretben No comment provided by engineer. @@ -222,17 +237,17 @@ %lld messages blocked - %lld üzenet blokkolva + %lld üzenet letiltva No comment provided by engineer. %lld messages blocked by admin - %lld üzenet blokkolva az admin által + %lld üzenetet letiltott az adminisztrátor No comment provided by engineer. %lld messages marked deleted - %lld törlésre megjelölt üzenet + %lld üzenet megjelölve törlésre No comment provided by engineer. @@ -247,27 +262,22 @@ %lld new interface languages - %lld új nyelvi csomag - No comment provided by engineer. - - - %lld second(s) - %lld másodperc + %lld új kezelőfelületi nyelv No comment provided by engineer. %lld seconds - %lld másodperc + %lld mp No comment provided by engineer. %lldd - %lldd + %lldn No comment provided by engineer. %lldh - %lldh + %lldó No comment provided by engineer. @@ -277,37 +287,32 @@ %lldm - %lldm + %lldp No comment provided by engineer. %lldmth - %lldmth + %lldh No comment provided by engineer. %llds - %llds + %lldmp No comment provided by engineer. %lldw - %lldw + %lldhét No comment provided by engineer. %u messages failed to decrypt. - %u üzenet visszafejtése sikertelen. + Nem sikerült visszafejteni %u üzenetet. No comment provided by engineer. %u messages skipped. - %u kihagyott üzenet. - No comment provided by engineer. - - - ( - ( + %u üzenet kihagyva. No comment provided by engineer. @@ -317,60 +322,57 @@ (this device v%@) - (ez az eszköz v%@) + (ez az eszköz: v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - **Ismerős hozzáadása**: új meghívó hivatkozás létrehozásához, vagy egy kapott hivatkozáson keresztül történő csatlakozáshoz. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Új ismerős hozzáadása**: egyszer használatos QR-kód vagy hivatkozás létrehozása a kapcsolattartóhoz. + + **Create 1-time link**: to create and share a new invitation link. + **Partner hozzáadása:** új meghívási hivatkozás létrehozásához, vagy egy kapott hivatkozáson keresztül történő kapcsolódáshoz. No comment provided by engineer. **Create group**: to create a new group. - **Csoport létrehozása**: új csoport létrehozásához. + **Csoport létrehozása:** új csoport létrehozásához. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. - **Privátabb**: 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken megosztásra kerül a SimpleX Chat kiszolgálóval, de az nem, hogy hány ismerőse vagy üzenete van. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. + **Privátabb:** 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken meg lesz osztva a SimpleX Chat-kiszolgálóval, de az nem, hogy hány partnere vagy üzenete van. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). - **Legprivátabb**: ne használja a SimpleX Chat értesítési szervert, rendszeresen ellenőrizze az üzeneteket a háttérben (attól függően, hogy milyen gyakran használja az alkalmazást). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. + **Legprivátabb:** ne használja a SimpleX Chat értesítési kiszolgálót, rendszeresen ellenőrizze az üzeneteket a háttérben (attól függően, hogy milyen gyakran használja az alkalmazást). No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Megjegyzés:** ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja a partnereitől érkező üzenetek visszafejtését. No comment provided by engineer. **Please note**: you will NOT be able to recover or change passphrase if you lose it. - **Figyelem**: NEM tudja visszaállítani vagy megváltoztatni jelmondatát, ha elveszíti azt. + **Megjegyzés:** NEM fogja tudni helyreállítani, vagy módosítani a jelmondatot abban az esetben, ha elveszíti. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. - **Javasolt**: az eszköztoken és az értesítések elküldésre kerülnek a SimpleX Chat értesítési szerverre, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. + **Megjegyzés:** az eszköztoken és az értesítések el lesznek küldve a SimpleX Chat értesítési kiszolgálóra, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik. + No comment provided by engineer. + + + **Scan / Paste link**: to connect via a link you received. + **Hivatkozás beolvasása / beillesztése**: egy kapott hivatkozáson keresztüli kapcsolódáshoz. No comment provided by engineer. **Warning**: Instant push notifications require passphrase saved in Keychain. - **Figyelmeztetés**: Az azonnali push-értesítésekhez a kulcstárolóban tárolt jelmondat megadása szükséges. + **Figyelmeztetés:** Az azonnali push-értesítésekhez a kulcstartóban tárolt jelmondat megadása szükséges. No comment provided by engineer. **Warning**: the archive will be removed. + **Figyelmeztetés:** az archívum el lesz távolítva. No comment provided by engineer. @@ -388,16 +390,11 @@ \*félkövér* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). - faster and more stable. - - kapcsolódás a [könyvtár szolgáltatáshoz] (simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2Ld3%3DWpxkKFeXSPv3pwp %2F%3Fv%3D1-2%26dh %3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6glco6bqjETA)4Beklco6bqj) + - kapcsolódás a [könyvtár szolgáltatáshoz](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - kézbesítési jelentések (legfeljebb 20 tag). - gyorsabb és stabilabb. No comment provided by engineer. @@ -407,7 +404,7 @@ - a bit better groups. - and more! - stabilabb üzenetkézbesítés. -- valamivel jobb csoportok. +- picit továbbfejlesztett csoportok. - és még sok más! No comment provided by engineer. @@ -415,8 +412,8 @@ - optionally notify deleted contacts. - profile names with spaces. - and more! - - opcionális értesítés a törölt kapcsolatokról. -- profilnevek szóközökkel. + - partnerek értesítése a törlésről (nem kötelező) +- profilnevek szóközökkel - és még sok más! No comment provided by engineer. @@ -424,16 +421,11 @@ - voice messages up to 5 minutes. - custom time to disappear. - editing history. - - hangüzenetek legfeljebb 5 perces időtartamig. -- egyedi eltűnési időhatár megadása. + - legfeljebb 5 perc hosszúságú hangüzenetek. +- egyéni üzenet-eltűnési időkorlát. - előzmények szerkesztése. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 mp @@ -447,7 +439,8 @@ 1 day 1 nap - time interval + delete after time +time interval 1 hour @@ -462,12 +455,29 @@ 1 month 1 hónap - time interval + delete after time +time interval 1 week 1 hét - time interval + delete after time +time interval + + + 1 year + 1 év + delete after time + + + 1-time link + Egyszer használható meghívó + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + Az egyszer használható meghívó egy hivatkozás és *csak egyetlen partnerrel használható* – személyesen vagy bármilyen üzenetváltó-alkalmazáson keresztül megosztható. + No comment provided by engineer. 5 minutes @@ -484,43 +494,38 @@ 30 másodperc No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> <p>Üdvözlöm!</p> -<p><a href="%@">Csatlakozzon hozzám a SimpleX Chaten</a></p> +<p><a href="%@">Csatlakozzon hozzám a SimpleX Chaten keresztül</a></p> email text A few more things - Még néhány dolog + Néhány további dolog No comment provided by engineer. A new contact - Egy új ismerős + Egy új partner notification title A new random profile will be shared. - Egy új, véletlenszerű profil kerül megosztásra. + Egy új, véletlenszerű profil lesz megosztva. No comment provided by engineer. A separate TCP connection will be used **for each chat profile you have in the app**. - A rendszer külön TCP-kapcsolatot fog használni **az alkalmazásban található minden csevegési profilhoz**. + **Az összes csevegési profiljához az alkalmazásban** külön TCP-kapcsolat (és SOCKS-hitelesítőadat) lesz használva. No comment provided by engineer. A separate TCP connection will be used **for each contact and group member**. **Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail. - A rendszer külön TCP-kapcsolatot fog használni **minden ismerőshöz és csoporttaghoz**. -**Figyelem**: sok kapcsolódás esetén, az akkumulátor- és adatforgalom fogyasztás jelentősen megnőhet, és egyes kapcsolatok meghiúsulhatnak. + **Az összes partneréhez és csoporttaghoz** külön TCP-kapcsolat (és SOCKS-hitelesítőadat) lesz használva. +**Megjegyzés:** ha sok kapcsolata van, az akkumulátor-használat és az adatforgalom jelentősen megnövekedhet, és néhány kapcsolódási kísérlet sikertelen lehet. No comment provided by engineer. @@ -530,17 +535,12 @@ Abort changing address - Címváltoztatás megszakítása + Cím módosításának megszakítása No comment provided by engineer. Abort changing address? - Címváltoztatás megszakítása?? - No comment provided by engineer. - - - About SimpleX - A SimpleX névjegye + Megszakítja a cím módosítását? No comment provided by engineer. @@ -548,50 +548,82 @@ A SimpleX Chat névjegye No comment provided by engineer. - - About SimpleX address - A SimpleX azonosítóról + + About operators + Az üzemeltetőkről No comment provided by engineer. - - Accent color - Kiemelő szín + + Accent + Kiemelőszín No comment provided by engineer. Accept Elfogadás accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Feltételek elfogadása + No comment provided by engineer. Accept connection request? - Kapcsolatfelvétel elfogadása? + Elfogadja a meghívási kérést? No comment provided by engineer. Accept contact request from %@? - Elfogadja %@ kapcsolat kérését? + Elfogadja %@ meghívási kérését? notification body Accept incognito - Fogadás inkognítóban - accept contact request via notification + Elfogadás inkognitóban + accept contact request via notification +swipe action + + + Accepted conditions + Elfogadott feltételek + No comment provided by engineer. + + + Acknowledged + Visszaigazolt + No comment provided by engineer. + + + Acknowledgement errors + Visszaigazolási hibák + No comment provided by engineer. + + + Active + Aktív + token status text + + + Active connections + Aktív kapcsolatok száma + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. - Azonosító hozzáadása a profilhoz, hogy az ismerősök megoszthassák másokkal. A profilfrissítés elküldésre kerül ismerősők számára. + Cím hozzáadása a profilhoz, hogy a partnerei megoszthassák másokkal. A profilfrissítés el lesz küldve partnerei számára. No comment provided by engineer. - - Add contact - Ismerős hozzáadása + + Add friends + Barátok hozzáadása No comment provided by engineer. - - Add preset servers - Előre beállított kiszolgálók hozzáadása + + Add list + Lista hozzáadása No comment provided by engineer. @@ -599,14 +631,19 @@ Profil hozzáadása No comment provided by engineer. + + Add server + Kiszolgáló hozzáadása + No comment provided by engineer. + Add servers by scanning QR codes. Kiszolgáló hozzáadása QR-kód beolvasásával. No comment provided by engineer. - - Add server… - Kiszolgáló hozzáadása… + + Add team members + Munkatársak hozzáadása No comment provided by engineer. @@ -614,9 +651,44 @@ Hozzáadás egy másik eszközhöz No comment provided by engineer. + + Add to list + Hozzáadás listához + No comment provided by engineer. + Add welcome message - Üdvözlő üzenet hozzáadása + Üdvözlőüzenet hozzáadása + No comment provided by engineer. + + + Add your team members to the conversations. + Adja hozzá a munkatársait a beszélgetésekhez. + No comment provided by engineer. + + + Added media & file servers + Hozzáadott média- és fájlkiszolgálók + No comment provided by engineer. + + + Added message servers + Hozzáadott üzenetkiszolgálók + No comment provided by engineer. + + + Additional accent + További kiemelőszín + No comment provided by engineer. + + + Additional accent 2 + További kiemelőszín 2 + No comment provided by engineer. + + + Additional secondary + További másodlagos szín No comment provided by engineer. @@ -626,16 +698,27 @@ Address change will be aborted. Old receiving address will be used. - A cím módosítása megszakad. A régi fogadási cím kerül felhasználásra. + A cím módosítása meg fog szakadni. A régi fogadási cím lesz használva. + No comment provided by engineer. + + + Address or 1-time link? + Cím vagy egyszer használható meghívó? + No comment provided by engineer. + + + Address settings + Címbeállítások No comment provided by engineer. Admins can block a member for all. + Az adminisztrátorok egy tagot a csoport összes tagja számára letilthatnak. No comment provided by engineer. Admins can create the links to join groups. - Az adminok hivatkozásokat hozhatnak létre a csoportokhoz való csatlakozáshoz. + Az adminisztrátorok hivatkozásokat hozhatnak létre a csoportokhoz való csatlakozáshoz. No comment provided by engineer. @@ -643,53 +726,94 @@ Speciális hálózati beállítások No comment provided by engineer. + + Advanced settings + Speciális beállítások + No comment provided by engineer. + + + All + Összes + No comment provided by engineer. + All app data is deleted. - Minden alkalmazásadat törölve. + Az összes alkalmazásadat törölve. No comment provided by engineer. All chats and messages will be deleted - this cannot be undone! - Minden csevegés és üzenet törlésre kerül - ez nem vonható vissza! + Az összes csevegés és üzenet törölve lesz – ez a művelet nem vonható vissza! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + Az összes csevegés el lesz távolítva a(z) %@ nevű listáról, és a lista is törölve lesz. + alert message + All data is erased when it is entered. - A jelkód megadása után minden adat törlésre kerül. + A jelkód megadása után az összes adat törölve lesz. + No comment provided by engineer. + + + All data is kept private on your device. + Az összes adat privát módon van tárolva az eszközén. No comment provided by engineer. All group members will remain connected. - Minden csoporttag csatlakoztatva marad. + Az összes csoporttag kapcsolatban marad. + No comment provided by engineer. + + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + Az összes üzenet és fájl **végpontok közötti titkosítással**, a közvetlen üzenetek továbbá kvantumbiztos titkosítással is rendelkeznek. No comment provided by engineer. All messages will be deleted - this cannot be undone! - Minden üzenet törlésre kerül – ez nem vonható vissza! + Az összes üzenet törölve lesz – ez a művelet nem vonható vissza! No comment provided by engineer. All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. - Minden üzenet törlésre kerül - ezt nem vonható vissza! Az üzenetek CSAK az ön számára törlődnek. + Az összes üzenet törölve lesz – ez a művelet nem vonható vissza! Az üzenetek CSAK az Ön számára törlődnek. No comment provided by engineer. All new messages from %@ will be hidden! - Minden új üzenet elrejtésre kerül tőle: %@! + %@ összes új üzenete el lesz rejtve! + No comment provided by engineer. + + + All profiles + Összes profil + profile dropdown + + + All reports will be archived for you. + Az összes jelentés archiválva lesz az Ön számára. + No comment provided by engineer. + + + All servers + Összes kiszolgáló No comment provided by engineer. All your contacts will remain connected. - Minden ismerős csatlakoztatva marad. + Az összes partnerével kapcsolatban marad. No comment provided by engineer. All your contacts will remain connected. Profile update will be sent to your contacts. - Ismerőseivel kapcsolatban marad. A profil változtatások frissítésre kerülnek az ismerősöknél. + A partnereivel kapcsolatban marad. A profilfrissítés el lesz küldve a partnerei számára. No comment provided by engineer. All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Az összes partnere, -beszélgetése és -fájlja biztonságosan titkosítva lesz, majd töredékekre bontva feltöltődnek a beállított XFTP-továbbítókiszolgálókra. No comment provided by engineer. @@ -699,92 +823,117 @@ Allow calls only if your contact allows them. - Hívások engedélyezése kizárólag abban az esetben, ha ismerőse is engedélyezi. + A hívások kezdeményezése csak abban az esetben van engedélyezve, ha a partnere is engedélyezi. + No comment provided by engineer. + + + Allow calls? + Engedélyezi a hívásokat? No comment provided by engineer. Allow disappearing messages only if your contact allows it to you. - Eltűnő üzenetek engedélyezése kizárólag abban az esetben, ha ismerőse is engedélyezi az ön számára. + Az eltűnő üzenetek küldése csak abban az esetben van engedélyezve, ha a partnere is engedélyezi az Ön számára. + No comment provided by engineer. + + + Allow downgrade + Visszafejlesztés engedélyezése No comment provided by engineer. Allow irreversible message deletion only if your contact allows it to you. (24 hours) - Üzenet végleges törlésének engedélyezése kizárólag abban az esetben, ha ismerőse is engedélyezi. (24 óra) + Az üzenetek végleges törlése csak abban az esetben van engedélyezve, ha a partnere is engedélyezi. (24 óra) No comment provided by engineer. Allow message reactions only if your contact allows them. - Üzenetreakciók engedélyezése kizárólag abban az esetben, ha ismerőse is engedélyezi. + A reakciók hozzáadása az üzenetekhez csak abban az esetben van engedélyezve, ha a partnere is engedélyezi. No comment provided by engineer. Allow message reactions. - Üzenetreakciók engedélyezése. + A reakciók hozzáadása az üzenetekhez engedélyezve van. No comment provided by engineer. Allow sending direct messages to members. - Közvetlen üzenetek küldésének engedélyezése tagok részére. + A közvetlen üzenetek küldése a tagok között engedélyezve van. No comment provided by engineer. Allow sending disappearing messages. - Eltűnő üzenetek küldésének engedélyezése. + Az eltűnő üzenetek küldése engedélyezve van. + No comment provided by engineer. + + + Allow sharing + Megosztás engedélyezése No comment provided by engineer. Allow to irreversibly delete sent messages. (24 hours) - Elküldött üzenetek visszafordíthatatlan törlésének engedélyezése. (24 óra) + Az elküldött üzenetek végleges törlése engedélyezve van. (24 óra) + No comment provided by engineer. + + + Allow to report messsages to moderators. + Az üzenetek jelentése a moderátorok felé engedélyezve van. + No comment provided by engineer. + + + Allow to send SimpleX links. + A SimpleX-hivatkozások küldése engedélyezve van. No comment provided by engineer. Allow to send files and media. - Fájlok és médiatartalom küldésének engedélyezése. + A fájlok- és a médiatartalmak küldése engedélyezve van. No comment provided by engineer. Allow to send voice messages. - Hangüzenetek küldésének engedélyezése. + A hangüzenetek küldése engedélyezve van. No comment provided by engineer. Allow voice messages only if your contact allows them. - Hangüzenetek küldésének engedélyezése kizárólag abban az esetben, ha ismerőse is engedélyezi. + A hangüzenetek küldése csak abban az esetben van engedélyezve, ha a partnere is engedélyezi. No comment provided by engineer. Allow voice messages? - Hangüzenetek engedélyezése? + Engedélyezi a hangüzeneteket? No comment provided by engineer. Allow your contacts adding message reactions. - Ismerősök általi üzenetreakciók hozzáadásának engedélyezése. + A reakciók hozzáadása az üzenetekhez engedélyezve van a partnerei számára. No comment provided by engineer. Allow your contacts to call you. - Hívások engedélyezése ismerősök számára. + A hívások kezdeményezése engedélyezve van a partnerei számára. No comment provided by engineer. Allow your contacts to irreversibly delete sent messages. (24 hours) - Elküldött üzenetek visszafordíthatatlan törlésének engedélyezése ismerősök számára. (24 óra) + Az elküldött üzenetek végleges törlése engedélyezve van a partnerei számára. (24 óra) No comment provided by engineer. Allow your contacts to send disappearing messages. - Eltűnő üzenetek engedélyezése ismerősök számára. + Az eltűnő üzenetek küldésének engedélyezése a partnerei számára. No comment provided by engineer. Allow your contacts to send voice messages. - Hangüzenetek küldésének engedélyezése ismerősök számára. + A hangüzenetek küldése engedélyezve van a partnerei számára. No comment provided by engineer. Already connected? - Csatlakoztatva? + Már kapcsolódott? No comment provided by engineer. @@ -794,31 +943,47 @@ Already joining the group! - Csatlakozás folyamatban! + A csatlakozás folyamatban van a csoporthoz! + No comment provided by engineer. + + + Always use private routing. + Mindig használjon privát útválasztást. No comment provided by engineer. Always use relay - Mindig használjon átjátszó kiszolgálót + Mindig használjon továbbítókiszolgálót No comment provided by engineer. An empty chat profile with the provided name is created, and the app opens as usual. - Egy üres csevegési profil jön létre a megadott névvel, és az alkalmazás a szokásos módon megnyílik. + Egy üres csevegési profil lesz létrehozva a megadott névvel, és az alkalmazás a szokásos módon megnyílik. No comment provided by engineer. + + Another reason + Egyéb indoklás + report reason + Answer call Hívás fogadása No comment provided by engineer. + + Anybody can host servers. + Bárki üzemeltethet kiszolgálókat. + No comment provided by engineer. + App build: %@ - Az alkalmazás build száma: %@ + Az alkalmazás összeállítási száma: %@ No comment provided by engineer. App data migration + Alkalmazásadatok átköltöztetése No comment provided by engineer. @@ -826,9 +991,14 @@ Az alkalmazás titkosítja a helyi fájlokat (a videók kivételével). No comment provided by engineer. + + App group: + Alkalmazáscsoport: + No comment provided by engineer. + App icon - Alkalmazás ikon + Alkalmazásikon No comment provided by engineer. @@ -838,17 +1008,22 @@ App passcode is replaced with self-destruct passcode. - Az alkalmazás jelkód helyettesítésre kerül egy önmegsemmisítő jelkóddal. + Az alkalmazás-jelkód helyettesítve lesz egy önmegsemmisítő-jelkóddal. + No comment provided by engineer. + + + App session + Alkalmazás munkamenete No comment provided by engineer. App version - Alkalmazás verzió + Az alkalmazás verziója No comment provided by engineer. App version: v%@ - Alkalmazás verzió: v%@ + Az alkalmazás verziója: v%@ No comment provided by engineer. @@ -858,19 +1033,67 @@ Apply + Alkalmaz + No comment provided by engineer. + + + Apply to + Alkalmazás erre + No comment provided by engineer. + + + Archive + Archívum + No comment provided by engineer. + + + Archive %lld reports? + Archivál %lld jelentést? + No comment provided by engineer. + + + Archive all reports? + Archiválja az összes jelentést? No comment provided by engineer. Archive and upload + Archiválás és feltöltés + No comment provided by engineer. + + + Archive contacts to chat later. + A partnerek archiválása a későbbi csevegéshez. + No comment provided by engineer. + + + Archive report + Jelentés archiválása + No comment provided by engineer. + + + Archive report? + Archiválja a jelentést? + No comment provided by engineer. + + + Archive reports + Jelentések archiválása + swipe action + + + Archived contacts + Archivált partnerek No comment provided by engineer. Archiving database + Adatbázis archiválása No comment provided by engineer. Attach - Csatolás + Mellékelés No comment provided by engineer. @@ -885,27 +1108,27 @@ Audio/video calls - Hang-/videóhívások + Hang- és videóhívások chat feature Audio/video calls are prohibited. - A hang- és videóhívások le vannak tiltva. + A hívások kezdeményezése le van tiltva ebben a csevegésben. No comment provided by engineer. Authentication cancelled - Hitelesítés megszakítva + Hitelesítés visszavonva PIN entry Authentication failed - Hitelesítés sikertelen + Sikertelen hitelesítés No comment provided by engineer. Authentication is required before the call is connected, but you may miss calls. - A hívás csatlakoztatása előtt hitelesítésre van szükség, de előfordulhat, hogy nem tud hívásokat fogadni. + A hívás összekapcsolása előtt hitelesítésre van szükség, de előfordulhat, hogy lemarad a hívásokról. No comment provided by engineer. @@ -920,22 +1143,32 @@ Auto-accept contact requests - Ismerős jelölések automatikus elfogadása + Meghívási kérések automatikus elfogadása No comment provided by engineer. Auto-accept images - Fotók automatikus elfogadása + Képek automatikus elfogadása No comment provided by engineer. + + Auto-accept settings + Beállítások automatikus elfogadása + alert title + Back Vissza No comment provided by engineer. + + Background + Háttér + No comment provided by engineer. + Bad desktop address - Hibás számítógép azonosító + Érvénytelen számítógépcím No comment provided by engineer. @@ -945,67 +1178,122 @@ Bad message hash - Téves üzenet hash + Érvénytelen az üzenet hasítóértéke + No comment provided by engineer. + + + Better calls + Továbbfejlesztett hívásélmény No comment provided by engineer. Better groups - Javított csoportok + Továbbfejlesztett csoportok + No comment provided by engineer. + + + Better groups performance + Továbbfejlesztett, gyorsabb csoportok + No comment provided by engineer. + + + Better message dates. + Továbbfejlesztett üzenetdátumok. No comment provided by engineer. Better messages - Jobb üzenetek + Továbbfejlesztett üzenetek + No comment provided by engineer. + + + Better networking + Jobb hálózatkezelés + No comment provided by engineer. + + + Better notifications + Továbbfejlesztett értesítések + No comment provided by engineer. + + + Better privacy and security + Továbbfejlesztett adatvédelem és biztonság + No comment provided by engineer. + + + Better security ✅ + Továbbfejlesztett biztonság ✅ + No comment provided by engineer. + + + Better user experience + Továbbfejlesztett felhasználói élmény + No comment provided by engineer. + + + Black + Fekete No comment provided by engineer. Block - Blokkolás + Letiltás No comment provided by engineer. Block for all - Mindenki számára letiltva + Letiltás No comment provided by engineer. Block group members - Csoporttagok blokkolása + Csoporttagok letiltása No comment provided by engineer. Block member - Tag blokkolása + Letiltás No comment provided by engineer. Block member for all? - Tag letiltása mindenki számára? + Az összes tag számára letiltja a tagot? No comment provided by engineer. Block member? - Tag blokkolása? + Letiltja a tagot? No comment provided by engineer. Blocked by admin - Letiltva az admin által + Letiltva az adminisztrátor által + No comment provided by engineer. + + + Blur for better privacy. + Elhomályosítás a jobb adatvédelemért. + No comment provided by engineer. + + + Blur media + Médiatartalom elhomályosítása No comment provided by engineer. Both you and your contact can add message reactions. - Mindkét fél is hozzáadhat üzenetreakciókat. + Mindkét fél hozzáadhat az üzenetekhez reakciókat. No comment provided by engineer. Both you and your contact can irreversibly delete sent messages. (24 hours) - Mindkét fél visszafordíthatatlanul törölheti az elküldött üzeneteket. (24 óra) + Mindkét fél véglegesen törölheti az elküldött üzeneteket. (24 óra) No comment provided by engineer. Both you and your contact can make calls. - Mindkét fél tud hívásokat indítani. + Mindkét fél tud hívásokat kezdeményezni. No comment provided by engineer. @@ -1023,9 +1311,33 @@ Bolgár, finn, thai és ukrán – köszönet a felhasználóknak és a [Weblate-nek](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + Üzleti cím + No comment provided by engineer. + + + Business chats + Üzleti csevegések + No comment provided by engineer. + + + Businesses + Üzleti + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). - Csevegési profil (alapértelmezett) vagy [kapcsolat alapján] (https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BÉTA). + A csevegési profillal (alapértelmezett), vagy a [kapcsolattal] (https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BÉTA). + No comment provided by engineer. + + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + A SimpleX Chat használatával Ön elfogadja, hogy: +- csak elfogadott tartalmakat tesz közzé a nyilvános csoportokban. +- tiszteletben tartja a többi felhasználót, és nem küld kéretlen tartalmat senkinek. No comment provided by engineer. @@ -1038,94 +1350,156 @@ Hívások No comment provided by engineer. + + Calls prohibited! + A hívások le vannak tiltva! + No comment provided by engineer. + Camera not available - A fényképező nem elérhető + A kamera nem elérhető + No comment provided by engineer. + + + Can't call contact + Nem lehet felhívni a partnert + No comment provided by engineer. + + + Can't call member + Nem lehet felhívni a tagot No comment provided by engineer. Can't invite contact! - Ismerősök meghívása le van tiltva! + Nem lehet meghívni a partnert! No comment provided by engineer. Can't invite contacts! - Ismerősök meghívása nem lehetséges! + Nem lehet meghívni a partnereket! + No comment provided by engineer. + + + Can't message member + Nem lehet üzenetet küldeni a tagnak No comment provided by engineer. Cancel - Megszakítás - No comment provided by engineer. + Mégse + alert action +alert button Cancel migration + Átköltöztetés visszavonása No comment provided by engineer. Cannot access keychain to save database password - Nem lehet hozzáférni a kulcstartóhoz az adatbázis jelszavának mentéséhez + Nem lehet hozzáférni a kulcstartóhoz az adatbázisjelszó mentéséhez + No comment provided by engineer. + + + Cannot forward message + Nem lehet továbbítani az üzenetet No comment provided by engineer. Cannot receive file Nem lehet fogadni a fájlt + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Kapacitás túllépés – a címzett nem kapta meg a korábban elküldött üzeneteket. + snd error text + + + Cellular + Mobilhálózat No comment provided by engineer. Change - Változtatás + Módosítás No comment provided by engineer. + + Change automatic message deletion? + Módosítja az automatikus üzenettörlést? + alert title + + + Change chat profiles + Csevegési profilok módosítása + authentication reason + Change database passphrase? - Adatbázis jelmondat megváltoztatása? + Módosítja az adatbázis jelmondatát? No comment provided by engineer. Change lock mode - Zárolási mód megváltoztatása + Zárolási mód módosítása authentication reason Change member role? - Tag szerepkörének megváltoztatása? + Módosítja a tag szerepkörét? No comment provided by engineer. Change passcode - Jelkód megváltoztatása + Jelkód módosítása authentication reason Change receiving address - A fogadó cím megváltoztatása + Fogadási cím módosítása No comment provided by engineer. Change receiving address? - Megváltoztatja a fogadó címet? + Módosítja a fogadási címet? No comment provided by engineer. Change role - Szerepkör megváltoztatása + Szerepkör módosítása No comment provided by engineer. Change self-destruct mode - Önmegsemmisítő mód megváltoztatása + Önmegsemmisítő-mód módosítása authentication reason Change self-destruct passcode - Önmegsemmisító jelkód megváltoztatása + Önmegsemmisítő-jelkód módosítása authentication reason - set passcode view +set passcode view - - Chat archive - Csevegési archívum + + Chat + Csevegés + No comment provided by engineer. + + + Chat already exists + A csevegés már létezik + No comment provided by engineer. + + + Chat already exists! + A csevegés már létezik! + No comment provided by engineer. + + + Chat colors + Csevegés színei No comment provided by engineer. @@ -1143,6 +1517,11 @@ Csevegési adatbázis törölve No comment provided by engineer. + + Chat database exported + Csevegési adatbázis exportálva + No comment provided by engineer. + Chat database imported Csevegési adatbázis importálva @@ -1155,16 +1534,22 @@ Chat is stopped - A csevegés leállt + A csevegés megállt No comment provided by engineer. Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat. - A csevegés leállt. Ha már használta ezt az adatbázist egy másik eszközön, úgy visszaállítás szükséges a csevegés megkezdése előtt. + A csevegés megállt. Ha már használta ezt az adatbázist egy másik eszközön, úgy visszaállítás szükséges a csevegés elindítása előtt. + No comment provided by engineer. + + + Chat list + Csevegési lista No comment provided by engineer. Chat migrated! + A csevegés átköltöztetve! No comment provided by engineer. @@ -1172,15 +1557,50 @@ Csevegési beállítások No comment provided by engineer. + + Chat preferences were changed. + A csevegési beállítások módosultak. + alert message + + + Chat profile + Csevegési profil + No comment provided by engineer. + + + Chat theme + Csevegés témája + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + A csevegés minden tag számára törölve lesz – ez a művelet nem vonható vissza! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + A csevegés törölve lesz az Ön számára – ez a művelet nem vonható vissza! + No comment provided by engineer. + Chats Csevegések No comment provided by engineer. + + Check messages every 20 min. + Üzenetek ellenőrzése 20 percenként. + No comment provided by engineer. + + + Check messages when allowed. + Üzenetek ellenőrzése, amikor engedélyezett. + No comment provided by engineer. + Check server address and try again. Kiszolgáló címének ellenőrzése és újrapróbálkozás. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1189,6 +1609,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + Válassza az _Átköltöztetés egy másik eszközről_ opciót az új eszközén és olvassa be a QR-kódot. No comment provided by engineer. @@ -1201,24 +1622,49 @@ Választás a könyvtárból No comment provided by engineer. + + Chunks deleted + Törölt töredékek + No comment provided by engineer. + + + Chunks downloaded + Letöltött töredékek + No comment provided by engineer. + + + Chunks uploaded + Feltöltött töredékek + No comment provided by engineer. + Clear Kiürítés - No comment provided by engineer. + swipe action Clear conversation - Beszélgetés kiürítése + Üzenetek kiürítése No comment provided by engineer. Clear conversation? - Beszélgetés kiürítése? + Kiüríti az üzeneteket? + No comment provided by engineer. + + + Clear group? + Kiüríti a csoportot? + No comment provided by engineer. + + + Clear or delete group? + Csoport kiürítése vagy törlése? No comment provided by engineer. Clear private notes? - Privát jegyzetek törlése? + Kiüríti a privát jegyzeteket? No comment provided by engineer. @@ -1226,24 +1672,79 @@ Hitelesítés törlése No comment provided by engineer. - - Colors - Színek + + Color chats with the new themes. + Csevegések színezése új témákkal. No comment provided by engineer. + + Color mode + Színmód + No comment provided by engineer. + + + Community guidelines violation + Közösségi irányelvek megsértése + report reason + Compare file - Fájl összehasonlítás + Fájl-összehasonlítás server test step Compare security codes with your contacts. - Biztonsági kódok összehasonlítása az ismerősökkel. + Biztonsági kódok összehasonlítása a partnerekével. + No comment provided by engineer. + + + Completed + Elkészült + No comment provided by engineer. + + + Conditions accepted on: %@. + Feltételek elfogadásának ideje: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + A következő üzemeltető(k) számára elfogadott feltételek: **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + A feltételek már el lettek fogadva a következő üzemeltető(k) számára: **%@**. + No comment provided by engineer. + + + Conditions of use + Használati feltételek + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + A feltételek el lesznek fogadva a következő üzemeltető(k) számára: **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + A feltételek el lesznek fogadva a következő időpontban: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + A feltételek automatikusan el lesznek fogadva az engedélyezett üzemeltetők számára a következő időpontban: %@. No comment provided by engineer. Configure ICE servers - ICE kiszolgálók beállítása + ICE-kiszolgálók beállítása + No comment provided by engineer. + + + Configure server operators + Kiszolgálóüzemeltetők beállítása No comment provided by engineer. @@ -1256,13 +1757,24 @@ Jelkód megerősítése No comment provided by engineer. + + Confirm contact deletion? + Biztosan törli a partnert? + No comment provided by engineer. + Confirm database upgrades - Adatbázis frissítés megerősítése + Adatbázis fejlesztésének megerősítése + No comment provided by engineer. + + + Confirm files from unknown servers. + Ismeretlen kiszolgálókról származó fájlok megerősítése. No comment provided by engineer. Confirm network settings + Hálózati beállítások megerősítése No comment provided by engineer. @@ -1277,12 +1789,19 @@ Confirm that you remember database passphrase to migrate it. + Az átköltöztetéshez erősítse meg, hogy emlékszik az adatbázis jelmondatára. No comment provided by engineer. Confirm upload + Feltöltés megerősítése No comment provided by engineer. + + Confirmed + Megerősítve + token status text + Connect Kapcsolódás @@ -1295,36 +1814,41 @@ Connect incognito - Inkognítóban csatlakozva + Kapcsolódás inkognitóban No comment provided by engineer. Connect to desktop - Kapcsolódás számítógéphez + Társítás számítógéppel + No comment provided by engineer. + + + Connect to your friends faster. + Kapcsolódjon gyorsabban a partnereihez. No comment provided by engineer. Connect to yourself? - Kapcsolódás saját magához? + Kapcsolódik saját magához? No comment provided by engineer. Connect to yourself? This is your own SimpleX address! - Kapcsolódás saját magához? -Ez a SimpleX azonosítója! + Kapcsolódik saját magához? +Ez a saját SimpleX-címe! No comment provided by engineer. Connect to yourself? This is your own one-time link! - Kapcsolódás saját magához? -Ez az egyszer használatos hivatkozása! + Kapcsolódik saját magához? +Ez a saját egyszer használható meghívója! No comment provided by engineer. Connect via contact address - Kapcsolódás ismerős azonosítója által + Kapcsolódás a kapcsolattartási címen keresztül No comment provided by engineer. @@ -1334,22 +1858,37 @@ Ez az egyszer használatos hivatkozása! Connect via one-time link - Kapcsolódás egyszer használatos hivatkozáson keresztül + Kapcsolódás egyszer használható meghívón keresztül No comment provided by engineer. Connect with %@ - Kapcsolódás ezzel: %@ + Kapcsolódás a következővel: %@ + No comment provided by engineer. + + + Connected + Kapcsolódott No comment provided by engineer. Connected desktop - Csatlakoztatott számítógép + Társított számítógép + No comment provided by engineer. + + + Connected servers + Kapcsolódott kiszolgálók No comment provided by engineer. Connected to desktop - Csatlakozva a számítógéphez + Kapcsolódva a számítógéphez + No comment provided by engineer. + + + Connecting + Kapcsolódás No comment provided by engineer. @@ -1359,7 +1898,12 @@ Ez az egyszer használatos hivatkozása! Connecting to server… (error: %@) - Kapcsolódás a kiszolgálóhoz... (hiba: %@) + Kapcsolódás a kiszolgálóhoz… (hiba: %@) + No comment provided by engineer. + + + Connecting to contact, please wait or check later! + Kapcsolódás a partnerhez, várjon vagy ellenőrizze később! No comment provided by engineer. @@ -1372,6 +1916,16 @@ Ez az egyszer használatos hivatkozása! Kapcsolat No comment provided by engineer. + + Connection and servers status. + Kapcsolatok- és kiszolgálók állapotának megjelenítése. + No comment provided by engineer. + + + Connection blocked + A kapcsolat le van tiltva + No comment provided by engineer. + Connection error Kapcsolódási hiba @@ -1382,9 +1936,36 @@ Ez az egyszer használatos hivatkozása! Kapcsolódási hiba (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + A kiszolgáló üzemeltetője letiltotta a kapcsolatot: +%@ + No comment provided by engineer. + + + Connection not ready. + A kapcsolat nem áll készen. + No comment provided by engineer. + + + Connection notifications + Kapcsolódási értesítések + No comment provided by engineer. + Connection request sent! - Kapcsolódási kérés elküldve! + Meghívási kérés elküldve! + No comment provided by engineer. + + + Connection requires encryption renegotiation. + A kapcsolat titkosítása újraegyeztetést igényel. + No comment provided by engineer. + + + Connection security + Kapcsolatbiztonság No comment provided by engineer. @@ -1394,72 +1975,112 @@ Ez az egyszer használatos hivatkozása! Connection timeout - Kapcsolat időtúllépés + Időtúllépés kapcsolódáskor + No comment provided by engineer. + + + Connection with desktop stopped + A kapcsolat a számítógéppel megszakadt + No comment provided by engineer. + + + Connections + Kapcsolatok No comment provided by engineer. Contact allows - Ismerős engedélyezi + Partner engedélyezi No comment provided by engineer. Contact already exists - Létező ismerős + A partner már létezik + No comment provided by engineer. + + + Contact deleted! + Partner törölve! No comment provided by engineer. Contact hidden: - Ismerős elrejtve: + Rejtett név: notification Contact is connected - Ismerős csatlakozott + Partnere kapcsolódott notification - - Contact is not connected yet! - Az ismerős még nem csatlakozott! + + Contact is deleted. + Törölt partner. No comment provided by engineer. Contact name - Ismerős neve + Csak név No comment provided by engineer. Contact preferences - Ismerős beállításai + Partnerbeállítások + No comment provided by engineer. + + + Contact will be deleted - this cannot be undone! + A partner törölve lesz – ez a művelet nem vonható vissza! No comment provided by engineer. Contacts - Ismerősök + Partnerek No comment provided by engineer. Contacts can mark messages for deletion; you will be able to view them. - Az ismerősök törlésre jelölhetnek üzeneteket ; megtekintheti őket. + A partnerei törlésre jelölhetnek üzeneteket; Ön majd meg tudja nézni azokat. No comment provided by engineer. + + Content violates conditions of use + A tartalom sérti a használati feltételeket + blocking reason + Continue Folytatás No comment provided by engineer. + + Conversation deleted! + Beszélgetés törölve! + No comment provided by engineer. + Copy Másolás - chat item action + No comment provided by engineer. + + + Copy error + Másolási hiba + No comment provided by engineer. Core version: v%@ - Alapverziószám: v%@ + Fő verzió: v%@ + No comment provided by engineer. + + + Corner + Sarok No comment provided by engineer. Correct name to %@? - Név javítása erre: %@? + Helyesbíti a nevet a következőre: %@? No comment provided by engineer. @@ -1467,19 +2088,19 @@ Ez az egyszer használatos hivatkozása! Létrehozás No comment provided by engineer. + + Create 1-time link + Egyszer használható meghívó létrehozása + No comment provided by engineer. + Create SimpleX address - SimpleX azonosító létrehozása + SimpleX-cím létrehozása No comment provided by engineer. Create a group using a random profile. - Csoport létrehozása véletlenszerűen létrehozott profillal. - No comment provided by engineer. - - - Create an address to let people connect with you. - Azonosító létrehozása, hogy az emberek kapcsolatba léphessenek önnel. + Csoport létrehozása véletlenszerű profillal. No comment provided by engineer. @@ -1494,7 +2115,7 @@ Ez az egyszer használatos hivatkozása! Create group link - Csoportos hivatkozás létrehozása + Csoporthivatkozás létrehozása No comment provided by engineer. @@ -1502,9 +2123,14 @@ Ez az egyszer használatos hivatkozása! Hivatkozás létrehozása No comment provided by engineer. + + Create list + Lista létrehozása + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 - Új profil létrehozása az [asztali kliensben](https://simplex.chat/downloads/). 💻 + Új profil létrehozása a [számítógép-alkalmazásban](https://simplex.chat/downloads/). 💻 No comment provided by engineer. @@ -1514,7 +2140,7 @@ Ez az egyszer használatos hivatkozása! Create queue - Várólista létrehozása + Sorba állítás létrehozása server test step @@ -1527,23 +2153,24 @@ Ez az egyszer használatos hivatkozása! Saját profil létrehozása No comment provided by engineer. + + Created + Létrehozva + No comment provided by engineer. + Created at - Létrehozva ekkor + Létrehozva No comment provided by engineer. Created at: %@ - Létrehozva ekkor: %@ + Létrehozva: %@ copied message info - - Created on %@ - Létrehozva %@ - No comment provided by engineer. - Creating archive link + Archívum hivatkozás létrehozása No comment provided by engineer. @@ -1556,19 +2183,39 @@ Ez az egyszer használatos hivatkozása! Jelenlegi jelkód No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + A jelenlegi feltételek szövegét nem lehetett betölteni, a feltételeket a következő hivatkozáson keresztül vizsgálhatja felül: + No comment provided by engineer. + Current passphrase… Jelenlegi jelmondat… No comment provided by engineer. + + Current profile + Jelenlegi profil + No comment provided by engineer. + Currently maximum supported file size is %@. - Jelenleg a maximális támogatott fájlméret %@. + Jelenleg támogatott legnagyobb fájl méret: %@. No comment provided by engineer. Custom time - Személyreszabott idő + Egyéni időköz + No comment provided by engineer. + + + Customizable message shape. + Személyre szabható üzenetbuborékok. + No comment provided by engineer. + + + Customize theme + Téma személyre szabása No comment provided by engineer. @@ -1576,24 +2223,29 @@ Ez az egyszer használatos hivatkozása! Sötét No comment provided by engineer. + + Dark mode colors + Sötét mód színei + No comment provided by engineer. + Database ID - Adatbázis ID + Adatbázis-azonosító No comment provided by engineer. Database ID: %d - Adatbázis azonosító: %d + Adatbázis-azonosító: %d copied message info Database IDs and Transport isolation option. - Adatbázis azonosítók és átviteli izolációs beállítások. + Adatbázis-azonosítók és átvitel-izolációs beállítások. No comment provided by engineer. Database downgrade - Visszatérés a korábbi adatbázis verzióra + Adatbázis visszafejlesztése No comment provided by engineer. @@ -1604,50 +2256,50 @@ Ez az egyszer használatos hivatkozása! Database encryption passphrase will be updated and stored in the keychain. - Az adatbázis titkosítási jelmondata frissül és tárolódik a kulcstárolóban. + Az adatbázis titkosítási jelmondata frissülni fog és a kulcstartóban lesz tárolva. No comment provided by engineer. Database encryption passphrase will be updated. - Adatbázis titkosítási jelmondat frissítve lesz. + Az adatbázis titkosítási jelmondata frissítve lesz. No comment provided by engineer. Database error - Adatbázis hiba + Adatbázishiba No comment provided by engineer. Database is encrypted using a random passphrase, you can change it. - Az adatbázis egy véletlenszerű jelmondattal van titkosítva, megváltoztatható. + Az adatbázis egy véletlenszerű jelmondattal van titkosítva, amelyet szabadon módosíthat. No comment provided by engineer. Database is encrypted using a random passphrase. Please change it before exporting. - Az adatbázis egy véletlenszerű jelmondattal van titkosítva. Exportálás előtti módosítás szükséges. + Az adatbázis egy véletlenszerű jelmondattal van titkosítva. Exportálás előtt módosítsa. No comment provided by engineer. Database passphrase - Adatbázis jelmondat + Adatbázis-jelmondat No comment provided by engineer. Database passphrase & export - Adatbázis jelmondat és exportálás + Adatbázis-jelmondat és -exportálás No comment provided by engineer. Database passphrase is different from saved in the keychain. - Az adatbázis jelmondata eltér a kulcstárlóban mentettől. + Az adatbázis jelmondata nem egyezik a kulcstartóba mentettől. No comment provided by engineer. Database passphrase is required to open chat. - Adatbázis jelmondat szükséges a csevegés megnyitásához. + A csevegés megnyitásához adja meg az adatbázis jelmondatát. No comment provided by engineer. @@ -1658,20 +2310,25 @@ Ez az egyszer használatos hivatkozása! Database will be encrypted and the passphrase stored in the keychain. - Az adatbázis titkosítva lesz, a jelmondat pedig a kulcstárolóban lesz tárolva. + Az adatbázis titkosítva lesz, a jelmondat pedig a kulcstartóban lesz tárolva. No comment provided by engineer. Database will be encrypted. - Az adatbázis titkosításra kerül. + Az adatbázis titkosítva lesz. No comment provided by engineer. Database will be migrated when the app restarts - Az adatbázis az alkalmazás újraindításakor migrálásra kerül + Az adatbázis az alkalmazás újraindításakor lesz átköltöztetve + No comment provided by engineer. + + + Debug delivery + Kézbesítési hibák felderítése No comment provided by engineer. @@ -1687,51 +2344,52 @@ Ez az egyszer használatos hivatkozása! Delete Törlés - chat item action + alert action +swipe action + + + Delete %lld messages of members? + Törli a tagok %lld üzenetét? + No comment provided by engineer. Delete %lld messages? Töröl %lld üzenetet? No comment provided by engineer. - - Delete Contact - Ismerős törlése - No comment provided by engineer. - Delete address - Azonosító törlése + Cím törlése No comment provided by engineer. Delete address? - Azonosító törlése? + Törli a címet? No comment provided by engineer. Delete after - Törlés miután + Törlés ennyi idő után No comment provided by engineer. Delete all files - Minden fájl törlése + Az összes fájl törlése No comment provided by engineer. Delete and notify contact - Törlés és ismerős értesítése + Törlés, és a partner értesítése No comment provided by engineer. - - Delete archive - Archívum törlése + + Delete chat + Csevegés törlése No comment provided by engineer. - - Delete chat archive? - Csevegési archívum törlése? + + Delete chat messages from your device. + Csevegési üzenetek törlése a saját eszközéről. No comment provided by engineer. @@ -1741,7 +2399,12 @@ Ez az egyszer használatos hivatkozása! Delete chat profile? - Csevegési profil törlése? + Törli a csevegési profilt? + No comment provided by engineer. + + + Delete chat? + Törli a csevegést? No comment provided by engineer. @@ -1751,14 +2414,12 @@ Ez az egyszer használatos hivatkozása! Delete contact - Ismerős törlése + Partner törlése No comment provided by engineer. - - Delete contact? -This cannot be undone! - Ismerős törlése? -Ezt nem vonható vissza! + + Delete contact? + Törli a partnert? No comment provided by engineer. @@ -1768,6 +2429,7 @@ Ezt nem vonható vissza! Delete database from this device + Adatbázis törlése erről az eszközről No comment provided by engineer. @@ -1777,22 +2439,22 @@ Ezt nem vonható vissza! Delete files and media? - Fájlok és a médiatartalmak törlése? + Törli a fájl- és a médiatartalmakat? No comment provided by engineer. Delete files for all chat profiles - Fájlok törlése minden csevegési profilból + Fájlok törlése az összes csevegési profilból No comment provided by engineer. Delete for everyone - Törlés mindenkinél + Törlés az összes tagnál chat feature Delete for me - Törlés nálam + Csak nálam No comment provided by engineer. @@ -1802,7 +2464,7 @@ Ezt nem vonható vissza! Delete group? - Csoport törlése? + Törli a csoportot? No comment provided by engineer. @@ -1812,32 +2474,37 @@ Ezt nem vonható vissza! Delete link - Hivatkozás törlése + Törlés No comment provided by engineer. Delete link? - Hivatkozás törlése? + Törli a hivatkozást? No comment provided by engineer. + + Delete list? + Törli a listát? + alert title + Delete member message? - Csoporttag üzenet törlése? + Törli a tag üzenetét? No comment provided by engineer. Delete message? - Üzenet törlése? + Törli az üzenetet? No comment provided by engineer. Delete messages Üzenetek törlése - No comment provided by engineer. + alert button Delete messages after - Üzenetek törlése miután + Üzenetek törlése ennyi idő után No comment provided by engineer. @@ -1847,17 +2514,17 @@ Ezt nem vonható vissza! Delete old database? - Régi adatbázis törlése? + Törli a régi adatbázist? No comment provided by engineer. - - Delete pending connection - Függőben lévő kapcsolat törlése + + Delete or moderate up to 200 messages. + Legfeljebb 200 üzenet egyszerre való törlése, vagy moderálása. No comment provided by engineer. Delete pending connection? - Függő kapcsolatfelvételi kérések törlése? + Törli a függőben lévő meghívót? No comment provided by engineer. @@ -1867,24 +2534,54 @@ Ezt nem vonható vissza! Delete queue - Várólista törlése + Sorba állítás törlése server test step + + Delete report + Jelentés törlése + No comment provided by engineer. + + + Delete up to 20 messages at once. + Legfeljebb 20 üzenet egyszerre való törlése. + No comment provided by engineer. + Delete user profile? - Felhasználói profil törlése? + Törli a felhasználói profilt? + No comment provided by engineer. + + + Delete without notification + Törlés értesítés nélkül + No comment provided by engineer. + + + Deleted + Törölve No comment provided by engineer. Deleted at - Törölve ekkor + Törölve No comment provided by engineer. Deleted at: %@ - Törölve ekkor: %@ + Törölve: %@ copied message info + + Deletion errors + Törlési hibák + No comment provided by engineer. + + + Delivered even when Apple drops them. + Kézbesítés akkor is, amikor az Apple eldobja őket. + No comment provided by engineer. + Delivery Kézbesítés @@ -1892,12 +2589,12 @@ Ezt nem vonható vissza! Delivery receipts are disabled! - Kézbesítési igazolások kikapcsolva! + A kézbesítési jelentések le vannak tiltva! No comment provided by engineer. Delivery receipts! - Kézbesítési igazolások! + Kézbesítési jelentések! No comment provided by engineer. @@ -1907,12 +2604,12 @@ Ezt nem vonható vissza! Desktop address - Számítógép azonosítója + Számítógép címe No comment provided by engineer. Desktop app version %@ is not compatible with this app. - Az asztali kliens verziója %@ nem kompatibilis ezzel az alkalmazással. + A számítógép-alkalmazás verziója (%@) nem kompatibilis ezzel az alkalmazással. No comment provided by engineer. @@ -1920,11 +2617,41 @@ Ezt nem vonható vissza! Számítógépek No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + A(z) %@ célkiszolgáló címe nem kompatibilis a(z) %@ továbbítókiszolgáló beállításaival. + No comment provided by engineer. + + + Destination server error: %@ + Célkiszolgáló-hiba: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + A(z) %@ célkiszolgáló verziója nem kompatibilis a(z) %@ továbbítókiszolgálóval. + No comment provided by engineer. + + + Detailed statistics + Részletes statisztikák + No comment provided by engineer. + + + Details + További részletek + No comment provided by engineer. + Develop Fejlesztés No comment provided by engineer. + + Developer options + Fejlesztői beállítások + No comment provided by engineer. + Developer tools Fejlesztői eszközök @@ -1937,17 +2664,17 @@ Ezt nem vonható vissza! Device authentication is disabled. Turning off SimpleX Lock. - Eszközhitelesítés kikapcsolva. SimpleX zárolás kikapcsolása. + Az eszközön nincs beállítva a képernyőzár. A SimpleX-zár ki van kapcsolva. No comment provided by engineer. Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication. - Eszközhitelesítés nem engedélyezett.A SimpleX zárolás bekapcsolható a Beállításokon keresztül, miután az eszköz hitelesítés engedélyezésre került. + Az eszközön nincs beállítva a képernyőzár. A SimpleX-zár az „Adatvédelem és biztonság” menüben kapcsolható be, miután beállította a képernyőzárat az eszközén. No comment provided by engineer. Different names, avatars and transport isolation. - Különböző nevek, avatarok és átviteli izoláció. + Különböző nevek, profilképek és átvitel-izoláció. No comment provided by engineer. @@ -1955,9 +2682,14 @@ Ezt nem vonható vissza! Közvetlen üzenetek chat feature - - Direct messages between members are prohibited in this group. - Ebben a csoportban tiltott a tagok közötti közvetlen üzenetek küldése. + + Direct messages between members are prohibited in this chat. + A tagok közötti közvetlen üzenetek le vannak tiltva ebben a csevegésben. + No comment provided by engineer. + + + Direct messages between members are prohibited. + A tagok közötti közvetlen üzenetek le vannak tiltva. No comment provided by engineer. @@ -1967,12 +2699,27 @@ Ezt nem vonható vissza! Disable SimpleX Lock - SimpleX zárolás kikapcsolása + SimpleX-zár kikapcsolása authentication reason + + Disable automatic message deletion? + Letiltja az automatikus üzenettörlést? + alert title + + + Disable delete messages + Üzenetek törlésének letiltása + alert button + Disable for all - Letiltás mindenki számára + Letiltás + No comment provided by engineer. + + + Disabled + Letiltva No comment provided by engineer. @@ -1987,22 +2734,22 @@ Ezt nem vonható vissza! Disappearing messages are prohibited in this chat. - Az eltűnő üzenetek le vannak tiltva ebben a csevegésben. + Az eltűnő üzenetek küldése le van tiltva ebben a csevegésben. No comment provided by engineer. - - Disappearing messages are prohibited in this group. - Az eltűnő üzenetek küldése le van tiltva ebben a csoportban. + + Disappearing messages are prohibited. + Az eltűnő üzenetek küldése le van tiltva. No comment provided by engineer. Disappears at - Eltűnik ekkor + Eltűnik No comment provided by engineer. Disappears at: %@ - Eltűnik ekkor: %@ + Eltűnik: %@ copied message info @@ -2012,12 +2759,12 @@ Ezt nem vonható vissza! Disconnect desktop? - Számítógép leválasztása? + Leválasztja a számítógépet? No comment provided by engineer. Discover and join groups - Helyi csoportok felfedezése és csatlakozás + Csoportok felfedezése és csatlakozás No comment provided by engineer. @@ -2025,24 +2772,44 @@ Ezt nem vonható vissza! Felfedezés helyi hálózaton keresztül No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + NE küldjön üzeneteket közvetlenül, még akkor sem, ha a saját kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. - NE használja a SimpleX-et segélyhívásokhoz. + NE használja a SimpleXet segélyhívásokhoz. + No comment provided by engineer. + + + Do NOT use private routing. + NE használjon privát útválasztást. No comment provided by engineer. Do it later - Későbbre halaszt + Befejezés később No comment provided by engineer. Do not send history to new members. - Ne küldjön előzményeket új tagok részére. + Az előzmények ne legyenek elküldve az új tagok számára. + No comment provided by engineer. + + + Do not use credentials with proxy. + Ne használja a hitelesítőadatokat proxyval. + No comment provided by engineer. + + + Documents: + Dokumentumok: No comment provided by engineer. Don't create address - Ne hozzon létre azonosítót + Ne hozzon létre címet No comment provided by engineer. @@ -2050,18 +2817,40 @@ Ezt nem vonható vissza! Ne engedélyezze No comment provided by engineer. + + Don't miss important messages. + Ne maradjon le a fontos üzenetekről. + No comment provided by engineer. + Don't show again Ne mutasd újra No comment provided by engineer. + + Done + Kész + No comment provided by engineer. + Downgrade and open chat - Visszatérés a korábbi verzióra és a csevegés megnyitása + Visszafejlesztés és a csevegés megnyitása + No comment provided by engineer. + + + Download + Letöltés + alert button +chat item action + + + Download errors + Letöltési hibák No comment provided by engineer. Download failed + Sikertelen letöltés No comment provided by engineer. @@ -2069,17 +2858,34 @@ Ezt nem vonható vissza! Fájl letöltése server test step + + Download files + Fájlok letöltése + alert action + + + Downloaded + Letöltve + No comment provided by engineer. + + + Downloaded files + Letöltött fájlok + No comment provided by engineer. + Downloading archive + Archívum letöltése No comment provided by engineer. Downloading link details + Letöltési hivatkozás részletei No comment provided by engineer. Duplicate display name! - Duplikált megjelenítési név! + Duplikált megjelenítendő név! No comment provided by engineer. @@ -2087,6 +2893,11 @@ Ezt nem vonható vissza! Időtartam No comment provided by engineer. + + E2E encrypted notifications. + Végpontok közötti titkosított értesítések. + No comment provided by engineer. + Edit Szerkesztés @@ -2094,7 +2905,7 @@ Ezt nem vonható vissza! Edit group profile - A csoport profiljának szerkesztése + Csoportprofil szerkesztése No comment provided by engineer. @@ -2107,20 +2918,25 @@ Ezt nem vonható vissza! Engedélyezés (felülírások megtartásával) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + A Flux kiszolgálókat engedélyezheti a beállításokban, a „Hálózat és kiszolgálók” menüben, a metaadatok jobb védelme érdekében. + No comment provided by engineer. + Enable SimpleX Lock - SimpleX zárolás engedélyezése + SimpleX-zár bekapcsolása authentication reason Enable TCP keep-alive - TCP életben tartásának engedélyezése + TCP életben tartása No comment provided by engineer. Enable automatic message deletion? - Automatikus üzenet törlés engedélyezése? - No comment provided by engineer. + Engedélyezi az automatikus üzenettörlést? + alert title Enable camera access @@ -2129,16 +2945,17 @@ Ezt nem vonható vissza! Enable for all - Engedélyezés mindenki részére + Engedélyezés az összes tag számára No comment provided by engineer. Enable in direct chats (BETA)! + Engedélyezés a közvetlen csevegésekben (BÉTA)! No comment provided by engineer. Enable instant notifications? - Azonnali értesítések engedélyezése? + Engedélyezi az azonnali értesítéseket? No comment provided by engineer. @@ -2153,7 +2970,7 @@ Ezt nem vonható vissza! Enable periodic notifications? - Időszakos értesítések engedélyezése? + Engedélyezi az időszakos értesítéseket? No comment provided by engineer. @@ -2163,9 +2980,19 @@ Ezt nem vonható vissza! Enable self-destruct passcode - Önmegsemmisítő jelkód engedélyezése + Önmegsemmisítő-jelkód engedélyezése set passcode view + + Enabled + Engedélyezve + No comment provided by engineer. + + + Enabled for + Számukra engedélyezve + No comment provided by engineer. + Encrypt Titkosít @@ -2173,7 +3000,7 @@ Ezt nem vonható vissza! Encrypt database? - Adatbázis titkosítása? + Titkosítja az adatbázist? No comment provided by engineer. @@ -2183,7 +3010,7 @@ Ezt nem vonható vissza! Encrypt stored files & media - Tárolt fájlok és médiatartalmak titkosítása + A tárolt fájlok- és a médiatartalmak titkosítása No comment provided by engineer. @@ -2198,22 +3025,22 @@ Ezt nem vonható vissza! Encrypted message: app is stopped - Titkosított üzenet: az alkalmazás leállt + Titkosított üzenet: az alkalmazás megállt notification Encrypted message: database error - Titkosított üzenet: adatbázis hiba + Titkosított üzenet: adatbázishiba notification Encrypted message: database migration error - Titkosított üzenet: adatbázis-migrációs hiba + Titkosított üzenet: adatbázis-átköltöztetési hiba notification Encrypted message: keychain error - Titkosított üzenet: kulcstároló hiba + Titkosított üzenet: kulcstartó hiba notification @@ -2228,66 +3055,72 @@ Ezt nem vonható vissza! Encryption re-negotiation error - Titkosítás újraegyeztetési hiba + Hiba történt a titkosítás újraegyeztetésekor message decrypt error item Encryption re-negotiation failed. - Titkosítás újraegyeztetése sikertelen. + Nem sikerült a titkosítást újraegyeztetni. + No comment provided by engineer. + + + Encryption renegotiation in progress. + A titkosítás újraegyeztetése folyamatban van. No comment provided by engineer. Enter Passcode - Jelkód megadása + Adja meg a jelkódot No comment provided by engineer. Enter correct passphrase. - Helyes jelmondat bevitele. + Adja meg a helyes jelmondatot. No comment provided by engineer. Enter group name… - Csoportnév megadása… + Adja meg a csoport nevét… No comment provided by engineer. Enter passphrase + Adja meg a jelmondatot No comment provided by engineer. Enter passphrase… - Jelmondat megadása… + Adja meg a jelmondatot… No comment provided by engineer. Enter password above to show! - Jelszó megadása a megjelenítéshez! + Adja meg a jelszót fentebb a megjelenítéshez! No comment provided by engineer. Enter server manually - Kiszolgáló megadása kézzel + Adja meg a kiszolgálót kézzel No comment provided by engineer. Enter this device name… - Eszköznév megadása… + Adja meg ennek az eszköznek a nevét… No comment provided by engineer. Enter welcome message… - Üdvözlő üzenetet megadása… + Adja meg az üdvözlőüzenetet… placeholder Enter welcome message… (optional) - Üdvözlő üzenetet megadása… (opcionális) + Adja meg az üdvözlőüzenetet… (nem kötelező) placeholder Enter your name… - Adja meg nevét… + Adjon meg egy nevet… No comment provided by engineer. @@ -2297,214 +3130,282 @@ Ezt nem vonható vissza! Error aborting address change - Hiba az azonosító megváltoztatásának megszakításakor + Hiba történt a cím módosításának megszakításakor No comment provided by engineer. + + Error accepting conditions + Hiba történt a feltételek elfogadásakor + alert title + Error accepting contact request - Hiba történt a kapcsolatfelvételi kérelem elfogadásakor - No comment provided by engineer. - - - Error accessing database file - Hiba az adatbázisfájl elérésekor + Hiba történt a meghívási kérés elfogadásakor No comment provided by engineer. Error adding member(s) - Hiba a tag(-ok) hozzáadásakor + Hiba történt a tag(ok) hozzáadásakor No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + Hiba történt a kiszolgáló hozzáadásakor + alert title Error changing address - Hiba az azonosító megváltoztatásakor + Hiba történt a cím módosításakor + No comment provided by engineer. + + + Error changing connection profile + Hiba történt a kapcsolati profilra való váltáskor No comment provided by engineer. Error changing role - Hiba a szerepkör megváltoztatásakor + Hiba történt a szerepkör módosításakor No comment provided by engineer. Error changing setting - Hiba a beállítás megváltoztatásakor + Hiba történt a beállítás módosításakor + No comment provided by engineer. + + + Error changing to incognito! + Hiba történt az inkognitóprofilra való váltáskor! + No comment provided by engineer. + + + Error checking token status + Hiba történt a token állapotának ellenőrzésekor + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + Hiba történt a(z) %@ továbbítókiszolgálóhoz való kapcsolódáskor. Próbálja meg később. No comment provided by engineer. Error creating address - Hiba az azonosító létrehozásakor + Hiba történt a cím létrehozásakor No comment provided by engineer. Error creating group - Hiba a csoport létrehozásakor + Hiba történt a csoport létrehozásakor No comment provided by engineer. Error creating group link - Hiba a csoport hivatkozásának létrehozásakor + Hiba történt a csoporthivatkozás létrehozásakor No comment provided by engineer. + + Error creating list + Hiba történt a lista létrehozásakor + alert title + Error creating member contact - Hiba az ismerőssel történő kapcsolat létrehozásában + Hiba történt a partnerrel történő kapcsolat létrehozásában No comment provided by engineer. Error creating message - Hiba az üzenet létrehozásakor + Hiba történt az üzenet létrehozásakor No comment provided by engineer. Error creating profile! - Hiba a profil létrehozásakor! + Hiba történt a profil létrehozásakor! + No comment provided by engineer. + + + Error creating report + Hiba történt a jelentés létrehozásakor No comment provided by engineer. Error decrypting file - Hiba a fájl visszafejtésekor + Hiba történt a fájl visszafejtésekor No comment provided by engineer. Error deleting chat database - Hiba a csevegési adatbázis törlésekor + Hiba történt a csevegési adatbázis törlésekor No comment provided by engineer. Error deleting chat! - Hiba a csevegés törlésekor! + Hiba történt a csevegés törlésekor! No comment provided by engineer. Error deleting connection - Hiba a kapcsolat törlésekor - No comment provided by engineer. - - - Error deleting contact - Hiba az ismerős törlésekor + Hiba történt a kapcsolat törlésekor No comment provided by engineer. Error deleting database - Hiba az adatbázis törlésekor + Hiba történt az adatbázis törlésekor No comment provided by engineer. Error deleting old database - Hiba a régi adatbázis törlésekor + Hiba történt a régi adatbázis törlésekor No comment provided by engineer. Error deleting token - Hiba a token törlésekor + Hiba történt a token törlésekor No comment provided by engineer. Error deleting user profile - Hiba a felhasználói profil törlésekor + Hiba történt a felhasználó-profil törlésekor No comment provided by engineer. Error downloading the archive + Hiba történt az archívum letöltésekor No comment provided by engineer. Error enabling delivery receipts! - Hiba a kézbesítési jelentések engedélyezésekor! + Hiba történt a kézbesítési jelentések engedélyezésekor! No comment provided by engineer. Error enabling notifications - Hiba az értesítések engedélyezésekor + Hiba történt az értesítések engedélyezésekor No comment provided by engineer. Error encrypting database - Hiba az adatbázis titkosításakor + Hiba történt az adatbázis titkosításakor No comment provided by engineer. Error exporting chat database - Hiba a csevegési adatbázis exportálásakor + Hiba történt a csevegési adatbázis exportálásakor + No comment provided by engineer. + + + Error exporting theme: %@ + Hiba történt a téma exportálásakor: %@ No comment provided by engineer. Error importing chat database - Hiba a csevegési adatbázis importálásakor + Hiba történt a csevegési adatbázis importálásakor No comment provided by engineer. Error joining group - Hiba a csoporthoz való csatlakozáskor + Hiba történt a csoporthoz való csatlakozáskor No comment provided by engineer. - - Error loading %@ servers - Hiba a %@ kiszolgálók betöltésekor + + Error loading servers + Hiba történt a kiszolgálók betöltésekor + alert title + + + Error migrating settings + Hiba történt a beállítások átköltöztetésekor No comment provided by engineer. Error opening chat - Hiba a csevegés megnyitásakor + Hiba történt a csevegés megnyitásakor No comment provided by engineer. Error receiving file - Hiba a fájl fogadásakor + Hiba történt a fájl fogadásakor + alert title + + + Error reconnecting server + Hiba történt a kiszolgálóhoz való újrakapcsolódáskor No comment provided by engineer. + + Error reconnecting servers + Hiba történt a kiszolgálókhoz való újrakapcsolódáskor + No comment provided by engineer. + + + Error registering for notifications + Hiba történt az értesítések regisztrálásakor + alert title + Error removing member - Hiba a tag eltávolításakor + Hiba történt a tag eltávolításakor No comment provided by engineer. - - Error saving %@ servers - Hiba történt a %@ kiszolgálók mentése közben + + Error reordering lists + Hiba történt a listák újrarendezésekor + alert title + + + Error resetting statistics + Hiba történt a statisztikák visszaállításakor No comment provided by engineer. Error saving ICE servers - Hiba az ICE kiszolgálók mentésekor + Hiba történt az ICE-kiszolgálók mentésekor No comment provided by engineer. + + Error saving chat list + Hiba történt a csevegési lista mentésekor + alert title + Error saving group profile - Hiba a csoport profil mentésekor + Hiba történt a csoportprofil mentésekor No comment provided by engineer. Error saving passcode - Hiba a jelkód mentése közben + Hiba történt a jelkód mentésekor No comment provided by engineer. Error saving passphrase to keychain - Hiba a jelmondat kulcstárolóba történő mentésekor + Hiba történt a jelmondat kulcstartóba történő mentésekor No comment provided by engineer. + + Error saving servers + Hiba történt a kiszolgálók mentésekor + alert title + Error saving settings + Hiba történt a beállítások mentésekor when migrating Error saving user password - Hiba a felhasználó jelszavának mentésekor + Hiba történt a felhasználó jelszavának mentésekor No comment provided by engineer. Error scanning code: %@ - Hiba a kód beolvasása közben: %@ + Hiba történt a kód beolvasásakor: %@ No comment provided by engineer. Error sending email - Hiba az e-mail küldésekor + Hiba történt az e-mail elküldésekor No comment provided by engineer. @@ -2514,44 +3415,59 @@ Ezt nem vonható vissza! Error sending message - Hiba az üzenet küldésekor + Hiba történt az üzenet elküldésekor No comment provided by engineer. Error setting delivery receipts! - Hiba történt a kézbesítési igazolások beállításakor! + Hiba történt a kézbesítési jelentések beállításakor! No comment provided by engineer. Error starting chat - Hiba a csevegés elindításakor + Hiba történt a csevegés elindításakor No comment provided by engineer. Error stopping chat - Hiba a csevegés megállításakor + Hiba történt a csevegés megállításakor + No comment provided by engineer. + + + Error switching profile + Hiba történt a profilváltáskor No comment provided by engineer. Error switching profile! - Hiba a profil váltásakor! - No comment provided by engineer. + Hiba történt a profilváltáskor! + alertTitle Error synchronizing connection - Hiba a kapcsolat szinkronizálása során + Hiba történt a kapcsolat szinkronizálásakor + No comment provided by engineer. + + + Error testing server connection + Hiba történt a kiszolgáló kapcsolatának tesztelésekor No comment provided by engineer. Error updating group link - Hiba a csoport hivatkozás frissítésekor + Hiba történt a csoporthivatkozás frissítésekor No comment provided by engineer. Error updating message - Hiba az üzenet frissítésekor + Hiba történt az üzenet frissítésekor No comment provided by engineer. + + Error updating server + Hiba történt a kiszolgáló frissítésekor + alert title + Error updating settings Hiba történt a beállítások frissítésekor @@ -2559,15 +3475,17 @@ Ezt nem vonható vissza! Error updating user privacy - Hiba a felhasználói beállítások frissítésekor + Hiba történt a felhasználói adatvédelem frissítésekor No comment provided by engineer. Error uploading the archive + Hiba történt az archívum feltöltésekor No comment provided by engineer. Error verifying passphrase: + Hiba történt a jelmondat hitelesítésekor: No comment provided by engineer. @@ -2578,18 +3496,30 @@ Ezt nem vonható vissza! Error: %@ Hiba: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid - Hiba: az URL érvénytelen + Hiba: a webcím érvénytelen No comment provided by engineer. Error: no database file - Hiba: nincs adatbázis fájl + Hiba: nincs adatbázisfájl No comment provided by engineer. + + Errors + Hibák + No comment provided by engineer. + + + Errors in servers configuration. + Hibák a kiszolgálók konfigurációjában. + servers error + Even when disabled in the conversation. Akkor is, ha le van tiltva a beszélgetésben. @@ -2605,6 +3535,11 @@ Ezt nem vonható vissza! Kibontás chat item action + + Expired + Lejárt + token status text + Export database Adatbázis exportálása @@ -2615,6 +3550,11 @@ Ezt nem vonható vissza! Exportálási hiba: No comment provided by engineer. + + Export theme + Téma exportálása + No comment provided by engineer. + Exported database archive. Exportált adatbázis-archívum. @@ -2622,11 +3562,12 @@ Ezt nem vonható vissza! Exported file doesn't exist + Az exportált fájl nem létezik No comment provided by engineer. Exporting database archive… - Adatbázis archívum exportálása… + Adatbázis-archívum exportálása… No comment provided by engineer. @@ -2639,29 +3580,83 @@ Ezt nem vonható vissza! Gyors és nem kell várni, amíg a feladó online lesz! No comment provided by engineer. + + Faster deletion of groups. + Gyorsabb csoporttörlés. + No comment provided by engineer. + Faster joining and more reliable messages. - Gyorsabb csatlakozás és megbízhatóbb üzenet kézbesítés. + Gyorsabb csatlakozás és megbízhatóbb üzenetkézbesítés. + No comment provided by engineer. + + + Faster sending messages. + Gyorsabb üzenetküldés. No comment provided by engineer. Favorite Kedvenc + swipe action + + + Favorites + Kedvencek No comment provided by engineer. + + File error + Fájlhiba + file error alert title + + + File errors: +%@ + Fájlhiba: +%@ + alert message + + + File is blocked by server operator: +%@. + A kiszolgáló üzemeltetője letiltotta a fájlt: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + A fájl nem található – valószínűleg a fájlt törölték vagy visszavonták. + file error text + + + File server error: %@ + Fájlkiszolgáló-hiba: %@ + file error text + + + File status + Fájl állapota + No comment provided by engineer. + + + File status: %@ + Fájl állapota: %@ + copied message info + File will be deleted from servers. - A fájl törölve lesz a kiszolgálóról. + A fájl törölve lesz a kiszolgálókról. No comment provided by engineer. File will be received when your contact completes uploading it. - A fájl akkor érkezik meg, amikor ismerőse befejezte annak feltöltést. + A fájl akkor érkezik meg, amikor a küldője befejezte annak feltöltését. No comment provided by engineer. File will be received when your contact is online, please wait or check later! - A fájl akkor érkezik meg, amint ismerőse online lesz, várjon, vagy ellenőrizze később! + A fájl akkor érkezik meg, amikor a küldője elérhető lesz, várjon, vagy ellenőrizze később! No comment provided by engineer. @@ -2669,24 +3664,34 @@ Ezt nem vonható vissza! Fájl: %@ No comment provided by engineer. + + Files + Fájlok + No comment provided by engineer. + Files & media - Fájlok és média + Fájlok és médiatartalmak No comment provided by engineer. Files and media - Fájlok és médiatartalom + Fájlok és médiatartalmak chat feature - - Files and media are prohibited in this group. - A fájlok- és a médiatartalom küldése le van tiltva ebben a csoportban. + + Files and media are prohibited. + A fájlok- és a médiatartalmak küldése le van tiltva. + No comment provided by engineer. + + + Files and media not allowed + A fájlok- és médiatartalmak nincsenek engedélyezve No comment provided by engineer. Files and media prohibited! - A fájlok- és a médiatartalom küldése le van tiltva! + A fájlok- és a médiatartalmak küldése le van tiltva! No comment provided by engineer. @@ -2696,10 +3701,12 @@ Ezt nem vonható vissza! Finalize migration + Átköltöztetés véglegesítése No comment provided by engineer. Finalize migration on another device. + Átköltöztetés véglegesítése egy másik eszközön. No comment provided by engineer. @@ -2734,7 +3741,7 @@ Ezt nem vonható vissza! Fix not supported by contact - Ismerős általi javítás nem támogatott + Partner általi javítás nem támogatott No comment provided by engineer. @@ -2742,11 +3749,115 @@ Ezt nem vonható vissza! Csoporttag általi javítás nem támogatott No comment provided by engineer. + + For all moderators + Az összes moderátor számára + No comment provided by engineer. + + + For chat profile %@: + A(z) %@ nevű csevegési profilhoz: + servers error + For console Konzolhoz No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + Például, ha a partnere egy SimpleX Chat-kiszolgálón keresztül fogadja az üzeneteket, akkor az Ön alkalmazása egy Flux-kiszolgálón keresztül fogja azokat kézbesíteni. + No comment provided by engineer. + + + For me + Csak magamnak + No comment provided by engineer. + + + For private routing + A privát útválasztáshoz + No comment provided by engineer. + + + For social media + A közösségi médiához + No comment provided by engineer. + + + Forward + Továbbítás + chat item action + + + Forward %d message(s)? + Továbbít %d üzenetet? + alert title + + + Forward and save messages + Üzenetek továbbítása és mentése + No comment provided by engineer. + + + Forward messages + Üzenetek továbbítása + alert action + + + Forward messages without files? + Továbbítja az üzeneteket fájlok nélkül? + alert message + + + Forward up to 20 messages at once. + Legfeljebb 20 üzenet egyszerre való továbbítása. + No comment provided by engineer. + + + Forwarded + Továbbított + No comment provided by engineer. + + + Forwarded from + Továbbítva innen + No comment provided by engineer. + + + Forwarding %lld messages + %lld üzenet továbbítása + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + A(z) %@ továbbítókiszolgáló nem tudott kapcsolódni a(z) %@ célkiszolgálóhoz. Próbálja meg később. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + A továbbítókiszolgáló címe nem kompatibilis a hálózati beállításokkal: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + A továbbítókiszolgáló verziója nem kompatibilis a hálózati beállításokkal: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + Továbbítókiszolgáló: %1$@ +Célkiszolgáló-hiba: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + Továbbítókiszolgáló: %1$@ +Hiba: %2$@ + snd error text + Found desktop Megtalált számítógép @@ -2764,27 +3875,22 @@ Ezt nem vonható vissza! Full name (optional) - Teljes név (opcionális) - No comment provided by engineer. - - - Full name: - Teljes név: + Teljes név (nem kötelező) No comment provided by engineer. Fully decentralized – visible only to members. - Teljesen decentralizált - kizárólag tagok számára látható. + Teljesen decentralizált – csak a tagok számára látható. No comment provided by engineer. Fully re-implemented - work in background! - Teljesen újra implementálva - háttérben történő működés! + Teljesen újra implementálva – háttérben történő működés! No comment provided by engineer. Further reduced battery usage - Tovább csökkentett akkumulátor használat + Tovább csökkentett akkumulátor-használat No comment provided by engineer. @@ -2792,6 +3898,21 @@ Ezt nem vonható vissza! GIF-ek és matricák No comment provided by engineer. + + Get notified when mentioned. + Kapjon értesítést, ha megemlítik. + No comment provided by engineer. + + + Good afternoon! + Jó napot! + message preview + + + Good morning! + Jó reggelt! + message preview + Group Csoport @@ -2814,67 +3935,37 @@ Ezt nem vonható vissza! Group full name (optional) - Csoport teljes neve (opcionális) + A csoport teljes neve (nem kötelező) No comment provided by engineer. Group image - Csoportkép + Csoport profilképe No comment provided by engineer. Group invitation - Csoportos meghívó + Csoportmeghívó No comment provided by engineer. Group invitation expired - A csoport meghívó lejárt + A csoportmeghívó lejárt No comment provided by engineer. Group invitation is no longer valid, it was removed by sender. - A csoport meghívó már nem érvényes, el lett távolítva a küldője által. + A csoportmeghívó már nem érvényes, a küldője eltávolította. No comment provided by engineer. Group link - Csoport hivatkozás + Csoporthivatkozás No comment provided by engineer. Group links - Csoport hivatkozások - No comment provided by engineer. - - - Group members can add message reactions. - Csoporttagok üzenetreakciókat adhatnak hozzá. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Csoporttagok visszafordíthatatlanul törölhetik az elküldött üzeneteket. (24 óra) - No comment provided by engineer. - - - Group members can send direct messages. - Csoporttagok küldhetnek közvetlen üzeneteket. - No comment provided by engineer. - - - Group members can send disappearing messages. - Csoporttagok küldhetnek eltűnő üzeneteket. - No comment provided by engineer. - - - Group members can send files and media. - Csoporttagok küldhetnek fájlokat és médiatartalmakat. - No comment provided by engineer. - - - Group members can send voice messages. - Csoporttagok küldhetnek hangüzeneteket. + Csoporthivatkozások No comment provided by engineer. @@ -2884,17 +3975,17 @@ Ezt nem vonható vissza! Group moderation - Csoport moderáció + Csoport moderálása No comment provided by engineer. Group preferences - Csoport beállítások + Csoportbeállítások No comment provided by engineer. Group profile - Csoport profil + Csoportprofil No comment provided by engineer. @@ -2904,27 +3995,37 @@ Ezt nem vonható vissza! Group welcome message - Csoport üdvözlő üzenete + A csoport üdvözlőüzenete No comment provided by engineer. Group will be deleted for all members - this cannot be undone! - Csoport törlésre kerül minden tag számára - ez nem vonható vissza! + A csoport törölve lesz az összes tag számára – ez a művelet nem vonható vissza! No comment provided by engineer. Group will be deleted for you - this cannot be undone! - A csoport törlésre kerül az ön részére - ez nem vonható vissza! + A csoport törölve lesz az Ön számára – ez a művelet nem vonható vissza! + No comment provided by engineer. + + + Groups + Csoportok No comment provided by engineer. Help - Segítség + Súgó + No comment provided by engineer. + + + Help admins moderating their groups. + Segítsen az adminisztrátoroknak a csoportjaik moderálásában. No comment provided by engineer. Hidden - Rejtett + Se név, se üzenet No comment provided by engineer. @@ -2934,12 +4035,12 @@ Ezt nem vonható vissza! Hidden profile password - Rejtett profil jelszó + Rejtett profiljelszó No comment provided by engineer. Hide - Elrejt + Összecsukás chat item action @@ -2954,7 +4055,7 @@ Ezt nem vonható vissza! Hide: - Elrejt: + Elrejtve: No comment provided by engineer. @@ -2964,7 +4065,7 @@ Ezt nem vonható vissza! History is not sent to new members. - Az előzmények nem kerülnek elküldésre új tagok részére. + Az előzmények nem lesznek elküldve az új tagok számára. No comment provided by engineer. @@ -2972,10 +4073,20 @@ Ezt nem vonható vissza! Hogyan működik a SimpleX No comment provided by engineer. + + How it affects privacy + Hogyan érinti az adatvédelmet + No comment provided by engineer. + + + How it helps privacy + Hogyan segíti az adatvédelmet + No comment provided by engineer. + How it works Hogyan működik - No comment provided by engineer. + alert button How to @@ -2984,16 +4095,17 @@ Ezt nem vonható vissza! How to use it - Hogyan használja + Használati útmutató No comment provided by engineer. How to use your servers - Kiszolgálók használata + Hogyan használja a saját kiszolgálóit No comment provided by engineer. Hungarian interface + Magyar kezelőfelület No comment provided by engineer. @@ -3001,39 +4113,44 @@ Ezt nem vonható vissza! ICE-kiszolgálók (soronként egy) No comment provided by engineer. + + IP address + IP-cím + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. - Ha nem tud személyesen találkozni, mutassa meg a QR-kódot egy videohívás során, vagy ossza meg a hivatkozást. + Ha nem tud személyesen találkozni, mutassa meg a QR-kódot egy videohívás közben, vagy ossza meg a hivatkozást. No comment provided by engineer. If you enter this passcode when opening the app, all app data will be irreversibly removed! - Ha az alkalmazás megnyitásakor megadja ezt a jelkódot, az összes alkalmazásadat visszafordíthatatlanul törlődik! + Ha az alkalmazás megnyitásakor megadja ezt a jelkódot, az összes alkalmazásadat véglegesen el lesz távolítva! No comment provided by engineer. If you enter your self-destruct passcode while opening the app: - Ha az alkalmazás megnyitásakor az önmegsemmisítő jelkódot megadásra kerül: + Ha az alkalmazás megnyitásakor megadja az önmegsemmisítő-jelkódot: No comment provided by engineer. If you need to use the chat now tap **Do it later** below (you will be offered to migrate the database when you restart the app). - Ha most kell használnia a csevegést, koppintson a ** Csináld később** elemre (az alkalmazás újraindításakor felajánlásra kerül az adatbázis áttelepítése). + Ha most kell használnia a csevegést, koppintson alább a **Befejezés később** lehetőségre (az alkalmazás újraindításakor fel lesz ajánlva az adatbázis átköltöztetése). No comment provided by engineer. Ignore - Figyelmen kívül hagyás + Mellőzés No comment provided by engineer. Image will be received when your contact completes uploading it. - A kép akkor érkezik meg, amikor ismerőse befejezte annak feltöltését. + A kép akkor érkezik meg, amikor a küldője befejezte annak feltöltését. No comment provided by engineer. Image will be received when your contact is online, please wait or check later! - A kép akkor érkezik meg, amikor ismerős elérhető lesz, várjon vagy ellenőrizze később! + A kép akkor érkezik meg, amikor a küldője elérhető lesz, várjon, vagy ellenőrizze később! No comment provided by engineer. @@ -3041,9 +4158,9 @@ Ezt nem vonható vissza! Azonnal No comment provided by engineer. - - Immune to spam and abuse - Spam és visszaélések elleni védelem + + Immune to spam + Védett a kéretlen tartalommal szemben No comment provided by engineer. @@ -3053,7 +4170,7 @@ Ezt nem vonható vissza! Import chat database? - Csevegési adatbázis importálása? + Importálja a csevegési adatbázist? No comment provided by engineer. @@ -3063,15 +4180,29 @@ Ezt nem vonható vissza! Import failed + Sikertelen importálás + No comment provided by engineer. + + + Import theme + Téma importálása No comment provided by engineer. Importing archive + Archívum importálása + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Továbbfejlesztett kézbesítés, csökkentett adatforgalom-használat. +További fejlesztések hamarosan! No comment provided by engineer. Improved message delivery - Továbbfejlesztett üzenetküldés + Továbbfejlesztett üzenetkézbesítés No comment provided by engineer. @@ -3086,13 +4217,29 @@ Ezt nem vonható vissza! In order to continue, chat should be stopped. + A folytatáshoz a csevegést meg kell szakítani. No comment provided by engineer. In reply to - Válasz neki + Válaszul erre No comment provided by engineer. + + In-call sounds + Bejövő hívás csengőhangja + No comment provided by engineer. + + + Inappropriate content + Kifogásolt tartalom + report reason + + + Inappropriate profile + Kifogásolt profil + report reason + Incognito Inkognitó @@ -3100,17 +4247,17 @@ Ezt nem vonható vissza! Incognito groups - Inkognitó csoportok + Inkognitócsoportok No comment provided by engineer. Incognito mode - Inkognitó mód + Inkognitómód No comment provided by engineer. Incognito mode protects your privacy by using a new random profile for each contact. - Az inkognitómód védi személyes adatait azáltal, hogy minden ismerőshöz új véletlenszerű profilt használ. + Az inkognitómód úgy védi a személyes adatait, hogy az összes partneréhez új, véletlenszerű profilt használ. No comment provided by engineer. @@ -3130,7 +4277,7 @@ Ezt nem vonható vissza! Incompatible database version - Nem kompatibilis adatbázis verzió + Nem kompatibilis adatbázis-verzió No comment provided by engineer. @@ -3163,23 +4310,53 @@ Ezt nem vonható vissza! A [SimpleX Chat terminálhoz] telepítése (https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Azonnali + No comment provided by engineer. + Instant push notifications will be hidden! - Az azonnali push értesítések elrejtésre kerülnek! + Az azonnali push-értesítések el lesznek rejtve! No comment provided by engineer. - - Instantly - Azonnal - No comment provided by engineer. - Interface - Felület + Kezelőfelület No comment provided by engineer. + + Interface colors + Kezelőfelület színei + No comment provided by engineer. + + + Invalid + Érvénytelen + token status text + + + Invalid (bad token) + Érvénytelen (hibás token) + token status text + + + Invalid (expired) + Érvénytelen (lejárt) + token status text + + + Invalid (unregistered) + Érvénytelen (nincs regisztrálva) + token status text + + + Invalid (wrong topic) + Érvénytelen (rossz topic) + token status text + Invalid QR code Érvénytelen QR-kód @@ -3187,12 +4364,12 @@ Ezt nem vonható vissza! Invalid connection link - Érvénytelen kapcsolati hivatkozás + Érvénytelen kapcsolattartási hivatkozás No comment provided by engineer. Invalid display name! - Érvénytelen megjelenítendő felhaszálónév! + Érvénytelen megjelenítendő név! No comment provided by engineer. @@ -3202,6 +4379,7 @@ Ezt nem vonható vissza! Invalid migration confirmation + Érvénytelen átköltöztetési visszaigazolás No comment provided by engineer. @@ -3217,7 +4395,7 @@ Ezt nem vonható vissza! Invalid server address! Érvénytelen kiszolgálócím! - No comment provided by engineer. + alert title Invalid status @@ -3239,6 +4417,11 @@ Ezt nem vonható vissza! Tagok meghívása No comment provided by engineer. + + Invite to chat + Meghívás a csevegésbe + No comment provided by engineer. + Invite to group Meghívás a csoportba @@ -3246,27 +4429,27 @@ Ezt nem vonható vissza! Irreversible message deletion - Visszafordíthatatlan üzenettörlés + Végleges üzenettörlés No comment provided by engineer. Irreversible message deletion is prohibited in this chat. - Ebben a csevegésben az üzenetek visszafordíthatatlan törlése le van tiltva. + Az üzenetek végleges törlése le van tiltva ebben a csevegésben. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. - Ebben a csoportban az üzenetek visszafordíthatatlan törlése le van tiltva. + + Irreversible message deletion is prohibited. + Az üzenetek végleges törlése le van tiltva. No comment provided by engineer. It allows having many anonymous connections without any shared data between them in a single chat profile. - Lehetővé teszi, hogy egyetlen csevegőprofilon belül több anonim kapcsolat legyen, anélkül, hogy megosztott adatok lennének közöttük. + Lehetővé teszi, hogy egyetlen csevegési profilon belül több névtelen kapcsolat legyen, anélkül, hogy megosztott adatok lennének közöttük. No comment provided by engineer. It can happen when you or your connection used the old database backup. - Ez akkor fordulhat elő, ha ön vagy a kapcsolata régi adatbázis biztonsági mentést használt. + Ez akkor fordulhat elő, ha Ön vagy a partnere régi adatbázis biztonsági mentést használt. No comment provided by engineer. @@ -3276,13 +4459,18 @@ Ezt nem vonható vissza! 3. The connection was compromised. Ez akkor fordulhat elő, ha: 1. Az üzenetek 2 nap után, vagy a kiszolgálón 30 nap után lejártak. -2. Az üzenet visszafejtése sikertelen volt, mert vagy az ismerőse régebbi adatbázis biztonsági mentést használt. +2. Nem sikerült az üzenetet visszafejteni, mert Ön, vagy a partnere régebbi adatbázis biztonsági mentést használt. 3. A kapcsolat sérült. No comment provided by engineer. + + It protects your IP address and connections. + Védi az IP-címét és a kapcsolatait. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). - Úgy tűnik, már csatlakozott ezen a hivatkozáson keresztül. Ha ez nem így van, akkor hiba történt (%@). + Úgy tűnik, már kapcsolódott ezen a hivatkozáson keresztül. Ha ez nem így van, akkor hiba történt (%@). No comment provided by engineer. @@ -3298,7 +4486,7 @@ Ezt nem vonható vissza! Join Csatlakozás - No comment provided by engineer. + swipe action Join group @@ -3329,7 +4517,7 @@ Ezt nem vonható vissza! Join your group? This is your link for group %@! Csatlakozik a csoportjához? -Ez az ön hivatkozása a(z) %@ csoporthoz! +Ez a saját hivatkozása a(z) %@ nevű csoporthoz! No comment provided by engineer. @@ -3339,7 +4527,12 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Keep - Megtart + Megtartás + alert action + + + Keep conversation + Beszélgetés megtartása No comment provided by engineer. @@ -3349,8 +4542,8 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Keep unused invitation? - Fel nem használt meghívó megtartása? - No comment provided by engineer. + Megtartja a fel nem használt meghívót? + alert title Keep your connections @@ -3359,12 +4552,12 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! KeyChain error - Kulcstároló hiba + Kulcstartóhiba No comment provided by engineer. Keychain error - Kulcstároló hiba + Kulcstartóhiba No comment provided by engineer. @@ -3384,7 +4577,17 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Leave - Elhagy + Elhagyás + swipe action + + + Leave chat + Csevegés elhagyása + No comment provided by engineer. + + + Leave chat? + Elhagyja a csevegést? No comment provided by engineer. @@ -3394,12 +4597,12 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Leave group? - Csoport elhagyása? + Elhagyja a csoportot? No comment provided by engineer. Let's talk in SimpleX Chat - Beszélgessünk a SimpleX Chat-ben + Beszélgessünk a SimpleX Chatben email subject @@ -3414,17 +4617,32 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Link mobile and desktop apps! 🔗 - Társítsa össze a mobil és az asztali alkalmazásokat! 🔗 + Társítsa össze a hordozható eszköz- és számítógépes alkalmazásokat! 🔗 No comment provided by engineer. Linked desktop options - Összekapcsolt számítógép beállítások + Társított számítógép beállítások No comment provided by engineer. Linked desktops - Összekapcsolt számítógépek + Társított számítógépek + No comment provided by engineer. + + + List + Lista + swipe action + + + List name and emoji should be different for all lists. + Az összes lista nevének és emodzsijának különbözőnek kell lennie. + No comment provided by engineer. + + + List name... + Lista neve… No comment provided by engineer. @@ -3437,11 +4655,6 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Élő üzenetek No comment provided by engineer. - - Local - Helyi - No comment provided by engineer. - Local name Helyi név @@ -3462,11 +4675,6 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Zárolási mód No comment provided by engineer. - - Make a private connection - Privát kapcsolat létrehozása - No comment provided by engineer. - Make one message disappear Egy üzenet eltüntetése @@ -3474,27 +4682,17 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Make profile private! - Tegye priváttá profilját! - No comment provided by engineer. - - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Győződjön meg arról, hogy a %@ szervercímek megfelelő formátumúak, sorszeparáltak és nem duplikáltak (%@). + Tegye priváttá a profilját! No comment provided by engineer. Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. - Győződjön meg arról, hogy a WebRTC ICE-kiszolgáló címei megfelelő formátumúak, sorszeparáltak és nem duplikáltak. - No comment provided by engineer. - - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Sokan kérdezték: *ha a SimpleX-nek nincsenek felhasználói azonosítói, akkor hogyan tud üzeneteket kézbesíteni?* + Győződjön meg arról, hogy a megadott WebRTC ICE-kiszolgálók címei megfelelő formátumúak, soronként elkülönítettek, és nincsenek duplikálva. No comment provided by engineer. Mark deleted for everyone - Jelölje meg mindenki számára töröltként + Jelölje meg az összes tag számára töröltként No comment provided by engineer. @@ -3504,7 +4702,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Mark verified - Ellenőrzöttként jelölve + Hitelesítés No comment provided by engineer. @@ -3517,24 +4715,104 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Max. 30 másodperc, azonnal érkezett. No comment provided by engineer. + + Media & file servers + Média- és fájlkiszolgálók + No comment provided by engineer. + + + Medium + Közepes + blur media + Member Tag No comment provided by engineer. + + Member inactive + Inaktív tag + item status text + + + Member reports + Tagok jelentései + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + A tag szerepköre a következőre fog módosulni: „%@”. A csevegés összes tagja értesítést fog kapni. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. - A tag szerepköre meg fog változni erre: "%@". A csoport minden tagja értesítést kap róla. + A tag szerepköre a következőre fog módosulni: „%@”. A csoport az összes tagja értesítést fog kapni. No comment provided by engineer. Member role will be changed to "%@". The member will receive a new invitation. - A tag szerepköre meg fog változni erre: "%@". A tag új meghívást fog kapni. + A tag szerepköre a következőre fog módosulni: „%@”. A tag új meghívást fog kapni. + No comment provided by engineer. + + + Member will be removed from chat - this cannot be undone! + A tag el lesz távolítva a csevegésből – ez a művelet nem vonható vissza! No comment provided by engineer. Member will be removed from group - this cannot be undone! - A tag eltávolítása a csoportból - ez nem vonható vissza! + A tag el lesz távolítva a csoportból – ez a művelet nem vonható vissza! + No comment provided by engineer. + + + Members can add message reactions. + A tagok reakciókat adhatnak hozzá az üzenetekhez. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + A tagok véglegesen törölhetik az elküldött üzeneteiket. (24 óra) + No comment provided by engineer. + + + Members can report messsages to moderators. + A tagok jelenthetik az üzeneteket a moderátorok felé. + No comment provided by engineer. + + + Members can send SimpleX links. + A tagok küldhetnek SimpleX-hivatkozásokat. + No comment provided by engineer. + + + Members can send direct messages. + A tagok küldhetnek egymásnak közvetlen üzeneteket. + No comment provided by engineer. + + + Members can send disappearing messages. + A tagok küldhetnek eltűnő üzeneteket. + No comment provided by engineer. + + + Members can send files and media. + A tagok küldhetnek fájlokat és médiatartalmakat. + No comment provided by engineer. + + + Members can send voice messages. + A tagok küldhetnek hangüzeneteket. + No comment provided by engineer. + + + Mention members 👋 + Tagok említése 👋 + No comment provided by engineer. + + + Menus + Menük No comment provided by engineer. @@ -3544,14 +4822,34 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Message delivery receipts! - Üzenetkézbesítési bizonylatok! + Üzenetkézbesítési jelentések! No comment provided by engineer. + + Message delivery warning + Üzenetkézbesítési figyelmeztetés + item status text + Message draft Üzenetvázlat No comment provided by engineer. + + Message forwarded + Továbbított üzenet + item status text + + + Message may be delivered later if member becomes active. + Az üzenet később is kézbesíthető, ha a tag aktívvá válik. + item status description + + + Message queue info + Üzenetsorbaállítási információ + No comment provided by engineer. + Message reactions Üzenetreakciók @@ -3559,21 +4857,52 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Message reactions are prohibited in this chat. - Az üzenetreakciók ebben a csevegésben le vannak tiltva. + A reakciók hozzáadása az üzenetekhez le van tiltva ebben a csevegésben. No comment provided by engineer. - - Message reactions are prohibited in this group. - Ebben a csoportban az üzenetreakciók le vannak tiltva. + + Message reactions are prohibited. + A reakciók hozzáadása az üzenetekhez le van tiltva. No comment provided by engineer. + + Message reception + Üzenetjelentés + No comment provided by engineer. + + + Message servers + Üzenetkiszolgálók + No comment provided by engineer. + + + Message shape + Üzenetbuborék alakja + No comment provided by engineer. + + + Message source remains private. + Az üzenet forrása titokban marad. + No comment provided by engineer. + + + Message status + Üzenet állapota + No comment provided by engineer. + + + Message status: %@ + Üzenet állapota: %@ + copied message info + Message text - Üzenet szövege + Név és üzenet No comment provided by engineer. Message too large + Az üzenet túl nagy No comment provided by engineer. @@ -3588,68 +4917,97 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Messages from %@ will be shown! - A(z) %@ által írt üzenetek megjelennek! + %@ összes üzenete meg fog jelenni! No comment provided by engineer. + + Messages in this chat will never be deleted. + Az ebben a csevegésben lévő üzenetek soha nem lesznek törölve. + alert message + + + Messages received + Fogadott üzenetek + No comment provided by engineer. + + + Messages sent + Elküldött üzenetek + No comment provided by engineer. + + + Messages were deleted after you selected them. + Az üzeneteket törölték miután kijelölte őket. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + Az üzenetek, a fájlok és a hívások **végpontok közötti titkosítással**, sérülés utáni titkosságvédelemmel és -helyreállítással, továbbá letagadhatósággal vannak védve. No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + Az üzenetek, a fájlok és a hívások **végpontok közötti kvantumbiztos titkosítással**, sérülés utáni titkosságvédelemmel és -helyreállítással, továbbá letagadhatósággal vannak védve. No comment provided by engineer. Migrate device + Eszköz átköltöztetése No comment provided by engineer. Migrate from another device + Átköltöztetés egy másik eszközről No comment provided by engineer. Migrate here + Átköltöztetés ide No comment provided by engineer. Migrate to another device + Átköltöztetés egy másik eszközre No comment provided by engineer. Migrate to another device via QR code. + Átköltöztetés egy másik eszközre QR-kód használatával. No comment provided by engineer. Migrating + Átköltöztetés No comment provided by engineer. Migrating database archive… - Adatbázis archívum migrálása… + Adatbázis-archívum átköltöztetése… No comment provided by engineer. Migration complete + Átköltöztetés befejezve No comment provided by engineer. Migration error: - Migrációs hiba: + Átköltöztetési hiba: No comment provided by engineer. Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat). - Sikertelen migráció. Koppintson a **Kihagyás** lehetőségre az aktuális adatbázis használatának folytatásához. Kérjük, jelentse a problémát az alkalmazás fejlesztőinek csevegésben vagy e-mailben [chat@simplex.chat](mailto:chat@simplex.chat). + Sikertelen átköltöztetés. Koppintson a **Kihagyás** lehetőségre a jelenlegi adatbázis használatának folytatásához. Jelentse a problémát az alkalmazás fejlesztőinek csevegésben vagy e-mailben [chat@simplex.chat](mailto:chat@simplex.chat). No comment provided by engineer. Migration is completed - A migráció befejeződött + Az átköltöztetés befejeződött No comment provided by engineer. - - Migrations: %@ - Migrációk: %@ + + Migrations: + Átköltöztetések: No comment provided by engineer. @@ -3659,38 +5017,53 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Moderated at - Moderálva ekkor + Moderálva No comment provided by engineer. Moderated at: %@ - Moderálva ekkor: %@ + Moderálva: %@ copied message info + + More + Továbbiak + swipe action + More improvements are coming soon! Hamarosan további fejlesztések érkeznek! No comment provided by engineer. + + More reliable network connection. + Megbízhatóbb hálózati kapcsolat. + No comment provided by engineer. + + + More reliable notifications + Megbízhatóbb értesítések + No comment provided by engineer. + Most likely this connection is deleted. - Valószínűleg ez a kapcsolat törlésre került. + Valószínűleg ez a kapcsolat törölve lett. item status description - - Most likely this contact has deleted the connection with you. - Valószínűleg ez az ismerős törölte önnel a kapcsolatot. - No comment provided by engineer. - Multiple chat profiles - Több csevegőprofil + Több csevegési profil No comment provided by engineer. Mute - Elnémítás - No comment provided by engineer. + Némítás + notification label action + + + Mute all + Összes némítása + notification label action Muted when inactive! @@ -3700,13 +5073,38 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Name Név - No comment provided by engineer. + swipe action Network & servers Hálózat és kiszolgálók No comment provided by engineer. + + Network connection + Hálózati kapcsolat + No comment provided by engineer. + + + Network decentralization + Hálózati decentralizáció + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + Hálózati problémák – az üzenet többszöri elküldési kísérlet után lejárt. + snd error text + + + Network management + Hálózatkezelés + No comment provided by engineer. + + + Network operator + Hálózatüzemeltető + No comment provided by engineer. + Network settings Hálózati beállítások @@ -3717,19 +5115,39 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Hálózat állapota No comment provided by engineer. + + New + Új + token status text + New Passcode Új jelkód No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Minden alkalommal, amikor elindítja az alkalmazást, új SOCKS-hitelesítő-adatokat fog használni. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Az összes kiszolgálóhoz új, SOCKS-hitelesítő-adatok legyenek használva. + No comment provided by engineer. + New chat - Új beszélgetés + Új csevegés + No comment provided by engineer. + + + New chat experience 🎉 + Új csevegési élmény 🎉 No comment provided by engineer. New contact request - Új kapcsolattartási kérelem + Új meghívási kérés notification @@ -3737,26 +5155,31 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Új kapcsolat: notification - - New database archive - Új adatbázis-archívum - No comment provided by engineer. - New desktop app! - Új asztali alkalmazás! + Új számítógép-alkalmazás! No comment provided by engineer. New display name - Új megjelenítési név + Új megjelenítendő név No comment provided by engineer. + + New events + Új események + notification + New in %@ Újdonságok a(z) %@ verzióban No comment provided by engineer. + + New media options + Új médiabeállítások + No comment provided by engineer. + New member role Új tag szerepköre @@ -3772,6 +5195,11 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Új jelmondat… No comment provided by engineer. + + New server + Új kiszolgáló + No comment provided by engineer. + No Nem @@ -3782,14 +5210,29 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Nincs alkalmazás jelszó Authentication unavailable + + No chats + Nincsenek csevegések + No comment provided by engineer. + + + No chats found + Nem találhatók csevegések + No comment provided by engineer. + + + No chats in list %@ + Nincsenek csevegések a(z) %@ nevű listában + No comment provided by engineer. + No contacts selected - Nem kerültek ismerősök kiválasztásra + Nincs partner kijelölve No comment provided by engineer. No contacts to add - Nincs hozzáadandó ismerős + Nincs hozzáadandó partner No comment provided by engineer. @@ -3799,9 +5242,14 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! No device token! - Nincs eszköztoken! + Nincs készüléktoken! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + Még nincs közvetlen kapcsolat, az üzenetet az adminisztrátor továbbítja. + item status description + No filtered chats Nincsenek szűrt csevegések @@ -3817,21 +5265,111 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Nincsenek előzmények No comment provided by engineer. + + No info, try to reload + Nincs információ, próbálja meg újratölteni + No comment provided by engineer. + + + No media & file servers. + Nincsenek média- és fájlkiszolgálók. + servers error + + + No message + Nincs üzenet + No comment provided by engineer. + + + No message servers. + Nincsenek üzenet-kiszolgálók. + servers error + + + No network connection + Nincs hálózati kapcsolat + No comment provided by engineer. + + + No permission to record speech + Nincs jogosultság megadva a beszéd rögzítéséhez + No comment provided by engineer. + + + No permission to record video + Nincs jogosultság megadva a videó rögzítéséhez + No comment provided by engineer. + No permission to record voice message Nincs engedély a hangüzenet rögzítésére No comment provided by engineer. + + No push server + Helyi + No comment provided by engineer. + No received or sent files Nincsenek fogadott vagy küldött fájlok No comment provided by engineer. + + No servers for private message routing. + Nincsenek kiszolgálók a privát üzenet-útválasztáshoz. + servers error + + + No servers to receive files. + Nincsenek fájlfogadási kiszolgálók. + servers error + + + No servers to receive messages. + Nincsenek üzenetfogadási kiszolgálók. + servers error + + + No servers to send files. + Nincsenek fájlküldő-kiszolgálók. + servers error + + + No token! + Nincs token! + alert title + + + No unread chats + Nincsenek olvasatlan csevegések + No comment provided by engineer. + + + No user identifiers. + Nincsenek felhasználó-azonosítók. + No comment provided by engineer. + Not compatible! Nem kompatibilis! No comment provided by engineer. + + Notes + Jegyzetek + No comment provided by engineer. + + + Nothing selected + Nincs semmi kijelölve + No comment provided by engineer. + + + Nothing to forward! + Nincs mit továbbítani! + alert title + Notifications Értesítések @@ -3842,13 +5380,28 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Az értesítések le vannak tiltva! No comment provided by engineer. + + Notifications error + Értesítési hiba + alert title + + + Notifications privacy + Értesítési adatvédelem + No comment provided by engineer. + + + Notifications status + Értesítések állapota + alert title + Now admins can: - delete members' messages. - disable members ("observer" role) - Most már az adminok is: + Most már az adminisztrátorok is: - törölhetik a tagok üzeneteit. -- letilthatnak tagokat ("megfigyelő" szerepkör) +- letilthatnak tagokat („megfigyelő” szerepkör) No comment provided by engineer. @@ -3858,124 +5411,148 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Off - Ki - No comment provided by engineer. + Kikapcsolva + blur media Ok Rendben - No comment provided by engineer. + alert button Old database Régi adatbázis No comment provided by engineer. - - Old database archive - Régi adatbázis archívum - No comment provided by engineer. - One-time invitation link - Egyszer használatos meghívó hivatkozás + Egyszer használható meghívó No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - A csatlakozáshoz Onion host-okra lesz szükség. VPN engedélyezése szükséges. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Onion-kiszolgálók **szükségesek** a kapcsolódáshoz. +Kompatibilis VPN szükséges. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Onion host-ok használata, ha azok rendelkezésre állnak. VPN engedélyezése szükséges. + + Onion hosts will be used when available. +Requires compatible VPN. + Onion-kiszolgálók használata, ha azok rendelkezésre állnak. +VPN engedélyezése szükséges. No comment provided by engineer. Onion hosts will not be used. - Onion host-ok nem lesznek használva. + Az onion-kiszolgálók nem lesznek használva. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. - Csak a klienseszközök tárolják a felhasználói profilokat, névjegyeket, csoportokat és a **2 rétegű végponttól-végpontig titkosítással** küldött üzeneteket. + + Only chat owners can change preferences. + Csak a csevegés tulajdonosai módosíthatják a csevegési beállításokat. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. + A felhasználói profilok, partnerek, csoportok és üzenetek csak az eszközön vannak tárolva a kliensen belül. + No comment provided by engineer. + + + Only delete conversation + Csak a beszélgetés törlése No comment provided by engineer. Only group owners can change group preferences. - Csak a csoporttulajdonosok módosíthatják a csoportbeállításokat. + Csak a csoport tulajdonosai módosíthatják a csoportbeállításokat. No comment provided by engineer. Only group owners can enable files and media. - Csak a csoporttulajdonosok engedélyezhetik a fájlok- és a médiatartalmak küldését. + Csak a csoport tulajdonosai engedélyezhetik a fájlok- és a médiatartalmak küldését. No comment provided by engineer. Only group owners can enable voice messages. - Csak a csoporttulajdonosok engedélyezhetik a hangüzenetek küldését. + Csak a csoport tulajdonosai engedélyezhetik a hangüzenetek küldését. + No comment provided by engineer. + + + Only sender and moderators see it + Csak a küldő és a moderátorok látják + No comment provided by engineer. + + + Only you and moderators see it + Csak Ön és a moderátorok látják No comment provided by engineer. Only you can add message reactions. - Csak ön adhat hozzá üzenetreakciókat. + Csak Ön adhat hozzá reakciókat az üzenetekhez. No comment provided by engineer. Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours) - Visszafordíthatatlanul csak ön törölhet üzeneteket (ismerőse csak törlésre jelölheti őket ). (24 óra) + Véglegesen csak Ön törölhet üzeneteket (partnere csak törlésre jelölheti meg őket ). (24 óra) No comment provided by engineer. Only you can make calls. - Csak ön tud hívásokat indítani. + Csak Ön tud hívásokat indítani. No comment provided by engineer. Only you can send disappearing messages. - Csak ön tud eltűnő üzeneteket küldeni. + Csak Ön tud eltűnő üzeneteket küldeni. No comment provided by engineer. Only you can send voice messages. - Csak ön tud hangüzeneteket küldeni. + Csak Ön tud hangüzeneteket küldeni. No comment provided by engineer. Only your contact can add message reactions. - Csak az ismerős tud üzeneteakciókat adni. + Csak a partnere adhat hozzá reakciókat az üzenetekhez. No comment provided by engineer. Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours) - Csak az ismerős tud visszafordíthatatlanul törölni üzeneteket (megjelölheti őket törlésre). (24 óra) + Csak a partnere tudja az üzeneteket véglegesen törölni (Ön csak törlésre jelölheti meg azokat). (24 óra) No comment provided by engineer. Only your contact can make calls. - Csak az ismerős tud hívást indítani. + Csak a partnere tud hívást indítani. No comment provided by engineer. Only your contact can send disappearing messages. - Csak az ismerős tud eltűnő üzeneteket küldeni. + Csak a partnere tud eltűnő üzeneteket küldeni. No comment provided by engineer. Only your contact can send voice messages. - Csak az ismerős tud hangüzeneteket küldeni. + Csak a partnere tud hangüzeneteket küldeni. No comment provided by engineer. Open Megnyitás - No comment provided by engineer. + alert action Open Settings Beállítások megnyitása No comment provided by engineer. + + Open changes + Módosítások megtekintése + No comment provided by engineer. + Open chat Csevegés megnyitása @@ -3983,35 +5560,51 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Open chat console - Csevegés konzol megnyitása + Csevegési konzol megnyitása authentication reason + + Open conditions + Feltételek megnyitása + No comment provided by engineer. + Open group Csoport megnyitása No comment provided by engineer. + + Open link? + alert title + Open migration to another device + Átköltöztetés indítása egy másik eszközre authentication reason - - Open user profiles - Felhasználói profilok megnyitása - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Nyílt forráskódú protokoll és forráskód – bárki üzemeltethet kiszolgálókat. - No comment provided by engineer. - Opening app… Az alkalmazás megnyitása… No comment provided by engineer. + + Operator + Üzemeltető + No comment provided by engineer. + + + Operator server + Kiszolgáló-üzemeltető + alert title + + + Or import archive file + Vagy archívumfájl importálása + No comment provided by engineer. + Or paste archive link + Vagy az archívum hivatkozásának beillesztése No comment provided by engineer. @@ -4021,6 +5614,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Or securely share this file link + Vagy ossza meg biztonságosan ezt a fájlhivatkozást No comment provided by engineer. @@ -4028,14 +5622,36 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Vagy mutassa meg ezt a kódot No comment provided by engineer. + + Or to share privately + Vagy a privát megosztáshoz + No comment provided by engineer. + + + Organize chats into lists + Csevegések listákba szervezése + No comment provided by engineer. + + + Other + További + No comment provided by engineer. + + + Other file errors: +%@ + Egyéb fájlhiba: +%@ + alert message + PING count - PING számláló + PING-ek száma No comment provided by engineer. PING interval - PING időköze + Időtartam a PING-ek között No comment provided by engineer. @@ -4045,7 +5661,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Passcode changed! - A jelkód megváltozott! + A jelkód módosult! No comment provided by engineer. @@ -4055,7 +5671,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Passcode not changed! - A jelkód nem változott! + A jelkód nem módosult! No comment provided by engineer. @@ -4063,19 +5679,24 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! A jelkód beállítva! No comment provided by engineer. + + Password + Jelszó + No comment provided by engineer. + Password to show - Jelszó mutatása + Jelszó a megjelenítéshez No comment provided by engineer. Past member %@ - Korábbi csoport tag %@ + (Már nem tag) %@ past/unknown group member Paste desktop address - Számítógép azonosítójának beillesztése + Számítógép címének beillesztése No comment provided by engineer. @@ -4085,22 +5706,22 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Paste link to connect! - Hivatkozás beillesztése a csatlakozáshoz! + Hivatkozás beillesztése a kapcsolódáshoz! No comment provided by engineer. Paste the link you received - Fogadott hivatkozás beillesztése + Kapott hivatkozás beillesztése No comment provided by engineer. - - People can connect to you only via the links you share. - Az emberek csak az ön által megosztott hivatkozáson keresztül kapcsolódhatnak. + + Pending + Függőben No comment provided by engineer. - - Periodically - Rendszeresen + + Periodic + Időszakos No comment provided by engineer. @@ -4110,30 +5731,49 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Picture-in-picture calls + Kép a képben hívások + No comment provided by engineer. + + + Play from the chat list. + Lejátszás a csevegési listából. + No comment provided by engineer. + + + Please ask your contact to enable calls. + Kérje meg a partnerét, hogy engedélyezze a hívásokat. No comment provided by engineer. Please ask your contact to enable sending voice messages. - Ismerős felkérése, hogy engedélyezze a hangüzenetek küldését. + Kérje meg a partnerét, hogy engedélyezze a hangüzenetek küldését. + No comment provided by engineer. + + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + Ellenőrizze, hogy a hordozható eszköz és a számítógép ugyanahhoz a helyi hálózathoz csatlakozik-e, valamint a számítógép tűzfalában engedélyezve van-e a kapcsolat. +Minden további problémát osszon meg a fejlesztőkkel. No comment provided by engineer. Please check that you used the correct link or ask your contact to send you another one. - Ellenőrizze, hogy a megfelelő hivatkozást használta-e, vagy kérje meg ismerősét, hogy küldjön egy másikat. + Ellenőrizze, hogy a megfelelő hivatkozást használta-e, vagy kérje meg a partnerét, hogy küldjön egy másikat. No comment provided by engineer. Please check your network connection with %@ and try again. - Kérjük, ellenőrizze hálózati kapcsolatát a(z) %@ segítségével, és próbálja újra. + Ellenőrizze a hálózati kapcsolatát a vele: %@, és próbálja újra. No comment provided by engineer. Please check yours and your contact preferences. - Ellenőrizze az ön és ismerőse beállításait. + Ellenőrizze a saját- és a partnere beállításait. No comment provided by engineer. Please confirm that network settings are correct for this device. + Ellenőrizze, hogy a hálózati beállítások megfelelők-e ehhez az eszközhöz. No comment provided by engineer. @@ -4145,22 +5785,22 @@ Hiba: %@ Please contact group admin. - Lépjen kapcsolatba a csoport adminnal. + Lépjen kapcsolatba a csoport adminisztrátorával. No comment provided by engineer. Please enter correct current passphrase. - Adja meg a helyes aktuális jelmondatát. + Adja meg a helyes, jelenlegi jelmondatot. No comment provided by engineer. Please enter the previous password after restoring database backup. This action can not be undone. - Előző jelszó megadása az adatbázis biztonsági mentésének visszaállítása után. Ez a művelet nem visszavonható. + Adja meg a korábbi jelszót az adatbázis biztonsági mentésének visszaállítása után. Ez a művelet nem vonható vissza. No comment provided by engineer. Please remember or store it securely - there is no way to recover a lost passcode! - Jegyezze fel vagy tárolja el biztonságosan - az elveszett jelkódot nem lehet visszaállítani! + Jegyezze fel vagy tárolja el biztonságosan – az elveszett jelkódot nem lehet visszaállítani! No comment provided by engineer. @@ -4170,7 +5810,7 @@ Hiba: %@ Please restart the app and migrate the database to enable push notifications. - Indítsa újra az alkalmazást az adatbázis-migrációhoz szükséges push értesítések engedélyezéséhez. + Indítsa újra az alkalmazást az adatbázis-átköltöztetéséhez szükséges push-értesítések engedélyezéséhez. No comment provided by engineer. @@ -4180,36 +5820,52 @@ Hiba: %@ Please store passphrase securely, you will NOT be able to change it if you lose it. - Tárolja el biztonságosan jelmondatát, mert ha elveszíti azt, NEM tudja megváltoztatni. + Tárolja el biztonságosan jelmondatát, mert ha elveszíti azt, NEM tudja módosítani. No comment provided by engineer. + + Please try to disable and re-enable notfications. + Próbálja meg letiltani és újra engedélyezni az értesítéseket. + token info + + + Please wait for token activation to complete. + Várjon, amíg a token aktiválása befejeződik. + token info + + + Please wait for token to be registered. + Várjon a token regisztrálására. + token info + Polish interface Lengyel kezelőfelület No comment provided by engineer. + + Port + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Lehetséges, hogy a kiszolgáló címében szereplő tanúsítvány-ujjlenyomat helytelen server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Az utolsó üzenet tervezetének megőrzése a mellékletekkel együtt. No comment provided by engineer. - - Preset server - Előre beállított kiszolgáló - No comment provided by engineer. - Preset server address - Előre beállított kiszolgáló címe + Az előre beállított kiszolgáló címe + No comment provided by engineer. + + + Preset servers + Előre beállított kiszolgálók No comment provided by engineer. @@ -4217,19 +5873,54 @@ Hiba: %@ Előnézet No comment provided by engineer. + + Previously connected servers + Korábban kapcsolódott kiszolgálók + No comment provided by engineer. + Privacy & security Adatvédelem és biztonság No comment provided by engineer. + + Privacy for your customers. + Saját ügyfeleinek adatvédelme. + No comment provided by engineer. + + + Privacy policy and conditions of use. + Adatvédelmi szabályzat és felhasználási feltételek. + No comment provided by engineer. + Privacy redefined - Adatvédelem újraértelmezve + Újraértelmezett adatvédelem + No comment provided by engineer. + + + Private chats, groups and your contacts are not accessible to server operators. + A privát csevegések, a csoportok és a partnerek nem érhetők el a szerver üzemeltetői számára. No comment provided by engineer. Private filenames - Privát fájl nevek + Privát fájlnevek + No comment provided by engineer. + + + Private media file names. + Privát nevek a médiafájlokhoz. + No comment provided by engineer. + + + Private message routing + Privát üzenet-útválasztás + No comment provided by engineer. + + + Private message routing 🚀 + Privát üzenet-útválasztás 🚀 No comment provided by engineer. @@ -4237,6 +5928,16 @@ Hiba: %@ Privát jegyzetek name of notes to self + + Private routing + Privát útválasztás + No comment provided by engineer. + + + Private routing error + Privát útválasztási hiba + No comment provided by engineer. + Profile and server connections Profil és kiszolgálókapcsolatok @@ -4247,14 +5948,9 @@ Hiba: %@ Profilkép No comment provided by engineer. - - Profile name - Profilnév - No comment provided by engineer. - - - Profile name: - Profil neve: + + Profile images + Profilképek No comment provided by engineer. @@ -4262,82 +5958,126 @@ Hiba: %@ Profiljelszó No comment provided by engineer. + + Profile theme + Profiltéma + No comment provided by engineer. + Profile update will be sent to your contacts. - A profilfrissítés elküldésre került az ismerősök számára. - No comment provided by engineer. + A profilfrissítés el lesz küldve a partnerei számára. + alert message Prohibit audio/video calls. - Hang- és videóhívások tiltása. + A hívások kezdeményezése le van tiltva. No comment provided by engineer. Prohibit irreversible message deletion. - Az üzenetek véglegesen való törlése le van tiltva. + Az elküldött üzenetek végleges törlése le van tiltva. No comment provided by engineer. Prohibit message reactions. - Üzenetreakciók tiltása. + A reakciók hozzáadása az üzenethez le van tiltva. No comment provided by engineer. Prohibit messages reactions. - Az üzenetreakciók tiltása. + A reakciók hozzáadása az üzenetekhez le van tiltva. + No comment provided by engineer. + + + Prohibit reporting messages to moderators. + Az üzenetek a moderátorok felé történő jelentésének megtiltása. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + A SimpleX-hivatkozások küldése le van tiltva. No comment provided by engineer. Prohibit sending direct messages to members. - Közvetlen üzenetek küldésének letiltása tagok részére. + A közvetlen üzenetek küldése a tagok között le van tiltva. No comment provided by engineer. Prohibit sending disappearing messages. - Eltűnő üzenetek küldésének letiltása. + Az eltűnő üzenetek küldése le van tiltva. No comment provided by engineer. Prohibit sending files and media. - Fájlok- és a médiatartalom küldés letiltása. + A fájlok- és a médiatartalmak küldése le van tiltva. No comment provided by engineer. Prohibit sending voice messages. - Hangüzenetek küldésének letiltása. + A hangüzenetek küldése le van tiltva. + No comment provided by engineer. + + + Protect IP address + IP-cím védelme No comment provided by engineer. Protect app screen - App képernyőjének védelme + Alkalmazás képernyőjének védelme + No comment provided by engineer. + + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + Védje az IP-címét a partnerei által kiválasztott üzenetváltási továbbítókiszolgálókkal szemben. +Engedélyezze a *Hálózat és kiszolgálók* menüben. No comment provided by engineer. Protect your chat profiles with a password! - Csevegési profiljok védelme jelszóval! + Védje meg a csevegési profiljait egy jelszóval! No comment provided by engineer. Protocol timeout - Protokoll időtúllépés + Protokoll időtúllépése No comment provided by engineer. Protocol timeout per KB - Protokoll időkorlát KB-onként + Protokoll időtúllépése kB-onként + No comment provided by engineer. + + + Proxied + Proxyzott + No comment provided by engineer. + + + Proxied servers + Proxyzott kiszolgálók + No comment provided by engineer. + + + Proxy requires password + A proxy jelszót igényel No comment provided by engineer. Push notifications - Push értesítések + Push-értesítések No comment provided by engineer. Push server + Push-kiszolgáló No comment provided by engineer. Quantum resistant encryption + Kvantumbiztos titkosítás No comment provided by engineer. @@ -4345,6 +6085,11 @@ Hiba: %@ Értékelje az alkalmazást No comment provided by engineer. + + Reachable chat toolbar + Könnyen elérhető eszköztár + No comment provided by engineer. + React… Reagálj… @@ -4352,32 +6097,27 @@ Hiba: %@ Read - Olvasd el - No comment provided by engineer. + Olvasott + swipe action Read more Tudjon meg többet No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - További információ a [Felhasználói útmutatóban](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). - További információ a [Felhasználói útmutatóban](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). + További információ a [Használati útmutatóban](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). + No comment provided by engineer. + + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + További információ a [Használati útmutatóban](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). No comment provided by engineer. Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). - További információ a [Felhasználói útmutatóban](https://simplex.chat/docs/guide/readme.html#connect-to-friends). - No comment provided by engineer. - - - Read more in our GitHub repository. - További információ a GitHub tárolónkban. + További információ a [Használati útmutatóban](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. @@ -4387,83 +6127,149 @@ Hiba: %@ Receipts are disabled - Üzenet kézbesítési jelentés letiltva + A kézbesítési jelentések le vannak tiltva + No comment provided by engineer. + + + Receive errors + Üzenetfogadási hibák No comment provided by engineer. Received at - Fogadva ekkor + Fogadva No comment provided by engineer. Received at: %@ - Fogadva ekkor: %@ + Fogadva: %@ copied message info Received file event - Fogadott fájl esemény + Fogadott fájlesemény notification Received message - Fogadott üzenet + Fogadott üzenetbuborék színe message info title + + Received messages + Fogadott üzenetek + No comment provided by engineer. + + + Received reply + Fogadott válaszüzenet-buborék színe + No comment provided by engineer. + + + Received total + Összes fogadott üzenet + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. - A fogadó cím egy másik kiszolgálóra változik. A címváltoztatás a feladó online állapotba kerülése után fejeződik be. + A fogadási cím egy másik kiszolgálóra fog módosulni. A cím módosítása a feladó online állapotba kerülése után fejeződik be. No comment provided by engineer. Receiving file will be stopped. - A fájl fogadása leállt. + A fájl fogadása le fog állni. No comment provided by engineer. Receiving via - Fogadás a + Fogadás a következőn keresztül: No comment provided by engineer. Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). - Legutóbbi előzmények és továbbfejlesztett [könyvtárbot] (simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2TxW3dfMfxy 3%23%2F%3Fv%3D1-2% 26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gloncbqjek4gloncbqjek. + Legutóbbi előzmények és továbbfejlesztett [könyvtárbot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). + No comment provided by engineer. + + + Recipient(s) can't see who this message is from. + A címzett(ek) nem látja(k), hogy kitől származik ez az üzenet. No comment provided by engineer. Recipients see updates as you type them. - A címzettek a beírás közben látják a frissítéseket. + A címzettek a beírás közben látják a szövegváltozásokat. + No comment provided by engineer. + + + Reconnect + Újrakapcsolódás No comment provided by engineer. Reconnect all connected servers to force message delivery. It uses additional traffic. - Az összes csatlakoztatott kiszolgáló újrakapcsolása az üzenetek kézbesítésének kikényszerítéséhez. Ez további forgalmat használ. + Az összes kiszolgálóhoz való újrakapcsolódás az üzenetkézbesítési jelentések kikényszerítéséhez. Ez további adatforgalmat használ. + No comment provided by engineer. + + + Reconnect all servers + Újrakapcsolódás az összes kiszolgálóhoz + No comment provided by engineer. + + + Reconnect all servers? + Újrakapcsolódik az összes kiszolgálóhoz? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + A kiszolgálóhoz való újrakapcsolódás az üzenetkézbesítési jelentések kikényszerítéséhez. Ez további adatforgalmat használ. + No comment provided by engineer. + + + Reconnect server? + Újrakapcsolódik a kiszolgálóhoz? No comment provided by engineer. Reconnect servers? - Kiszolgálók újracsatlakoztatása? + Újrakapcsolódik a kiszolgálókhoz? No comment provided by engineer. Record updated at - A bejegyzés frissítve + Bejegyzés frissítve No comment provided by engineer. Record updated at: %@ - A bejegyzés frissítve: %@ + Bejegyzés frissítve: %@ copied message info Reduced battery usage - Csökkentett akkumulátorhasználat + Csökkentett akkumulátor-használat No comment provided by engineer. + + Register + Regisztrálás + No comment provided by engineer. + + + Register notification token? + Regisztrálja az értesítési tokent? + token info + + + Registered + Regisztrálva + token status text + Reject Elutasítás - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4472,17 +6278,17 @@ Hiba: %@ Reject contact request - Kapcsolatfelvételi kérelem elutasítása + Meghívási kérés elutasítása No comment provided by engineer. Relay server is only used if necessary. Another party can observe your IP address. - Az átjátszó kiszolgáló csak szükség esetén kerül használatra. Egy másik fél megfigyelheti az IP-címét. + A továbbítókiszolgáló csak szükség esetén lesz használva. Egy másik fél megfigyelheti az IP-címet. No comment provided by engineer. Relay server protects your IP address, but it can observe the duration of the call. - Az átjátszó kiszolgáló megvédi IP-címét, de megfigyelheti a hívás időtartamát. + A továbbítókiszolgáló megvédi az Ön IP-címét, de megfigyelheti a hívás időtartamát. No comment provided by engineer. @@ -4490,24 +6296,34 @@ Hiba: %@ Eltávolítás No comment provided by engineer. + + Remove archive? + Eltávolítja az archívumot? + No comment provided by engineer. + + + Remove image + Kép eltávolítása + No comment provided by engineer. + Remove member - Tag eltávolítása + Eltávolítás No comment provided by engineer. Remove member? - Tag eltávolítása? + Eltávolítja a tagot? No comment provided by engineer. Remove passphrase from keychain? - Jelmondat eltávolítása a kulcstárolóból? + Eltávolítja a jelmondatot a kulcstartóból? No comment provided by engineer. Renegotiate - Újraegyzetetés + Újraegyeztetés No comment provided by engineer. @@ -4517,29 +6333,32 @@ Hiba: %@ Renegotiate encryption? - Titkosítás újraegyeztetése? + Újraegyezteti a titkosítást? No comment provided by engineer. Repeat connection request? - Kapcsolódási kérés megismétlése? + Megismétli a meghívási kérést? No comment provided by engineer. Repeat download + Letöltés ismét No comment provided by engineer. Repeat import + Importálás ismét No comment provided by engineer. Repeat join request? - Csatlakozási kérés megismétlése? + Megismétli a meghívási kérést? No comment provided by engineer. Repeat upload + Feltöltés ismét No comment provided by engineer. @@ -4547,24 +6366,99 @@ Hiba: %@ Válasz chat item action + + Report + Jelentés + chat item action + + + Report content: only group moderators will see it. + Tartalom jelentése: csak a csoport moderátorai látják. + report reason + + + Report member profile: only group moderators will see it. + Tag profiljának jelentése: csak a csoport moderátorai látják. + report reason + + + Report other: only group moderators will see it. + Egyéb jelentés: csak a csoport moderátorai látják. + report reason + + + Report reason? + Jelentés indoklása? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + Kéretlen tartalom jelentése: csak a csoport moderátorai látják. + report reason + + + Report violation: only group moderators will see it. + Szabálysértés jelentése: csak a csoport moderátorai látják. + report reason + + + Report: %@ + Jelentés: %@ + report in notification + + + Reporting messages to moderators is prohibited. + Az üzenetek jelentése a moderátorok felé le van tiltva. + No comment provided by engineer. + + + Reports + Jelentések + No comment provided by engineer. + Required - Megkövetelt + Szükséges No comment provided by engineer. Reset - Alaphelyzetbe állítás + Visszaállítás + No comment provided by engineer. + + + Reset all hints + Tippek visszaállítása + No comment provided by engineer. + + + Reset all statistics + Az összes statisztika visszaállítása + No comment provided by engineer. + + + Reset all statistics? + Visszaállítja az összes statisztikát? No comment provided by engineer. Reset colors - Színek alaphelyzetbe állítása + Színek visszaállítása + No comment provided by engineer. + + + Reset to app theme + Alkalmazás témájának visszaállítása No comment provided by engineer. Reset to defaults - Alaphelyzetbe állítás + Visszaállítás alapértelmezettre + No comment provided by engineer. + + + Reset to user theme + Felhasználó által létrehozott téma visszaállítása No comment provided by engineer. @@ -4589,12 +6483,12 @@ Hiba: %@ Restore database backup? - Adatbázismentés visszaállítása? + Visszaállítja az adatbázismentést? No comment provided by engineer. Restore database error - Hiba az adatbázis visszaállításakor + Hiba történt az adatbázis visszaállításakor No comment provided by engineer. @@ -4607,9 +6501,9 @@ Hiba: %@ Felfedés chat item action - - Revert - Visszaállít + + Review conditions + Feltételek felülvizsgálata No comment provided by engineer. @@ -4624,7 +6518,7 @@ Hiba: %@ Revoke file? - Fájl visszavonása? + Visszavonja a fájlt? No comment provided by engineer. @@ -4637,73 +6531,85 @@ Hiba: %@ Csevegési szolgáltatás indítása No comment provided by engineer. - - SMP servers - Üzenetküldő (SMP) kiszolgálók + + SMP server + SMP-kiszolgáló + No comment provided by engineer. + + + SOCKS proxy + SOCKS-proxy + No comment provided by engineer. + + + Safely receive files + Fájlok biztonságos fogadása No comment provided by engineer. Safer groups + Biztonságosabb csoportok No comment provided by engineer. Save Mentés - chat item action + alert button +chat item action Save (and notify contacts) - Mentés (és az ismerősök értesítése) - No comment provided by engineer. + Mentés (és a partnerek értesítése) + alert button Save and notify contact - Mentés és ismerős értesítése - No comment provided by engineer. + Mentés és a partner értesítése + alert button Save and notify group members Mentés és a csoporttagok értesítése No comment provided by engineer. + + Save and reconnect + Mentés és újrakapcsolódás + No comment provided by engineer. + Save and update group profile - Mentés és a csoport profil frissítése - No comment provided by engineer. - - - Save archive - Archívum mentése - No comment provided by engineer. - - - Save auto-accept settings - Automatikus elfogadási beállítások mentése + Mentés és a csoportprofil frissítése No comment provided by engineer. Save group profile - Csoport profil elmentése + Csoportprofil mentése + No comment provided by engineer. + + + Save list + Lista mentése No comment provided by engineer. Save passphrase and open chat - Jelmondat elmentése és csevegés megnyitása + Jelmondat mentése és a csevegés megnyitása No comment provided by engineer. Save passphrase in Keychain - Jelmondat mentése a kulcstárban + Jelmondat mentése a kulcstartóba No comment provided by engineer. Save preferences? - Beállítások mentése? - No comment provided by engineer. + Menti a beállításokat? + alert title Save profile password - Felhasználói fiók jelszavának mentése + Profiljelszó mentése No comment provided by engineer. @@ -4713,22 +6619,32 @@ Hiba: %@ Save servers? - Kiszolgálók mentése? - No comment provided by engineer. - - - Save settings? - Beállítások mentése? - No comment provided by engineer. + Menti a kiszolgálókat? + alert title Save welcome message? - Üdvözlőszöveg mentése? + Menti az üdvözlőüzenetet? + No comment provided by engineer. + + + Save your profile? + Menti a profilt? + alert title + + + Saved + Mentett No comment provided by engineer. Saved WebRTC ICE servers will be removed - A mentett WebRTC ICE kiszolgálók eltávolításra kerülnek + A mentett WebRTC ICE-kiszolgálók el lesznek távolítva + No comment provided by engineer. + + + Saved from + Elmentve innen No comment provided by engineer. @@ -4736,6 +6652,21 @@ Hiba: %@ Mentett üzenet message info title + + Saving %lld messages + %lld üzenet mentése + No comment provided by engineer. + + + Scale + Méretezés + No comment provided by engineer. + + + Scan / Paste link + Hivatkozás beolvasása / beillesztése + No comment provided by engineer. + Scan QR code QR-kód beolvasása @@ -4743,17 +6674,17 @@ Hiba: %@ Scan QR code from desktop - QR-kód beolvasása számítógépről + QR-kód beolvasása a számítógépről No comment provided by engineer. Scan code - Kód beolvasása + Beolvasás No comment provided by engineer. Scan security code from your contact's app. - Biztonsági kód beolvasása ismerős általi alkalmazásból. + Biztonsági kód beolvasása a partnere alkalmazásából. No comment provided by engineer. @@ -4768,19 +6699,29 @@ Hiba: %@ Search bar accepts invitation links. - A keresősáv fogadja a meghívó hivatkozásokat. + A keresősáv elfogadja a meghívási hivatkozásokat. No comment provided by engineer. Search or paste SimpleX link - Keresés, vagy SimpleX hivatkozás beillesztése + Keresés vagy SimpleX-hivatkozás beillesztése + No comment provided by engineer. + + + Secondary + Másodlagos szín No comment provided by engineer. Secure queue - Biztonságos várólista + Biztonságos sorba állítás server test step + + Secured + Biztosítva + No comment provided by engineer. + Security assessment Biztonsági kiértékelés @@ -4793,7 +6734,22 @@ Hiba: %@ Select - Választás + Kijelölés + chat item action + + + Select chat profile + Csevegési profil kijelölése + No comment provided by engineer. + + + Selected %lld + %lld kijelölve + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + A kijelölt csevegési beállítások tiltják ezt az üzenetet. No comment provided by engineer. @@ -4803,17 +6759,17 @@ Hiba: %@ Self-destruct passcode - Önmegsemmisítési jelkód + Önmegsemmisítő-jelkód No comment provided by engineer. Self-destruct passcode changed! - Az önmegsemmisítési jelkód megváltozott! + Az önmegsemmisítő-jelkód módosult! No comment provided by engineer. Self-destruct passcode enabled! - Az önmegsemmisítési jelkód engedélyezve! + Az önmegsemmisítő-jelkód engedélyezve! No comment provided by engineer. @@ -4823,7 +6779,7 @@ Hiba: %@ Send a live message - it will update for the recipient(s) as you type it - Élő üzenet küldése - a címzett(ek) számára frissül, ahogy beírja + Élő üzenet küldése – az üzenet a címzett(ek) számára valós időben frissül, ahogy Ön beírja az üzenetet No comment provided by engineer. @@ -4831,14 +6787,9 @@ Hiba: %@ A kézbesítési jelentéseket a következő címre kell küldeni No comment provided by engineer. - - Send direct message - Közvetlen üzenet küldése - No comment provided by engineer. - Send direct message to connect - A kapcsolódáshoz közvetlen üzenet küldése + Közvetlen üzenet küldése a kapcsolódáshoz No comment provided by engineer. @@ -4846,9 +6797,14 @@ Hiba: %@ Eltűnő üzenet küldése No comment provided by engineer. + + Send errors + Üzenetküldési hibák + No comment provided by engineer. + Send link previews - Hivatkozás előnézetek küldése + Hivatkozás előnézete No comment provided by engineer. @@ -4856,124 +6812,219 @@ Hiba: %@ Élő üzenet küldése No comment provided by engineer. + + Send message to enable calls. + Üzenet küldése a hívások engedélyezéséhez. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + Közvetlen üzenetküldés, ha az IP-cím védett és a saját kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + Közvetlen üzenetküldés, ha a saját kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. + No comment provided by engineer. + Send notifications Értesítések küldése No comment provided by engineer. - - Send notifications: - Értesítések küldése: + + Send private reports + Privát jelentések küldése No comment provided by engineer. Send questions and ideas - Ötletek és kérdések beküldése + Ötletek és javaslatok No comment provided by engineer. Send receipts - Üzenet kézbesítési jelentések + Kézbesítési jelentések küldése No comment provided by engineer. Send them from gallery or custom keyboards. - Küldje el őket galériából vagy egyedi billentyűzetekről. + Küldje el őket a galériából vagy az egyéni billentyűzetekről. No comment provided by engineer. Send up to 100 last messages to new members. - Utolsó 100 üzenet küldése új tagoknak. + Legfeljebb az utolsó 100 üzenet elküldése az új tagok számára. No comment provided by engineer. Sender cancelled file transfer. - A küldő megszakította a fájl átvitelt. - No comment provided by engineer. + A fájl küldője visszavonta az átvitelt. + alert message Sender may have deleted the connection request. - A küldő törölhette a kapcsolódási kérelmet. + A küldője törölhette a meghívási kérést. No comment provided by engineer. Sending delivery receipts will be enabled for all contacts in all visible chat profiles. - A kézbesítési jelentések küldése engedélyezésre kerül az összes látható csevegési profilban lévő minden ismerős számára. + A kézbesítési jelentések küldése engedélyezve lesz az összes látható csevegési profilban lévő összes partnere számára. No comment provided by engineer. Sending delivery receipts will be enabled for all contacts. - A kézbesítési jelentés küldése minden ismerős számára engedélyezésre kerül. + A kézbesítési jelentések küldése az összes partnere számára engedélyezve lesz. No comment provided by engineer. Sending file will be stopped. - A fájl küldése leállt. + A fájl küldése le fog állni. No comment provided by engineer. Sending receipts is disabled for %lld contacts - A kézbesítési jelentések küldése le van tiltva %lld ismerősnél + A kézbesítési jelentések le vannak tiltva %lld partnernél No comment provided by engineer. Sending receipts is disabled for %lld groups - A kézbesítési jelentések küldése le van tiltva %lld csoportban + A kézbesítési jelentések le vannak tiltva %lld csoportban No comment provided by engineer. Sending receipts is enabled for %lld contacts - A kézbesítési jelentések küldése engedélyezve van %lld ismerős számára + A kézbesítési jelentések engedélyezve vannak %lld partnernél No comment provided by engineer. Sending receipts is enabled for %lld groups - A kézbesítési jelentések küldése engedélyezve van %lld csoportban + A kézbesítési jelentések engedélyezve vannak %lld csoportban No comment provided by engineer. Sending via - Küldés ezen keresztül + Küldés a következőn keresztül: No comment provided by engineer. Sent at - Elküldve ekkor + Elküldve No comment provided by engineer. Sent at: %@ - Elküldve ekkor: %@ + Elküldve: %@ copied message info + + Sent directly + Közvetlenül küldött + No comment provided by engineer. + Sent file event - Elküldött fájl esemény + Elküldött fájlesemény notification Sent message - Elküldött üzenet + Üzenetbuborék színe message info title + + Sent messages + Elküldött üzenetek + No comment provided by engineer. + Sent messages will be deleted after set time. - Az elküldött üzenetek törlésre kerülnek a beállított idő után. + Az elküldött üzenetek törölve lesznek a beállított idő után. No comment provided by engineer. + + Sent reply + Válaszüzenet-buborék színe + No comment provided by engineer. + + + Sent total + Összes elküldött üzenet + No comment provided by engineer. + + + Sent via proxy + Proxyn keresztül küldött + No comment provided by engineer. + + + Server + Kiszolgáló + No comment provided by engineer. + + + Server added to operator %@. + Kiszolgáló hozzáadva a következő üzemeltetőhöz: %@. + alert message + + + Server address + Kiszolgáló címe + No comment provided by engineer. + + + Server address is incompatible with network settings. + A kiszolgáló címe nem kompatibilis a hálózati beállításokkal. + srv error text. + + + Server address is incompatible with network settings: %@. + A kiszolgáló címe nem kompatibilis a hálózati beállításokkal: %@. + No comment provided by engineer. + + + Server operator changed. + A kiszolgáló üzemeltetője módosult. + alert title + + + Server operators + Kiszolgálóüzemeltetők + No comment provided by engineer. + + + Server protocol changed. + A kiszolgáló-protokoll módosult. + alert title + Server requires authorization to create queues, check password - A kiszolgálónak engedélyre van szüksége a várólisták létrehozásához, ellenőrizze jelszavát + A kiszolgálónak engedélyre van szüksége a sorba állítás létrehozásához, ellenőrizze a jelszavát server test error Server requires authorization to upload, check password - A kiszolgálónak engedélyre van szüksége a várólisták feltöltéséhez, ellenőrizze jelszavát + A kiszolgálónak hitelesítésre van szüksége a feltöltéshez, ellenőrizze jelszavát server test error Server test failed! - A kiszolgáló tesztje sikertelen! + Sikertelen kiszolgáló teszt! + No comment provided by engineer. + + + Server type + Kiszolgáló típusa + No comment provided by engineer. + + + Server version is incompatible with network settings. + A kiszolgáló verziója nem kompatibilis a hálózati beállításokkal. + srv error text + + + Server version is incompatible with your app: %@. + A kiszolgáló verziója nem kompatibilis az alkalmazással: %@. No comment provided by engineer. @@ -4981,6 +7032,16 @@ Hiba: %@ Kiszolgálók No comment provided by engineer. + + Servers info + Információk a kiszolgálókról + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + A kiszolgálók statisztikái visszaállnak – ez a művelet nem vonható vissza! + No comment provided by engineer. + Session code Munkamenet kód @@ -4991,9 +7052,19 @@ Hiba: %@ Beállítva 1 nap No comment provided by engineer. + + Set chat name… + Csevegés nevének beállítása… + No comment provided by engineer. + Set contact name… - Ismerős nevének beállítása… + Partner nevének beállítása… + No comment provided by engineer. + + + Set default theme + Alapértelmezett téma beállítása No comment provided by engineer. @@ -5003,7 +7074,12 @@ Hiba: %@ Set it instead of system authentication. - Rendszerhitelesítés helyetti beállítás. + Beállítás a rendszer-hitelesítés helyett. + No comment provided by engineer. + + + Set message expiration in chats. + Üzenetek eltűnési idejének módosítása a csevegésekben. No comment provided by engineer. @@ -5013,6 +7089,7 @@ Hiba: %@ Set passphrase + Jelmondat beállítása No comment provided by engineer. @@ -5022,7 +7099,7 @@ Hiba: %@ Set the message shown to new members! - Megjelenő üzenetet beállítása új tagok részére! + Megjelenítendő üzenet beállítása az új tagok számára! No comment provided by engineer. @@ -5035,43 +7112,90 @@ Hiba: %@ Beállítások No comment provided by engineer. + + Settings were changed. + A beállítások módosultak. + alert message + + + Shape profile images + Profilkép alakzata + No comment provided by engineer. + Share Megosztás - chat item action + alert action +chat item action Share 1-time link - Egyszer használatos hivatkozás megosztása + Egyszer használható meghívó megosztása + No comment provided by engineer. + + + Share 1-time link with a friend + Egyszer használható meghívó megosztása egy baráttal + No comment provided by engineer. + + + Share SimpleX address on social media. + SimpleX-cím megosztása a közösségi médiában. No comment provided by engineer. Share address - Azonosító megosztása + Cím megosztása + No comment provided by engineer. + + + Share address publicly + Cím nyilvános megosztása No comment provided by engineer. Share address with contacts? - Megosztja az azonosítót az ismerősökkel? + Megosztja a címet a partnereivel? + alert title + + + Share from other apps. + Megosztás más alkalmazásokból. No comment provided by engineer. Share link - Hivatkozás megosztása + Megosztás + No comment provided by engineer. + + + Share profile + Profil megosztása No comment provided by engineer. Share this 1-time invite link - Egyszer használatos meghívó hivatkozás megosztása + Ennek az egyszer használható meghívónak a megosztása + No comment provided by engineer. + + + Share to SimpleX + Megosztás a SimpleXben No comment provided by engineer. Share with contacts - Megosztás ismerősökkel + Megosztás a partnerekkel + No comment provided by engineer. + + + Short link + Rövid hivatkozás No comment provided by engineer. Show QR code + QR-kód megjelenítése No comment provided by engineer. @@ -5081,27 +7205,52 @@ Hiba: %@ Show developer options - Fejlesztői beállítások mutatása + Fejlesztői beállítások megjelenítése No comment provided by engineer. Show last messages - Utolsó üzenetek megjelenítése + Legutóbbi üzenet előnézetének megjelenítése + No comment provided by engineer. + + + Show message status + Üzenet állapotának megjelenítése + No comment provided by engineer. + + + Show percentage + Százalék megjelenítése No comment provided by engineer. Show preview - Előnézet megjelenítése + Értesítés előnézete + No comment provided by engineer. + + + Show → on messages sent via private routing. + Egy „→” jel megjelenítése a privát útválasztáson keresztül küldött üzeneteknél. No comment provided by engineer. Show: - Mutat: + Megjelenítve: + No comment provided by engineer. + + + SimpleX + SimpleX No comment provided by engineer. SimpleX Address - SimpleX azonosító + SimpleX-cím + No comment provided by engineer. + + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + A SimpleX Chat és a Flux megállapodást kötött arról, hogy a Flux által üzemeltetett kiszolgálókat beépítik az alkalmazásba. No comment provided by engineer. @@ -5111,32 +7260,47 @@ Hiba: %@ SimpleX Lock - SimpleX zárolás + SimpleX-zár No comment provided by engineer. SimpleX Lock mode - SimpleX zárolási mód + Zárolási mód No comment provided by engineer. SimpleX Lock not enabled! - SimpleX zárolás nincs engedélyezve! + A SimpleX-zár nincs bekapcsolva! No comment provided by engineer. SimpleX Lock turned on - SimpleX zárolás bekapcsolva + SimpleX-zár bekapcsolva No comment provided by engineer. SimpleX address - SimpleX azonosító + SimpleX-cím No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + A SimpleX-cím és az egyszer használható meghívó biztonságosan megosztható bármilyen üzenetváltó-alkalmazáson keresztül. + No comment provided by engineer. + + + SimpleX address or 1-time link? + SimpleX-cím vagy egyszer használható meghívó? + No comment provided by engineer. + + + SimpleX channel link + SimpleX-csatornahivatkozás + simplex link type + SimpleX contact address - SimpleX ismerős azonosítója + SimpleX kapcsolattartási cím simplex link type @@ -5146,22 +7310,42 @@ Hiba: %@ SimpleX group link - SimpleX csoport hivatkozás + SimpleX-csoporthivatkozás simplex link type SimpleX links - SimpleX hivatkozások + SimpleX-hivatkozások + chat feature + + + SimpleX links are prohibited. + A SimpleX-hivatkozások küldése le van tiltva. + No comment provided by engineer. + + + SimpleX links not allowed + A SimpleX-hivatkozások küldése le van tiltva No comment provided by engineer. SimpleX one-time invitation - SimpleX egyszer használatos meghívó + Egyszer használható SimpleX-meghívó simplex link type + + SimpleX protocols reviewed by Trail of Bits. + A SimpleX Chat biztonsága a Trail of Bits által lett felülvizsgálva. + No comment provided by engineer. + Simplified incognito mode - Egyszerűsített inkognító mód + Egyszerűsített inkognitómód + No comment provided by engineer. + + + Size + Méret No comment provided by engineer. @@ -5179,16 +7363,54 @@ Hiba: %@ Kis csoportok (max. 20 tag) No comment provided by engineer. + + Soft + Enyhe + blur media + + + Some app settings were not migrated. + Egyes alkalmazásbeállítások nem lettek átköltöztetve. + No comment provided by engineer. + + + Some file(s) were not exported: + Néhány fájl nem lett exportálva: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. - Néhány nem végzetes hiba történt az importálás során – további részletekért a csevegési konzolban olvashat. + Néhány nem végzetes hiba történt az importáláskor – további részleteket a csevegési konzolban olvashat. No comment provided by engineer. + + Some non-fatal errors occurred during import: + Néhány nem végzetes hiba történt az importáláskor: + No comment provided by engineer. + + + Some servers failed the test: +%@ + Néhány kiszolgáló megbukott a teszten: +%@ + alert message + Somebody Valaki notification title + + Spam + Kéretlen tartalom + blocking reason +report reason + + + Square, circle, or anything in between. + Négyzet, kör vagy bármi a kettő között. + No comment provided by engineer. + Start chat Csevegés indítása @@ -5196,12 +7418,22 @@ Hiba: %@ Start chat? - Csevegés indítása? + Elindítja a csevegést? No comment provided by engineer. Start migration - Migráció indítása + Átköltöztetés indítása + No comment provided by engineer. + + + Starting from %@. + Statisztikagyűjtés kezdete: %@. + No comment provided by engineer. + + + Statistics + Statisztikák No comment provided by engineer. @@ -5211,26 +7443,22 @@ Hiba: %@ Stop SimpleX - A SimpleX megállítása + SimpleX megállítása authentication reason Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - Csevegés leállítása az adatbázis-műveletek engedélyezéséhez + Csevegési szolgáltatás megállítása No comment provided by engineer. Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. - A csevegés leállítása a csevegőadatbázis exportálásához, importálásához vagy törléséhez. A csevegés leállítása alatt nem tud üzeneteket fogadni és küldeni. + A csevegés megállítása a csevegési adatbázis exportálásához, importálásához vagy törléséhez. A csevegés megállításakor nem tud üzeneteket fogadni és küldeni. No comment provided by engineer. Stop chat? - Csevegési szolgáltatás megállítása? + Megállítja a csevegést? No comment provided by engineer. @@ -5240,36 +7468,72 @@ Hiba: %@ Stop receiving file? - Fájl fogadás megszakítása? + Megállítja a fájlfogadást? No comment provided by engineer. Stop sending file? - Fájl küldés megszakítása? + Megállítja a fájlküldést? No comment provided by engineer. Stop sharing - Megosztás leállítása - No comment provided by engineer. + Megosztás megállítása + alert action Stop sharing address? - Címmegosztás megállítása? - No comment provided by engineer. + Megállítja a címmegosztást? + alert title Stopping chat + Csevegés megállítása folyamatban No comment provided by engineer. + + Storage + Tárhely + No comment provided by engineer. + + + Strong + Erős + blur media + Submit Elküldés No comment provided by engineer. + + Subscribed + Feliratkozva + No comment provided by engineer. + + + Subscription errors + Feliratkozási hibák + No comment provided by engineer. + + + Subscriptions ignored + Mellőzött feliratkozások + No comment provided by engineer. + Support SimpleX Chat - Támogassa a SimpleX Chatet + SimpleX Chat támogatása + No comment provided by engineer. + + + Switch audio and video during the call. + Hang/Videó váltása hívás közben. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Csevegési profilváltás az egyszer használható meghívókhoz. No comment provided by engineer. @@ -5279,12 +7543,22 @@ Hiba: %@ System authentication - Rendszerhitelesítés + Rendszer-hitelesítés + No comment provided by engineer. + + + TCP connection + TCP-kapcsolat No comment provided by engineer. TCP connection timeout - TCP kapcsolat időtúllépés + TCP-kapcsolat időtúllépése + No comment provided by engineer. + + + TCP port for messaging + TCP-port az üzenetváltáshoz No comment provided by engineer. @@ -5302,19 +7576,29 @@ Hiba: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + Farok + No comment provided by engineer. + Take picture - Fotó készítése + Kép készítése + No comment provided by engineer. + + + Tap Create SimpleX address in the menu to create it later. + Koppintson a SimpleX-cím létrehozása menüpontra a későbbi létrehozáshoz. No comment provided by engineer. Tap button - Koppintson a gombra + Koppintson a No comment provided by engineer. Tap to Connect - Koppintson a csatlakozáshoz + Koppintson ide a kapcsolódáshoz No comment provided by engineer. @@ -5324,34 +7608,39 @@ Hiba: %@ Tap to join - Koppintson a csatlakozáshoz + Koppintson ide a csatlakozáshoz No comment provided by engineer. Tap to join incognito - Koppintson az inkognitómódhoz való csatlakozáshoz + Koppintson ide az inkognitóban való kapcsolódáshoz No comment provided by engineer. Tap to paste link - Koppintson a hivatkozás beillesztéséhez + Koppintson ide a hivatkozás beillesztéséhez No comment provided by engineer. Tap to scan - Koppintson a beolvasáshoz + Koppintson ide a QR-kód beolvasásához No comment provided by engineer. - - Tap to start a new chat - Koppintson az új csevegés indításához - No comment provided by engineer. + + Temporary file error + Ideiglenes fájlhiba + file error alert title Test failed at step %@. - A teszt sikertelen volt a(z) %@ lépésnél. + A teszt a(z) %@ lépésnél sikertelen volt. server test failure + + Test notifications + Értesítések tesztelése + No comment provided by engineer. + Test server Kiszolgáló tesztelése @@ -5365,7 +7654,7 @@ Hiba: %@ Tests failed! Sikertelen tesztek! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5374,54 +7663,64 @@ Hiba: %@ Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! - Köszönet a felhasználóknak – [hozzájárulás a Weblate-en keresztül](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + Köszönet a felhasználóknak [a Weblate-en való közreműködésért](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. Thanks to the users – contribute via Weblate! - Köszönet a felhasználóknak - hozzájárulás a Weblaten! - No comment provided by engineer. - - - The 1st platform without any user identifiers – private by design. - Az első csevegési rendszer bármiféle felhasználó azonosító nélkül - privátra lett tervezre. + Köszönet a felhasználóknak a Weblate-en való közreműködésért! No comment provided by engineer. The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. - A következő üzenet azonosítója hibás (kisebb vagy egyenlő az előzővel). -Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. + A következő üzenet azonosítója érvénytelen (kisebb vagy egyenlő az előzővel). +Ez valamilyen hiba vagy sérült kapcsolat esetén fordulhat elő. No comment provided by engineer. The app can notify you when you receive messages or contact requests - please open settings to enable. - Az alkalmazás értesíteni fogja, amikor üzeneteket vagy kapcsolatfelvételi kéréseket kap – beállítások megnyitása az engedélyezéshez. + Az alkalmazás értesíteni fogja, amikor üzeneteket vagy meghívási kéréseket kap – ezt a beállítások menüben engedélyezheti. + No comment provided by engineer. + + + The app protects your privacy by using different operators in each conversation. + Az alkalmazás úgy védi az adatait, hogy minden egyes beszélgetéshez más-más üzemeltetőt használ. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + Az alkalmazás kérni fogja az ismeretlen fájlkiszolgálókról (kivéve .onion) történő letöltések megerősítését. No comment provided by engineer. The attempt to change database passphrase was not completed. - Az adatbázis jelmondatának megváltoztatására tett kísérlet nem fejeződött be. + Az adatbázis jelmondatának módosítására tett kísérlet nem fejeződött be. No comment provided by engineer. The code you scanned is not a SimpleX link QR code. - A beolvasott kód nem egy SimpleX hivatkozás QR-kód. + A beolvasott QR-kód nem egy SimpleX-QR-kód-hivatkozás. + No comment provided by engineer. + + + The connection reached the limit of undelivered messages, your contact may be offline. + A kapcsolat elérte a kézbesítetlen üzenetek számának határát, a partnere lehet, hogy offline állapotban van. No comment provided by engineer. The connection you accepted will be cancelled! - Az ön által elfogadott kapcsolat megszakad! + Az Ön által elfogadott kérelem vissza lesz vonva! No comment provided by engineer. The contact you shared this link with will NOT be able to connect! - Ismerőse NEM fog tudni csatlakozni, akivel megosztotta ezt a hivatkozást! + A partnere, akivel megosztotta ezt a hivatkozást, NEM fog tudni kapcsolódni! No comment provided by engineer. The created archive is available via app Settings / Database / Old database archive. - A létrehozott archívum a Beállítások / Adatbázis / Régi adatbázis-archívum menüpontban érhető el. + A létrehozott archívum a „Beállítások / Adatbázis / Régi adatbázis-archívum” menüben érhető el. No comment provided by engineer. @@ -5429,34 +7728,49 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. A titkosítás működik, és új titkosítási egyezményre nincs szükség. Ez kapcsolati hibákat eredményezhet! No comment provided by engineer. + + The future of messaging + Az üzenetváltás jövője + No comment provided by engineer. + The hash of the previous message is different. - Az előző üzenet hash-e más. + Az előző üzenet hasítóértéke különbözik. No comment provided by engineer. The message will be deleted for all members. - Az üzenet minden tag számára törlésre kerül. + Az üzenet az összes tag számára törölve lesz. No comment provided by engineer. The message will be marked as moderated for all members. - Az üzenet minden tag számára moderáltként lesz megjelölve. + Az üzenet az összes tag számára moderáltként lesz megjelölve. No comment provided by engineer. - - The next generation of private messaging - A privát üzenetküldés következő generációja + + The messages will be deleted for all members. + Az üzenetek az összes tag számára törölve lesznek. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. + Az üzenetek az összes tag számára moderáltként lesznek megjelölve. No comment provided by engineer. The old database was not removed during the migration, it can be deleted. - A régi adatbázis nem került eltávolításra a migráció során, így törölhető. + A régi adatbázis nem lett eltávolítva az átköltöztetéskor, ezért törölhető. No comment provided by engineer. - - The profile is only shared with your contacts. - Profilja csak az ismerősök számára kerül megosztásra. + + The same conditions will apply to operator **%@**. + Ugyanezek a feltételek lesznek elfogadva a következő üzemeltető számára is: **%@**. + No comment provided by engineer. + + + The second preset operator in the app! + A második előre beállított üzemeltető az alkalmazásban! No comment provided by engineer. @@ -5471,50 +7785,72 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. The servers for new connections of your current chat profile **%@**. - Jelenlegi profil új ismerőseinek kiszolgálói **%@**. + A jelenlegi **%@** nevű csevegési profiljához tartozó új kapcsolatok kiszolgálói. + No comment provided by engineer. + + + The servers for new files of your current chat profile **%@**. + A jelenlegi **%@** nevű csevegési profiljához tartozó új fájlok kiszolgálói. No comment provided by engineer. The text you pasted is not a SimpleX link. - A beillesztett szöveg nem egy SimpleX hivatkozás. + A beillesztett szöveg nem egy SimpleX-hivatkozás. No comment provided by engineer. - - Theme - Téma + + The uploaded database archive will be permanently removed from the servers. + A feltöltött adatbázis-archívum véglegesen el lesz távolítva a kiszolgálókról. + No comment provided by engineer. + + + Themes + Témák + No comment provided by engineer. + + + These conditions will also apply for: **%@**. + Ezek a feltételek lesznek elfogadva a következő számára is: **%@**. No comment provided by engineer. These settings are for your current profile **%@**. - Ezek a beállítások a jelenlegi **%@** profiljára vonatkoznak. + Ezek a beállítások csak a jelenlegi **%@** nevű csevegési profiljára vonatkoznak. No comment provided by engineer. They can be overridden in contact and group settings. - Ezek felülbírálhatóak az ismerős- és csoportbeállításokban. + Ezek felülbírálhatók a partner- és csoportbeállításokban. No comment provided by engineer. This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain. - Ez a művelet nem vonható vissza - az összes fogadott és küldött fájl a médiatartalommal együtt törlésre kerülnek. Az alacsony felbontású fotók viszont megmaradnak. + Ez a művelet nem vonható vissza – az összes fogadott és küldött fájl a médiatartalmakkal együtt törölve lesznek. Az alacsony felbontású képek viszont megmaradnak. No comment provided by engineer. This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes. - Ez a művelet nem vonható vissza - a kiválasztottnál korábban küldött és fogadott üzenetek törlésre kerülnek. Ez több percet is igénybe vehet. + Ez a művelet nem vonható vissza – a kijelöltnél korábban küldött és fogadott üzenetek törölve lesznek. Ez több percet is igénybe vehet. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + Ez a művelet nem vonható vissza – a kijelölt üzenettől korábban küldött és fogadott üzenetek törölve lesznek a csevegésből. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. - Ez a művelet nem vonható vissza - profilok, ismerősök, üzenetek és fájlok visszafordíthatatlanul törlésre kerülnek. + Ez a művelet nem vonható vissza – profiljai, partnerei, üzenetei és fájljai véglegesen törölve lesznek. No comment provided by engineer. This chat is protected by end-to-end encryption. + Ez a csevegés végpontok közötti titkosítással védett. E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + Ez a csevegés végpontok közötti kvantumbiztos titkosítással védett. E2EE info chat item @@ -5524,12 +7860,12 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. This display name is invalid. Please choose another name. - Ez a megjelenített felhasználónév érvénytelen. Válasszon egy másik nevet. + Ez a megjelenítendő név érvénytelen. Válasszon egy másik nevet. No comment provided by engineer. This group has over %lld members, delivery receipts are not sent. - Ennek a csoportnak több mint %lld tagja van, a kézbesítési jelentések nem kerülnek elküldésre. + Ennek a csoportnak több mint %lld tagja van, a kézbesítési jelentések nem lesznek elküldve. No comment provided by engineer. @@ -5539,17 +7875,37 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. This is your own SimpleX address! - Ez a SimpleX azonosítója! + Ez a saját SimpleX-címe! No comment provided by engineer. This is your own one-time link! - Ez az egyszer használatos hivatkozása! + Ez a saját egyszer használható meghívója! + No comment provided by engineer. + + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + Ez a hivatkozás újabb alkalmazásverziót igényel. Frissítse az alkalmazást vagy kérjen egy kompatibilis hivatkozást a partnerétől. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + Ezt a hivatkozást egy másik hordozható eszközön már használták, hozzon létre egy új hivatkozást a számítógépén. + No comment provided by engineer. + + + This message was deleted or not received yet. + Ez az üzenet törölve lett vagy még nem érkezett meg. No comment provided by engineer. This setting applies to messages in your current chat profile **%@**. - Ez a beállítás a jelenlegi **%@** profiljában lévő üzenetekre érvényes. + Ez a beállítás csak az Ön jelenlegi **%@** nevű csevegési profiljában lévő üzenetekre vonatkozik. + No comment provided by engineer. + + + Title + Cím No comment provided by engineer. @@ -5559,7 +7915,7 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. To connect, your contact can scan QR code or use the link in the app. - A csatlakozáshoz az ismerős beolvashatja a QR-kódot, vagy használhatja az alkalmazásban található hivatkozást. + A kapcsolódáshoz a partnere beolvashatja a QR-kódot, vagy használhatja az alkalmazásban található hivatkozást. No comment provided by engineer. @@ -5572,21 +7928,46 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. Új kapcsolat létrehozásához No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Az adatvédelem érdekében, a más csevegési platformokon megszokott felhasználói azonosítók helyett, a SimpleX üzenetsorokhoz rendel azonosítókat, minden egyes ismerőshöz egy különbözőt. + + To protect against your link being replaced, you can compare contact security codes. + A hivatkozás cseréje elleni védelem érdekében összehasonlíthatja a biztonsági kódokat a partnerével. No comment provided by engineer. To protect timezone, image/voice files use UTC. - Az időzóna védelme érdekében a kép-/hangfájlok UTC-t használnak. + Az időzóna védelmének érdekében a kép-/hangfájlok UTC-t használnak. + No comment provided by engineer. + + + To protect your IP address, private routing uses your SMP servers to deliver messages. + Az IP-cím védelmének érdekében a privát útválasztás az SMP-kiszolgálókat használja az üzenetek kézbesítéséhez. No comment provided by engineer. To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. - Az adatavédelem érdekében kapcsolja be a SimpleX zárolás funkciót. -A funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befejezésére. + A biztonsága érdekében kapcsolja be a SimpleX-zár funkciót. +A funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beállítására az eszközén. + No comment provided by engineer. + + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Adatainak védelme érdekében a SimpleX külön üzenet-azonosítókat használ minden egyes kapcsolatához. + No comment provided by engineer. + + + To receive + A fogadáshoz + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + A beszéd rögzítéséhez adjon engedélyt a Mikrofon használatára. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + A videó rögzítéséhez adjon engedélyt a Kamera használatára. No comment provided by engineer. @@ -5596,37 +7977,72 @@ A funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befej To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. - Rejtett profilja feltárásához írja be a teljes jelszót a keresőmezőbe a **Csevegési profiljai** oldalon. + Rejtett profilja felfedéséhez adja meg a teljes jelszót a keresőmezőben, a **Csevegési profilok** menüben. + No comment provided by engineer. + + + To send + A küldéshez No comment provided by engineer. To support instant push notifications the chat database has to be migrated. - Az azonnali push értesítések támogatásához a csevegési adatbázis migrálása szükséges. + Az azonnali push-értesítések támogatásához a csevegési adatbázis átköltöztetése szükséges. + No comment provided by engineer. + + + To use the servers of **%@**, accept conditions of use. + A(z) **%@** kiszolgálóinak használatához fogadja el a használati feltételeket. No comment provided by engineer. To verify end-to-end encryption with your contact compare (or scan) the code on your devices. - A végpontok közötti titkosítás ellenőrzéséhez ismerősével hasonlítsa össze (vagy szkennelje be) az eszközén lévő kódot. + A végpontok közötti titkosítás hitelesítéséhez hasonlítsa össze (vagy olvassa be a QR-kódot) a partnere eszközén lévő kóddal. + No comment provided by engineer. + + + Toggle chat list: + Csevegési lista átváltása: No comment provided by engineer. Toggle incognito when connecting. - Inkognító mód csatlakozáskor. + Inkognitóra váltás kapcsolódáskor. + No comment provided by engineer. + + + Token status: %@. + Token állapota: %@. + token status + + + Toolbar opacity + Eszköztár átlátszatlansága + No comment provided by engineer. + + + Total + Összes kapcsolat No comment provided by engineer. Transport isolation - Kapcsolat izolációs mód + Átvitel-izoláció + No comment provided by engineer. + + + Transport sessions + Munkamenetek átvitele No comment provided by engineer. Trying to connect to the server used to receive messages from this contact (error: %@). - Csatlakozási kísérlet a kapcsolat üzeneteinek fogadására használt kiszolgálóhoz (hiba: %@). + Kapcsolódási kísérlet ahhoz a kiszolgálóhoz, amely az adott partnerétől érkező üzenetek fogadására szolgál (hiba: %@). No comment provided by engineer. Trying to connect to the server used to receive messages from this contact. - Csatlakozási kísérlet a kapcsolat üzeneteinek fogadására használt kiszolgálóhoz ettől az ismerőstől. + Kapcsolódási kísérlet ahhoz a kiszolgálóhoz, amely az adott partnerétől érkező üzenetek fogadására szolgál. No comment provided by engineer. @@ -5656,7 +8072,7 @@ A funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befej Unblock for all - Letiltás feloldása mindenki számára + Feloldás No comment provided by engineer. @@ -5666,28 +8082,28 @@ A funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befej Unblock member for all? - Mindenki számára feloldja a tag letiltását? + Az összes tag számára feloldja a tag letiltását? No comment provided by engineer. Unblock member? - Tag feloldása? + Feloldja a tag letiltását? No comment provided by engineer. - - Unexpected error: %@ - Váratlan hiba: %@ - item status description + + Undelivered messages + Kézbesítetlen üzenetek + No comment provided by engineer. Unexpected migration state - Váratlan migrációs állapot + Váratlan átköltöztetési állapot No comment provided by engineer. Unfav. - Nem kedvelt. - No comment provided by engineer. + Kedvenc megszüntetése + swipe action Unhide @@ -5724,6 +8140,11 @@ A funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befej Ismeretlen hiba No comment provided by engineer. + + Unknown servers! + Ismeretlen kiszolgálók! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. Hacsak nem az iOS hívási felületét használja, engedélyezze a Ne zavarjanak módot a megszakítások elkerülése érdekében. @@ -5732,8 +8153,8 @@ A funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befej Unless your contact deleted the connection or this link was already used, it might be a bug - please report it. To connect, please ask your contact to create another connection link and check that you have a stable network connection. - Hacsak az ismerős nem törölte a kapcsolatot, vagy ez a hivatkozás már használatban volt, hiba lehet – kérjük, jelentse. -A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsolati hivatkozást, és ellenőrizze, hogy a hálózati kapcsolat stabil-e. + Hacsak a partnere nem törölte a kapcsolatot, vagy ez a hivatkozás már használatban volt egyszer, lehet hogy ez egy hiba – jelentse a problémát. +A kapcsolódáshoz kérje meg a partnerét, hogy hozzon létre egy másik kapcsolattartási hivatkozást, és ellenőrizze, hogy a hálózati kapcsolat stabil-e. No comment provided by engineer. @@ -5743,7 +8164,7 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Unlink desktop? - Számítógép szétkapcsolása? + Leválasztja a számítógépet? No comment provided by engineer. @@ -5758,17 +8179,22 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Unmute - Némítás feloldása - No comment provided by engineer. + Némítás megszüntetése + notification label action Unread Olvasatlan + swipe action + + + Unsupported connection link + Nem támogatott kapcsolattartási hivatkozás No comment provided by engineer. Up to 100 last messages are sent to new members. - Legfeljebb az utolsó 100 üzenet kerül elküldésre az új tagoknak. + Legfeljebb az utolsó 100 üzenet lesz elküldve az új tagok számára. No comment provided by engineer. @@ -5776,43 +8202,44 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Frissítés No comment provided by engineer. - - Update .onion hosts setting? - Tor .onion host beállítások frissítése? - No comment provided by engineer. - Update database passphrase - Adatbázis jelmondat megváltoztatása + Az adatbázis jelmondatának módosítása No comment provided by engineer. Update network settings? - Hálózati beállítások megváltoztatása? + Módosítja a hálózati beállításokat? No comment provided by engineer. - - Update transport isolation mode? - Kapcsolat izolációs mód frissítése? + + Update settings? + Frissíti a beállításokat? + No comment provided by engineer. + + + Updated conditions + Frissített feltételek No comment provided by engineer. Updating settings will re-connect the client to all servers. - A beállítások frissítése a szerverekhez újra kapcsolódással jár. - No comment provided by engineer. - - - Updating this setting will re-connect the client to all servers. - A beállítás frissítésével a kliens újracsatlakozik az összes kiszolgálóhoz. + A beállítások frissítése a kiszolgálókhoz való újra kapcsolódással jár. No comment provided by engineer. Upgrade and open chat - A csevegés frissítése és megnyitása + Fejlesztés és a csevegés megnyitása + No comment provided by engineer. + + + Upload errors + Feltöltési hibák No comment provided by engineer. Upload failed + Sikertelen feltöltés No comment provided by engineer. @@ -5820,23 +8247,54 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Fájl feltöltése server test step + + Uploaded + Feltöltve + No comment provided by engineer. + + + Uploaded files + Feltöltött fájlok + No comment provided by engineer. + Uploading archive + Archívum feltöltése + No comment provided by engineer. + + + Use %@ + %@ használata No comment provided by engineer. Use .onion hosts - Tor .onion hostok használata + Onion-kiszolgálók használata + No comment provided by engineer. + + + Use SOCKS proxy + SOCKS-proxy használata No comment provided by engineer. Use SimpleX Chat servers? - SimpleX Chat kiszolgálók használata? + SimpleX Chat-kiszolgálók használata? + No comment provided by engineer. + + + Use TCP port %@ when no port is specified. + A következő TCP-port használata, amikor nincs port megadva: %@. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + A 443-as TCP-port használata kizárólag az előre beállított kiszolgálokhoz. No comment provided by engineer. Use chat - Csevegés használata + SimpleX Chat használata No comment provided by engineer. @@ -5844,6 +8302,16 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Jelenlegi profil használata No comment provided by engineer. + + Use for files + Használat a fájlokhoz + No comment provided by engineer. + + + Use for messages + Használat az üzenetekhez + No comment provided by engineer. + Use for new connections Alkalmazás új kapcsolatokhoz @@ -5851,17 +8319,17 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Use from desktop - Használat számítógépről + Társítás számítógéppel No comment provided by engineer. Use iOS call interface - Az iOS hívófelület használata + Az iOS hívási felületét használata No comment provided by engineer. Use new incognito profile - Az új inkognító profil használata + Új inkognitóprofil használata No comment provided by engineer. @@ -5869,61 +8337,94 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Csak helyi értesítések használata? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + Használjon privát útválasztást ismeretlen kiszolgálókkal, ha az IP-cím nem védett. + No comment provided by engineer. + + + Use private routing with unknown servers. + Használjon privát útválasztást ismeretlen kiszolgálókkal. + No comment provided by engineer. + Use server Kiszolgáló használata No comment provided by engineer. + + Use servers + Kiszolgálók használata + No comment provided by engineer. + + + Use short links (BETA) + Rövid hivatkozások használata (béta) + No comment provided by engineer. + Use the app while in the call. + Használja az alkalmazást hívás közben. No comment provided by engineer. - - User profile - Felhasználói profil + + Use the app with one hand. + Használja az alkalmazást egy kézzel. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - A .onion hosztok használatához kompatibilis VPN szolgáltatóra van szükség. + + Use web port + Webport használata + No comment provided by engineer. + + + User selection + Felhasználó kijelölése + No comment provided by engineer. + + + Username + Felhasználónév No comment provided by engineer. Using SimpleX Chat servers. - SimpleX Chat kiszolgálók használatban. + SimpleX Chat-kiszolgálók használatban. No comment provided by engineer. Verify code with desktop - Kód ellenőrzése a számítógépen + Kód hitelesítése a számítógépen No comment provided by engineer. Verify connection - Kapcsolat ellenőrzése + Kapcsolat hitelesítése No comment provided by engineer. Verify connection security - Kapcsolat biztonságának ellenőrzése + Biztonságos kapcsolat hitelesítése No comment provided by engineer. Verify connections - Kapcsolatok ellenőrzése + Kapcsolatok hitelesítése No comment provided by engineer. Verify database passphrase + Az adatbázis jelmondatának hitelesítése No comment provided by engineer. Verify passphrase + Jelmondat hitelesítése No comment provided by engineer. Verify security code - Biztonsági kód ellenőrzése + Biztonsági kód hitelesítése No comment provided by engineer. @@ -5933,7 +8434,7 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Via secure quantum resistant protocol. - Biztonságos kvantum ellenálló protokoll által. + Biztonságos kvantumbiztos protokollon keresztül. No comment provided by engineer. @@ -5943,17 +8444,22 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Video will be received when your contact completes uploading it. - A videó akkor érkezik meg, amikor az ismerőse befejezte annak feltöltését. + A videó akkor érkezik meg, amikor a küldője befejezte annak feltöltését. No comment provided by engineer. Video will be received when your contact is online, please wait or check later! - A videó akkor érkezik meg, amikor az ismerős elérhető, várjon, vagy ellenőrizze később! + A videó akkor érkezik meg, amikor a küldője elérhető lesz, várjon, vagy ellenőrizze később! No comment provided by engineer. Videos and files up to 1gb - Videók és fájlok 1Gb méretig + Videók és fájlok legfeljebb 1GB méretig + No comment provided by engineer. + + + View conditions + Feltételek megtekintése No comment provided by engineer. @@ -5961,6 +8467,11 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Biztonsági kód megtekintése No comment provided by engineer. + + View updated conditions + Frissített feltételek megtekintése + No comment provided by engineer. + Visible history Látható előzmények @@ -5973,17 +8484,22 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Voice messages are prohibited in this chat. - A hangüzenetek le vannak tiltva ebben a csevegésben. + A hangüzenetek küldése le van tiltva ebben a csevegésben. No comment provided by engineer. - - Voice messages are prohibited in this group. - A hangüzenetek küldése le van tiltva ebben a csoportban. + + Voice messages are prohibited. + A hangüzenetek küldése le van tiltva. + No comment provided by engineer. + + + Voice messages not allowed + A hangüzenetek küldése le van tiltva No comment provided by engineer. Voice messages prohibited! - A hangüzenetek le vannak tilva! + A hangüzenetek le vannak tiltva! No comment provided by engineer. @@ -5993,26 +8509,37 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Waiting for desktop... - Várakozás az asztali kliensre... + Várakozás a számítógép-alkalmazásra… No comment provided by engineer. Waiting for file - Fájlra várakozás + Várakozás a fájlra No comment provided by engineer. Waiting for image - Képre várakozás + Várakozás a képre No comment provided by engineer. Waiting for video - Videóra várakozás + Várakozás a videóra + No comment provided by engineer. + + + Wallpaper accent + Háttérkép kiemelőszíne + No comment provided by engineer. + + + Wallpaper background + Háttérkép háttérszíne No comment provided by engineer. Warning: starting chat on multiple devices is not supported and will cause message delivery failures + Figyelmeztetés: a csevegés elindítása egyszerre több eszközön nem támogatott, mert üzenetkézbesítési hibákat okoz No comment provided by engineer. @@ -6022,26 +8549,27 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola WebRTC ICE servers - WebRTC ICE kiszolgálók + WebRTC ICE-kiszolgálók No comment provided by engineer. Welcome %@! - Üdvözöllek %@! + Üdvözöljük %@! No comment provided by engineer. Welcome message - Üdvözlő üzenet + Üdvözlőüzenet No comment provided by engineer. Welcome message is too long + Az üdvözlőüzenet túl hosszú No comment provided by engineer. What's new - Milyen újdonságok vannak + Újdonságok No comment provided by engineer. @@ -6049,9 +8577,14 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Amikor elérhető No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Csatlakozási kérelmek esetében, elfogadhatja vagy elutasíthatja azokat. + + When connecting audio and video calls. + Amikor egy bejövő hang- vagy videóhívás érkezik. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. + Amikor egynél több üzemeltető van engedélyezve, akkor egyik sem rendelkezik olyan metaadatokkal, amelyekből megtudható, hogy ki kivel kommunikál. No comment provided by engineer. @@ -6059,115 +8592,166 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola Inkognitóprofil megosztása esetén a rendszer azt a profilt fogja használni azokhoz a csoportokhoz, amelyekbe meghívást kapott. No comment provided by engineer. + + WiFi + Wi-Fi + No comment provided by engineer. + + + Will be enabled in direct chats! + A közvetlen csevegésekben engedélyezve lesz! + No comment provided by engineer. + + + Wired ethernet + Vezetékes Ethernet + No comment provided by engineer. + With encrypted files and media. - Titkosított fájlokkal és médiatartalommal. + Titkosított fájlokkal és médiatartalmakkal. No comment provided by engineer. With optional welcome message. - Opcionális üdvözlő üzenettel. + Nem kötelező üdvözlőüzenettel. No comment provided by engineer. With reduced battery usage. - Csökkentett akkumulátorhasználattal. + Csökkentett akkumulátor-használattal. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + Tor vagy VPN nélkül az Ön IP-címe látható lesz a fájlkiszolgálók számára. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + Tor vagy VPN nélkül az Ön IP-címe látható lesz a következő XFTP-továbbítókiszolgálók számára: %@. + alert message + Wrong database passphrase - Téves adatbázis jelmondat + Érvénytelen adatbázis-jelmondat No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + Érvénytelen kulcs vagy ismeretlen kapcsolat – valószínűleg ez a kapcsolat törlődött. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + Érvénytelen kulcs vagy ismeretlen fájltöredékcím – valószínűleg a fájl törlődött. + file error text + Wrong passphrase! - Téves jelmondat! + Érvénytelen jelmondat! No comment provided by engineer. - - XFTP servers - XFTP kiszolgálók - No comment provided by engineer. - - - You - Ön + + XFTP server + XFTP-kiszolgáló No comment provided by engineer. You **must not** use the same database on two devices. + **Nem szabad** ugyanazt az adatbázist használni egyszerre két eszközön. No comment provided by engineer. You accepted connection - Kapcsolódás elfogadva + Kapcsolat létrehozása No comment provided by engineer. You allow - Engedélyezte + Ön engedélyezi No comment provided by engineer. You already have a chat profile with the same display name. Please choose another name. - Már van egy csevegési profil ugyanezzel a megjelenített névvel. Válasszon egy másik nevet. + Már van egy csevegési profil ugyanezzel a megjelenítendő névvel. Válasszon egy másik nevet. No comment provided by engineer. You are already connected to %@. - Már csatlakozva van ehhez: %@. + Ön már kapcsolódott a következőhöz: %@. + No comment provided by engineer. + + + You are already connected with %@. + Ön már kapcsolódva van vele: %@. No comment provided by engineer. You are already connecting to %@. - Már folyamatban van a csatlakozás ehhez: %@. + A kapcsolódás már folyamatban van a következőhöz: %@. No comment provided by engineer. You are already connecting via this one-time link! - Már csatlakozik ezen az egyszer használatos hivatkozáson keresztül! + A kapcsolódás már folyamatban van ezen az egyszer használható meghívón keresztül! No comment provided by engineer. You are already in group %@. - Már a %@ csoportban van. + Ön már a(z) %@ nevű csoport tagja. No comment provided by engineer. You are already joining the group %@. - Már folyamatban van a csatlakozás a csoporthoz %@. + A csatlakozás már folyamatban van a(z) %@ nevű csoporthoz. No comment provided by engineer. You are already joining the group via this link! - Már csatlakozott a csoporthoz ezen a hivatkozáson keresztül! + A csatlakozás már folyamatban van a csoporthoz ezen a hivatkozáson keresztül! No comment provided by engineer. You are already joining the group via this link. - Ezen a hivatkozáson keresztül már csatlakozik a csoporthoz. + A csatlakozás már folyamatban van a csoporthoz ezen a hivatkozáson keresztül. No comment provided by engineer. You are already joining the group! Repeat join request? - Csatlakozás folyamatban! -Csatlakozási kérés megismétlése? + A csatlakozás már folyamatban van a csoporthoz! +Megismétli a meghívási kérést? No comment provided by engineer. You are connected to the server used to receive messages from this contact. - Kiszolgálóhoz történő csatlakozás, mely az adott ismerőstől érkező üzenetek fogadására szolgál. + Ön már kapcsolódott ahhoz a kiszolgálóhoz, amely az adott partnerétől érkező üzenetek fogadására szolgál. No comment provided by engineer. You are invited to group - Meghívást kapott a csoportba + Ön meghívást kapott a csoportba + No comment provided by engineer. + + + You are not connected to these servers. Private routing is used to deliver messages to them. + Ön nem kapcsolódik ezekhez a kiszolgálókhoz. A privát útválasztás az üzenetek kézbesítésére szolgál. No comment provided by engineer. You can accept calls from lock screen, without device and app authentication. - Hívásokat fogadhat a lezárási képernyőről, eszköz- és alkalmazáshitelesítés nélkül. + Hívásokat fogadhat a lezárási képernyőről, eszköz- és alkalmazás-hitelesítés nélkül. + No comment provided by engineer. + + + You can change it in Appearance settings. + Ezt a „Megjelenés” menüben módosíthatja. + No comment provided by engineer. + + + You can configure servers via settings. + A kiszolgálókat a „Hálózat és kiszolgálók” menüben konfigurálhatja. No comment provided by engineer. @@ -6177,61 +8761,72 @@ Csatlakozási kérés megismétlése? You can enable later via Settings - Később engedélyezheti a Beállításokban + Később engedélyezheti a „Beállításokban” No comment provided by engineer. You can enable them later via app Privacy & Security settings. - Később engedélyezheti őket az alkalmazás Adatvédelem és biztonság menüpontban. + Később engedélyezheti őket az „Adatvédelem és biztonság” menüben. No comment provided by engineer. You can give another try. + Megpróbálhatja még egyszer. No comment provided by engineer. You can hide or mute a user profile - swipe it to the right. - Elrejthet vagy némíthat egy felhasználói profilt – csúsztasson jobbra. + Elrejtheti vagy lenémíthatja a felhasználó -profiljait – csúsztassa jobbra a profilt. No comment provided by engineer. You can make it visible to your SimpleX contacts via Settings. - Láthatóvá teheti SimpleX ismerősök számára a Beállításokban. + Láthatóvá teheti a SimpleXbeli partnerei számára a „Beállításokban”. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ Mostantól küldhet üzeneteket %@ számára notification body + + You can send messages to %@ from Archived contacts. + Az „Archivált partnerekből” továbbra is küldhet üzeneteket neki: %@. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + Beállíthatja a partner nevét, hogy emlékezzen arra, hogy kivel osztotta meg a hivatkozást. + No comment provided by engineer. + You can set lock screen notification preview via settings. - A beállításokon keresztül beállíthatja a lezárási képernyő értesítési előnézetét. + A lezárási képernyő értesítési előnézetét az „Értesítések” menüben állíthatja be. No comment provided by engineer. You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it. - Megoszthat egy hivatkozást vagy QR-kódot - így bárki csatlakozhat a csoporthoz. Ha a csoport később törlésre kerül, akkor nem fogja elveszíteni annak tagjait. + Megoszthat egy hivatkozást vagy QR-kódot – így bárki csatlakozhat a csoporthoz. Ha a csoportot Ön később törli, akkor nem fogja elveszíteni annak tagjait. No comment provided by engineer. You can share this address with your contacts to let them connect with **%@**. - Megoszthatja ezt a hivatkozást ismerőseivel, hogy kapcsolatba léphessenek önnel a **%@** nevű profilján keresztül. - No comment provided by engineer. - - - You can share your address as a link or QR code - anybody can connect to you. - Megoszthatja azonosítóját hivatkozásként vagy QR-kódként – így bárki csatlakozhat önhöz. + Megoszthatja ezt a SimpleX-címet a partnereivel, hogy kapcsolatba léphessenek vele: **%@**. No comment provided by engineer. You can start chat via app Settings / Database or by restarting the app - A csevegést az alkalmazás Beállítások / Adatbázis menü segítségével vagy az alkalmazás újraindításával indíthatja el + A csevegést az alkalmazás „Beállítások / Adatbázis” menüben vagy az alkalmazás újraindításával indíthatja el + No comment provided by engineer. + + + You can still view conversation with %@ in the list of chats. + A(z) %@ nevű partnerével folytatott beszélgetéseit továbbra is megtekintheti a csevegések listájában. No comment provided by engineer. You can turn on SimpleX Lock via Settings. - A SimpleX zárolás a Beállításokon keresztül kapcsolható be. + A SimpleX-zár az „Adatvédelem és biztonság” menüben kapcsolható be. No comment provided by engineer. @@ -6241,109 +8836,124 @@ Csatlakozási kérés megismétlése? You can view invitation link again in connection details. - A meghívó hivatkozást újra megtekintheti a kapcsolat részleteinél. - No comment provided by engineer. + A meghívási hivatkozást újra megtekintheti a kapcsolat részleteinél. + alert message You can't send messages! Nem lehet üzeneteket küldeni! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Ön szabályozhatja, hogy mely kiszogál(ók)ón keresztül **kapja** az üzeneteket, az ismerősöket - az üzenetküldéshez használt szervereken. - No comment provided by engineer. - You could not be verified; please try again. - Nem lehetett ellenőrizni; próbálja meg újra. + Nem sikerült hitelesíteni; próbálja meg újra. + No comment provided by engineer. + + + You decide who can connect. + Ön dönti el, hogy kivel beszélget. No comment provided by engineer. You have already requested connection via this address! - Már kért egy csatlakozást ezen az azonosítón keresztül! + Már küldött egy meghívási kérést ezen a címen keresztül! No comment provided by engineer. You have already requested connection! Repeat connection request? - Már kérelmezte a csatlakozást! -Kapcsolódási kérés megismétlése? - No comment provided by engineer. - - - You have no chats - Nincsenek csevegési üzenetek + Ön már küldött egy meghívási kérést! +Megismétli a meghívási kérést? No comment provided by engineer. You have to enter passphrase every time the app starts - it is not stored on the device. - A jelmondatot minden alkalommal meg kell adnia, amikor az alkalmazás elindul - nem az eszközön kerül tárolásra. + A jelmondatot minden alkalommal meg kell adnia, amikor az alkalmazás elindul – nem az eszközön van tárolva. No comment provided by engineer. You invited a contact - Meghívott egy ismerőst + Ön meghívta egy partnerét No comment provided by engineer. You joined this group - Csatlakozott ehhez a csoporthoz + Ön csatlakozott ehhez a csoporthoz No comment provided by engineer. You joined this group. Connecting to inviting group member. - Csatlakozott ehhez a csoporthoz. Kapcsolódás a meghívó csoporttaghoz. + Ön csatlakozott ehhez a csoporthoz. Kapcsolódás a meghívó csoporttaghoz. + No comment provided by engineer. + + + You may migrate the exported database. + Az exportált adatbázist átköltöztetheti. + No comment provided by engineer. + + + You may save the exported archive. + Az exportált archívumot elmentheti. No comment provided by engineer. You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. - A csevegési adatbázis legfrissebb verzióját CSAK egy eszközön kell használnia, ellenkező esetben előfordulhat, hogy az üzeneteket nem fogja megkapni valamennyi ismerőstől. + A csevegési adatbázis legfrissebb verzióját CSAK egy eszközön kell használnia, ellenkező esetben előfordulhat, hogy az üzeneteket nem fogja megkapni valamennyi partnerétől. + No comment provided by engineer. + + + You need to allow your contact to call to be able to call them. + Engedélyeznie kell a hívásokat a partnere számára, hogy fel tudják hívni egymást. No comment provided by engineer. You need to allow your contact to send voice messages to be able to send them. - Hangüzeneteket küldéséhez engedélyeznie kell azok küldését az ismerősök számára. + Engedélyeznie kell a hangüzenetek küldését a partnere számára, hogy hangüzeneteket küldhessenek egymásnak. No comment provided by engineer. You rejected group invitation - Csoport meghívó elutasítva + Csoportmeghívó elutasítva No comment provided by engineer. You sent group invitation - Csoport meghívó elküldve + Csoportmeghívó elküldve No comment provided by engineer. + + You should receive notifications. + Ön megkapja az értesítéseket. + token info + You will be connected to group when the group host's device is online, please wait or check later! - Akkor tud csatlakozni a csoporthoz, amikor a csoport tulajdonosának eszköze online lesz, várjon, vagy ellenőrizze később! + Akkor lesz kapcsolódva a csoporthoz, amikor a csoport tulajdonosának eszköze online lesz, várjon, vagy ellenőrizze később! No comment provided by engineer. You will be connected when group link host's device is online, please wait or check later! - Akkor lesz csatlakoztatva, amikor a csoportos hivatkozás tulajdonosának eszköze online lesz, várjon, vagy ellenőrizze később! + Akkor lesz kapcsolódva, amikor a csoporthivatkozás tulajdonosának eszköze online lesz, várjon, vagy ellenőrizze később! No comment provided by engineer. You will be connected when your connection request is accepted, please wait or check later! - Akkor lesz csatlakoztatva, ha a csatlakozási kérelme elfogadásra került, várjon, vagy ellenőrizze később! + Akkor lesz kapcsolódva, ha a meghívási kérése el lesz fogadva, várjon, vagy ellenőrizze később! No comment provided by engineer. You will be connected when your contact's device is online, please wait or check later! - Akkor csatlakozik, amikor az ismerős eszköze online lesz, várjon, vagy ellenőrizze később! + Akkor lesz kapcsolódva, amikor a partnerének az eszköze online lesz, várjon, vagy ellenőrizze később! No comment provided by engineer. You will be required to authenticate when you start or resume the app after 30 seconds in background. - Az alkalmazás indításakor, vagy 30 másodpercnyi háttérben töltött idő után az alkalmazáshoz visszatérve hitelesítés szükséges. + Az alkalmazás elindításához vagy 30 másodpercnyi háttérben töltött idő után, az alkalmazáshoz való visszatéréshez hitelesítésre lesz szükség. No comment provided by engineer. You will connect to all group members. - Csatlakozni fog a csoport összes tagjához. + Kapcsolódni fog a csoport összes tagjához. No comment provided by engineer. @@ -6351,6 +8961,11 @@ Kapcsolódási kérés megismétlése? Továbbra is kap hívásokat és értesítéseket a némított profiloktól, ha azok aktívak. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + Ön nem fog több üzenetet kapni ebből a csevegésből, de a csevegés előzményei megmaradnak. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Ettől a csoporttól nem fog értesítéseket kapni. A csevegési előzmények megmaradnak. @@ -6358,42 +8973,27 @@ Kapcsolódási kérés megismétlése? You won't lose your contacts if you later delete your address. - Nem veszíti el ismerőseit, ha később törli az azonosítóját. + Nem veszíti el a partnereit, ha később törli a címét. No comment provided by engineer. You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile - Egy olyan ismerőst próbál meghívni, akivel inkognító profilt osztott meg abban a csoportban, amelyben saját fő profilja van használatban + Egy olyan partnerét próbálja meghívni, akivel inkognitóprofilt osztott meg abban a csoportban, amelyben a fő profilja van használatban No comment provided by engineer. You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed - Inkognító profilt használ ehhez a csoporthoz - fő profilja megosztásának elkerülése érdekében meghívók küldése tiltott - No comment provided by engineer. - - - Your %@ servers - %@ nevű profiljához tartozó kiszolgálók + Inkognitóprofilt használ ehhez a csoporthoz – fő profilja megosztásának elkerülése érdekében a meghívók küldése le van tiltva No comment provided by engineer. Your ICE servers - ICE kiszolgálók - No comment provided by engineer. - - - Your SMP servers - SMP kiszolgálók + Saját ICE-kiszolgálók No comment provided by engineer. Your SimpleX address - SimpleX azonosítója - No comment provided by engineer. - - - Your XFTP servers - XFTP kiszolgálók + Profil SimpleX-címe No comment provided by engineer. @@ -6403,44 +9003,52 @@ Kapcsolódási kérés megismétlése? Your chat database - Csevegési adatbázisa + Csevegési adatbázis No comment provided by engineer. Your chat database is not encrypted - set passphrase to encrypt it. - Csevegési adatbázisa nincs titkosítva – adjon meg egy jelmondatot a titkosításhoz. + A csevegési adatbázis nincs titkosítva – adjon meg egy jelmondatot a titkosításhoz. No comment provided by engineer. + + Your chat preferences + Az Ön csevegési beállításai + alert title + Your chat profiles - Csevegési profiljai + Csevegési profilok No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Az ismerősnek online kell lennie ahhoz, hogy a kapcsolat létrejöjjön. -Megszakíthatja ezt a kapcsolatfelvételt és törölheti az ismerőst (ezt később ismét megpróbálhatja egy új hivatkozással). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + A kapcsolata át lett helyezve ide: %@, de egy váratlan hiba történt a profilra való átirányításkor. No comment provided by engineer. Your contact sent a file that is larger than currently supported maximum size (%@). - Ismerőse olyan fájlt küldött, amely meghaladja a jelenleg támogatott maximális méretet (%@). + A partnere a jelenleg megengedett maximális méretű (%@) fájlnál nagyobbat küldött. No comment provided by engineer. Your contacts can allow full message deletion. - Ismerősök engedélyezhetik a teljes üzenet törlést. + A partnerei engedélyezhetik a teljes üzenet törlését. No comment provided by engineer. Your contacts will remain connected. - Az ismerősök továbbra is csatlakoztatva maradnak. + A partnerei továbbra is kapcsolódva maradnak. + No comment provided by engineer. + + + Your credentials may be sent unencrypted. + A hitelesítőadatai titkosítatlanul is elküldhetők. No comment provided by engineer. Your current chat database will be DELETED and REPLACED with the imported one. - A jelenlegi csevegési adatbázis TÖRLŐDNI FOG, és a HELYÉRE az importált adatbázis kerül. + A jelenlegi csevegési adatbázis TÖRÖLVE és CSERÉLVE lesz az importáltra. No comment provided by engineer. @@ -6465,34 +9073,37 @@ Megszakíthatja ezt a kapcsolatfelvételt és törölheti az ismerőst (ezt kés Your profile **%@** will be shared. - **%@** nevű profilja megosztásra kerül. + A(z) **%@** nevű profilja meg lesz osztva. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Profilja az eszközön van tárolva, és csak az ismerősökkel kerül megosztásra. -A SimpleX kiszolgálók nem látjhatják profilját. + + Your profile is stored on your device and only shared with your contacts. + A profilja csak a partnereivel van megosztva. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Profilja, ismerősök és az elküldött üzenetek az eszközön kerülnek tárolásra. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + A profilja az eszközén van tárolva és csak a partnereivel van megosztva. A SimpleX-kiszolgálók nem láthatják a profilját. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + A profilja módosult. Ha elmenti, a profilfrissítés el lesz küldve a partnerei számára. + alert message + Your random profile Véletlenszerű profil No comment provided by engineer. - - Your server - Saját kiszolgáló - No comment provided by engineer. - Your server address - Saját kiszolgáló cím + Saját SMP-kiszolgálójának címe + No comment provided by engineer. + + + Your servers + Saját kiszolgálók No comment provided by engineer. @@ -6502,7 +9113,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. [Contribute](https://github.com/simplex-chat/simplex-chat#contribute) - [Hozzájárulás](https://github.com/simplex-chat/simplex-chat#contribute) + [Közreműködés](https://github.com/simplex-chat/simplex-chat#contribute) No comment provided by engineer. @@ -6512,7 +9123,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. [Star on GitHub](https://github.com/simplex-chat/simplex-chat) - [Csillag a GitHubon](https://github.com/simplex-chat/simplex-chat) + [Csillagozás a GitHubon](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. @@ -6527,22 +9138,32 @@ A SimpleX kiszolgálók nem látjhatják profilját. above, then choose: - fent, majd válassza ki: + gombra fent, majd válassza ki: No comment provided by engineer. accepted call - elfogadott hívás + fogadott hívás call status + + accepted invitation + elfogadott meghívó + chat list item title + admin - admin + adminisztrátor member role + + admins + adminisztrátorok + feature role + agreeing encryption for %@… - titkosítás jóváhagyása %@ számára… + titkosítás elfogadása %@ számára… chat item text @@ -6550,6 +9171,11 @@ A SimpleX kiszolgálók nem látjhatják profilját. titkosítás elfogadása… chat item text + + all members + összes tag + feature role + always mindig @@ -6557,7 +9183,17 @@ A SimpleX kiszolgálók nem látjhatják profilját. and %lld other events - és %lld egyéb esemény + és további %lld esemény + No comment provided by engineer. + + + archived report + archivált jelentés + No comment provided by engineer. + + + attempts + próbálkozások No comment provided by engineer. @@ -6577,32 +9213,38 @@ A SimpleX kiszolgálók nem látjhatják profilját. bad message hash - téves üzenet hash + érvénytelen az üzenet hasítóértéke integrity error chat item blocked - blokkolva + letiltva marked deleted chat item preview text blocked %@ - %@ letiltva + letiltotta őt: %@ rcv group event chat item blocked by admin - letiltva az admin által - marked deleted chat item preview text + letiltva az adminisztrátor által + blocked chat item +marked deleted chat item preview text bold félkövér No comment provided by engineer. + + call + hívás + No comment provided by engineer. + call error - hiba a hívásban + híváshiba call status @@ -6617,22 +9259,22 @@ A SimpleX kiszolgálók nem látjhatják profilját. cancelled %@ - %@ törölve + %@ visszavonva feature offered item changed address for you - Cím megváltoztatva + módosította a címet az Ön számára chat item text changed role of %1$@ to %2$@ - %1$@ szerepköre megváltozott erre: %2$@ + a következőre módosította %1$@ szerepkörét: „%2$@” rcv group event chat item changed your role to %@ - megváltoztatta a szerepkörét erre: %@ + a következőre módosította az Ön szerepkörét: „%@” rcv group event chat item @@ -6642,12 +9284,12 @@ A SimpleX kiszolgálók nem látjhatják profilját. changing address… - azonosító megváltoztatása… + cím módosítása… chat item text colored - színes + színezett No comment provided by engineer. @@ -6657,17 +9299,17 @@ A SimpleX kiszolgálók nem látjhatják profilját. connect to SimpleX Chat developers. - Csatlakozás a SimpleX Chat fejlesztőkhöz. + kapcsolódás a SimpleX Chat fejlesztőkhöz. No comment provided by engineer. connected - kapcsolódva + kapcsolódott No comment provided by engineer. connected directly - közvetlenül kapcsolódva + közvetlenül kapcsolódott rcv group event chat item @@ -6687,27 +9329,27 @@ A SimpleX kiszolgálók nem látjhatják profilját. connecting (introduced) - kapcsolódás (bejelentve) + kapcsolódás (bemutatkozva) No comment provided by engineer. connecting (introduction invitation) - csatlakozás (bemutatkozás meghívás) + kapcsolódás (bemutatkozó meghívó) No comment provided by engineer. connecting call… - hívás kapcsolódik… + kapcsolódási hívás… call status connecting… kapcsolódás… - chat list item title + No comment provided by engineer. connection established - Kapcsolat létrehozva + kapcsolat létrehozva chat list item title (it should not be shown @@ -6717,32 +9359,32 @@ A SimpleX kiszolgálók nem látjhatják profilját. contact %1$@ changed to %2$@ - %1$@ ismerősének neve megváltozott erre: %2$@ + %1$@ a következőre módosította a nevét: %2$@ profile update event chat item contact has e2e encryption - az ismerősnél az e2e titkosítás elérhető + a partner e2e titkosítással rendelkezik No comment provided by engineer. contact has no e2e encryption - az ismerősnél az e2e titkosítás nem elérhető + a partner nem rendelkezik e2e titkosítással No comment provided by engineer. creator - szerző + készítő No comment provided by engineer. custom - egyedi + egyéni dropdown time picker choice database version is newer than the app, but no down migration for: %@ - az adatbázis verziója újabb, mint az alkalmazásé, de nincs visszafelé migráció: %@ + az adatbázis verziója újabb, mint az alkalmazásé, de a visszafelé történő átköltöztetés viszont nem lehetséges a következőhöz: %@ No comment provided by engineer. @@ -6750,10 +9392,16 @@ A SimpleX kiszolgálók nem látjhatják profilját. nap time unit + + decryption errors + visszafejtési hibák + No comment provided by engineer. + default (%@) alapértelmezett (%@) - pref value + delete after time +pref value default (no) @@ -6772,7 +9420,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. deleted contact - törölt ismerős + törölt partner rcv direct event chat item @@ -6782,7 +9430,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. different migration in the app/database: %@ / %@ - különböző migrációk az alkalmazásban/adatbázisban: %@ / %@ + különböző átköltöztetés az alkalmazásban/adatbázisban: %@ / %@ No comment provided by engineer. @@ -6797,9 +9445,14 @@ A SimpleX kiszolgálók nem látjhatják profilját. duplicate message - duplikálódott üzenet + duplikált üzenet integrity error chat item + + duplicates + duplikációk + No comment provided by engineer. + e2e encrypted e2e titkosított @@ -6812,17 +9465,17 @@ A SimpleX kiszolgálók nem látjhatják profilját. enabled for contact - engedélyezve ismerős részére + engedélyezve a partner számára enabled status enabled for you - engedélyezve az ön számára + engedélyezve az Ön számára enabled status encryption agreed - titkosítás egyeztetve + titkosítás elfogadva chat item text @@ -6837,27 +9490,27 @@ A SimpleX kiszolgálók nem látjhatják profilját. encryption ok for %@ - titkosítás rendben vele: %@ + titkosítás rendben %@ számára chat item text encryption re-negotiation allowed - titkosítás újraegyeztetés engedélyezve + a titkosítás újraegyeztetése engedélyezve van chat item text encryption re-negotiation allowed for %@ - titkosítás újraegyeztetés engedélyezve vele: %@ + a titkosítás újraegyeztetése engedélyezve van %@ számára chat item text encryption re-negotiation required - titkosítás újraegyeztetés szükséges + a titkosítás újraegyeztetése szükséges chat item text encryption re-negotiation required for %@ - titkosítás újraegyeztetés szükséges %@ számára + a titkosítás újraegyeztetése szükséges %@ számára chat item text @@ -6875,9 +9528,14 @@ A SimpleX kiszolgálók nem látjhatják profilját. hiba No comment provided by engineer. - - event happened - esemény történt + + expired + lejárt + No comment provided by engineer. + + + forwarded + továbbított No comment provided by engineer. @@ -6887,7 +9545,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. group profile updated - csoport profil frissítve + csoportprofil frissítve snd group event chat item @@ -6897,27 +9555,32 @@ A SimpleX kiszolgálók nem látjhatják profilját. iOS Keychain is used to securely store passphrase - it allows receiving push notifications. - Az iOS kulcstár a jelmondat biztonságos tárolására szolgál - lehetővé teszi a push-értesítések fogadását. + Az iOS kulcstartó a jelmondat biztonságos tárolására szolgál – lehetővé teszi a push-értesítések fogadását. No comment provided by engineer. iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications. - Az iOS kulcstár az alkalmazás újraindítása, vagy a jelmondat módosítása után a jelmondat biztonságos tárolására szolgál - lehetővé teszi a push-értesítések fogadását. + Az iOS kulcstartó biztonságosan fogja tárolni a jelmondatot az alkalmazás újraindítása, vagy a jelmondat módosítása után – lehetővé teszi a push-értesítések fogadását. + No comment provided by engineer. + + + inactive + inaktív No comment provided by engineer. incognito via contact address link - inkognitó a kapcsolattartási hivatkozáson keresztül + inkognitó a kapcsolattartási címhivatkozáson keresztül chat list item description incognito via group link - inkognitó a csoportos hivatkozáson keresztül + inkognitó a csoporthivatkozáson keresztül chat list item description incognito via one-time link - inkognitó egyszer használatos hivatkozáson keresztül + inkognitó egy egyszer használható meghívón keresztül chat list item description @@ -6932,7 +9595,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. invalid chat data - érvénytelen csevegés adat + érvénytelen csevegésadat No comment provided by engineer. @@ -6945,24 +9608,29 @@ A SimpleX kiszolgálók nem látjhatják profilját. meghívás a(z) %@ csoportba group name + + invite + meghívás + No comment provided by engineer. + invited - meghívott + meghíva No comment provided by engineer. invited %@ - %@ meghívott + meghívta őt: %@ rcv group event chat item invited to connect - meghívott, hogy csatlakozzon + Függőben lévő meghívó chat list item title invited via your group link - meghívott a csoport hivatkozásán keresztül + meghíva a saját csoporthivatkozásán keresztül rcv group event chat item @@ -6977,12 +9645,12 @@ A SimpleX kiszolgálók nem látjhatják profilját. left - elhagyta + elhagyta a csoportot rcv group event chat item marked deleted - töröltnek jelölve + törlésre jelölve marked deleted chat item preview text @@ -6992,14 +9660,19 @@ A SimpleX kiszolgálók nem látjhatják profilját. member %1$@ changed to %2$@ - %1$@ tag megváltoztatta a nevét erre: %2$@ + %1$@ a következőre módosította a nevét: %2$@ profile update event chat item connected - kapcsolódva + kapcsolódott rcv group event chat item + + message + üzenet + No comment provided by engineer. + message received üzenet érkezett @@ -7022,9 +9695,14 @@ A SimpleX kiszolgálók nem látjhatják profilját. moderated by %@ - %@ által moderálva + moderálva lett %@ által marked deleted chat item preview text + + moderator + moderátor + member role + months hónap @@ -7033,7 +9711,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. never soha - No comment provided by engineer. + delete after time new message @@ -7062,10 +9740,10 @@ A SimpleX kiszolgálók nem látjhatják profilját. off - ki + kikapcsolva enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7074,31 +9752,57 @@ A SimpleX kiszolgálók nem látjhatják profilját. offered %1$@: %2$@ - ajánlotta %1$@: %2$@-kor + ajánlotta: %1$@, ekkor: %2$@ feature offered item on - be + bekapcsolva group pref value + + other + egyéb + No comment provided by engineer. + + + other errors + egyéb hibák + No comment provided by engineer. + owner tulajdonos member role + + owners + tulajdonosok + feature role + peer-to-peer - ponttól-pontig + egyenrangú + No comment provided by engineer. + + + pending + függőben + No comment provided by engineer. + + + pending approval + jóváhagyásra vár No comment provided by engineer. quantum resistant e2e encryption + végpontok közötti kvantumbiztos titkosítás chat item text received answer… - fogadott válasz… + válasz fogadása… No comment provided by engineer. @@ -7106,6 +9810,11 @@ A SimpleX kiszolgálók nem látjhatják profilját. visszaigazolás fogadása… No comment provided by engineer. + + rejected + elutasítva + No comment provided by engineer. + rejected call elutasított hívás @@ -7118,24 +9827,44 @@ A SimpleX kiszolgálók nem látjhatják profilját. removed %@ - %@ eltávolítva + eltávolította őt: %@ rcv group event chat item removed contact address - törölt csatlakozási cím + eltávolította a kapcsolattartási címet profile update event chat item removed profile picture - törölt profilkép + eltávolította a profilképét profile update event chat item removed you - eltávolítottak + eltávolította Önt rcv group event chat item + + requested to connect + Függőben lévő meghívási kérelem + chat list item title + + + saved + mentett + No comment provided by engineer. + + + saved from %@ + elmentve innen: %@ + No comment provided by engineer. + + + search + keresés + No comment provided by engineer. + sec mp @@ -7153,7 +9882,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. security code changed - biztonsági kód megváltozott + a biztonsági kód módosult chat item text @@ -7161,18 +9890,28 @@ A SimpleX kiszolgálók nem látjhatják profilját. közvetlen üzenet küldése No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + a kiszolgáló sorbaállítási információi: %1$@ + +utoljára fogadott üzenet: %2$@ + queue info + set new contact address - új kapcsolattartási cím beállítása + új kapcsolattartási címet állított be profile update event chat item set new profile picture - új profilkép beállítása + új profilképet állított be profile update event chat item standard end-to-end encryption + szabványos végpontok közötti titkosítás chat item text @@ -7187,12 +9926,12 @@ A SimpleX kiszolgálók nem látjhatják profilját. this contact - ez az ismerős + ez a partner notification title unblocked %@ - %@ feloldva + feloldotta %@ letiltását rcv group event chat item @@ -7200,14 +9939,24 @@ A SimpleX kiszolgálók nem látjhatják profilját. ismeretlen connection info + + unknown servers + ismeretlen átjátszók + No comment provided by engineer. + unknown status - ismeretlen státusz + ismeretlen állapot + No comment provided by engineer. + + + unprotected + nem védett No comment provided by engineer. updated group profile - módosított csoport profil + frissítette a csoport profilját rcv group event chat item @@ -7227,22 +9976,27 @@ A SimpleX kiszolgálók nem látjhatják profilját. via contact address link - ismerős azonosítójának hivatkozásán keresztül + a kapcsolattartási címhivatkozáson keresztül chat list item description via group link - csoport hivatkozáson keresztül + a csoporthivatkozáson keresztül chat list item description via one-time link - egyszer használatos hivatkozáson keresztül + egy egyszer használható meghívón keresztül chat list item description via relay - átjátszón keresztül + egy továbbítókiszolgálón keresztül + No comment provided by engineer. + + + video + videó No comment provided by engineer. @@ -7252,7 +10006,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. waiting for answer… - várakozás válaszra… + várakozás a válaszra… No comment provided by engineer. @@ -7262,7 +10016,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. wants to connect to you! - kapcsolatba akar lépni önnel! + kapcsolatba akar lépni Önnel! No comment provided by engineer. @@ -7270,74 +10024,84 @@ A SimpleX kiszolgálók nem látjhatják profilját. hét time unit + + when IP hidden + ha az IP-cím rejtett + No comment provided by engineer. + yes igen pref value + + you + Ön + No comment provided by engineer. + you are invited to group - meghívást kapott a csoportba + Ön meghívást kapott a csoportba No comment provided by engineer. you are observer - megfigyelő szerep + Ön megfigyelő No comment provided by engineer. you blocked %@ - blokkolta őt: %@ + Ön letiltotta őt: %@ snd group event chat item you changed address - azonosítója megváltoztatva + Ön módosította a címet chat item text you changed address for %@ - %@ azonosítója megváltoztatva + Ön módosította a címet %@ számára chat item text you changed role for yourself to %@ - saját szerepkör megváltoztatva erre: %@ + Ön a következőre módosította a saját szerepkörét: „%@” snd group event chat item you changed role of %1$@ to %2$@ - megváltoztatta %1$@ szerepkörét erre: %@ + Ön a következőre módosította %1$@ szerepkörét: „%2$@” snd group event chat item you left - elhagyta + Ön elhagyta a csoportot snd group event chat item you removed %@ - eltávolította őt: %@ + Ön eltávolította őt: %@ snd group event chat item you shared one-time link - egyszer használatos hivatkozást osztott meg + Ön egy egyszer használható meghívót osztott meg chat list item description you shared one-time link incognito - egyszer használatos hivatkozást osztott meg inkognitóban + Ön egy egyszer használható meghívót osztott meg inkognitóban chat list item description you unblocked %@ - feloldotta %@ blokkolását + Ön feloldotta %@ letiltását snd group event chat item you: - ön: + Ön: No comment provided by engineer. @@ -7349,7 +10113,7 @@ A SimpleX kiszolgálók nem látjhatják profilját.
- +
@@ -7359,7 +10123,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. SimpleX needs camera access to scan QR codes to connect to other users and for video calls. - A SimpleX-nek kamera-hozzáférésre van szüksége a QR-kódok beolvasásához, hogy csatlakozhasson más felhasználókhoz és videohívásokhoz. + A SimpleXnek kamera-hozzáférésre van szüksége a QR-kódok beolvasásához, hogy kapcsolódhasson más felhasználókhoz és videohívásokhoz. Privacy - Camera Usage Description @@ -7369,24 +10133,24 @@ A SimpleX kiszolgálók nem látjhatják profilját. SimpleX uses local network access to allow using user chat profile via desktop app on the same network. - A SimpleX helyi hálózati hozzáférést használ, hogy lehetővé tegye a felhasználói csevegőprofil használatát számítógépen keresztül ugyanazon a hálózaton. + A SimpleX helyi hálózati hozzáférést használ, hogy lehetővé tegye a felhasználói csevegési profil használatát számítógépen keresztül ugyanazon a hálózaton. Privacy - Local Network Usage Description SimpleX needs microphone access for audio and video calls, and to record voice messages. - A SimpleX-nek mikrofon-hozzáférésre van szüksége hang- és videohívásokhoz, valamint hangüzenetek rögzítéséhez. + A SimpleXnek mikrofon-hozzáférésre van szüksége hang- és videohívásokhoz, valamint hangüzenetek rögzítéséhez. Privacy - Microphone Usage Description SimpleX needs access to Photo Library for saving captured and received media - A SimpleX-nek hozzáférésre van szüksége a Galériához a rögzített és fogadott média mentéséhez + A SimpleXnek galéria-hozzáférésre van szüksége a rögzített és fogadott média mentéséhez Privacy - Photo Library Additions Usage Description
- +
@@ -7406,4 +10170,250 @@ A SimpleX kiszolgálók nem látjhatják profilját.
+ +
+ +
+ + + %d new events + %d új esemény + notification body + + + From %d chat(s) + %d csevegésből + notification body + + + From: %@ + Tőle: %@ + notification body + + + New events + Új események + notification + + + New messages + Új üzenetek + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright © 2024 SimpleX Chat. Minden jog fenntartva. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + Az alkalmazás zárolva van! + No comment provided by engineer. + + + Cancel + Mégse + No comment provided by engineer. + + + Cannot access keychain to save database password + Nem lehet hozzáférni a kulcstartóhoz az adatbázis jelszavának mentéséhez + No comment provided by engineer. + + + Cannot forward message + Nem lehet továbbítani az üzenetet + No comment provided by engineer. + + + Comment + Hozzászólás + No comment provided by engineer. + + + Currently maximum supported file size is %@. + Jelenleg támogatott legnagyobb fájl méret: %@. + No comment provided by engineer. + + + Database downgrade required + Adatbázis visszafejlesztése szükséges + No comment provided by engineer. + + + Database encrypted! + Adatbázis titkosítva! + No comment provided by engineer. + + + Database error + Adatbázishiba + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + Az adatbázis jelmondata nem egyezik a kulcstartóba mentettől. + No comment provided by engineer. + + + Database passphrase is required to open chat. + A csevegés megnyitásához adja meg az adatbázis jelmondatát. + No comment provided by engineer. + + + Database upgrade required + Adatbázis fejlesztése szükséges + No comment provided by engineer. + + + Error preparing file + Hiba történt a fájl előkészítésekor + No comment provided by engineer. + + + Error preparing message + Hiba történt az üzenet előkészítésekor + No comment provided by engineer. + + + Error: %@ + Hiba: %@ + No comment provided by engineer. + + + File error + Fájlhiba + No comment provided by engineer. + + + Incompatible database version + Nem kompatibilis adatbázis-verzió + No comment provided by engineer. + + + Invalid migration confirmation + Érvénytelen átköltöztetési visszaigazolás + No comment provided by engineer. + + + Keychain error + Kulcstartóhiba + No comment provided by engineer. + + + Large file! + Nagy fájl! + No comment provided by engineer. + + + No active profile + Nincs aktív profil + No comment provided by engineer. + + + Ok + Rendben + No comment provided by engineer. + + + Open the app to downgrade the database. + Nyissa meg az alkalmazást az adatbázis visszafejlesztéséhez. + No comment provided by engineer. + + + Open the app to upgrade the database. + Nyissa meg az alkalmazást az adatbázis fejlesztéséhez. + No comment provided by engineer. + + + Passphrase + Jelmondat + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Hozzon létre egy profilt a SimpleX alkalmazásban + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + A kijelölt csevegési beállítások tiltják ezt az üzenetet. + No comment provided by engineer. + + + Sending a message takes longer than expected. + Az üzenet elküldése a vártnál tovább tart. + No comment provided by engineer. + + + Sending message… + Üzenet küldése… + No comment provided by engineer. + + + Share + Megosztás + No comment provided by engineer. + + + Slow network? + Lassú a hálózata? + No comment provided by engineer. + + + Unknown database error: %@ + Ismeretlen adatbázishiba: %@ + No comment provided by engineer. + + + Unsupported format + Nem támogatott formátum + No comment provided by engineer. + + + Wait + Várjon + No comment provided by engineer. + + + Wrong database passphrase + Érvénytelen adatbázis-jelmondat + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + A megosztást az Adatvédelem és biztonság / SimpleX-zár menüben engedélyezheti. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/contents.json b/apps/ios/SimpleX Localizations/hu.xcloc/contents.json index bc788c3c10..c07ec0f900 100644 --- a/apps/ios/SimpleX Localizations/hu.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/hu.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "hu", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff index 30467415cf..cf5f61918f 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff +++ b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (può essere copiato) @@ -104,11 +77,12 @@ %@ connected - %@ si è connesso/a + %@ connesso/a No comment provided by engineer. %@ downloaded + %@ scaricati No comment provided by engineer. @@ -126,13 +100,19 @@ %@ è verificato/a No comment provided by engineer. + + %@ server + %@ server + No comment provided by engineer. + %@ servers - Server %@ + %@ server No comment provided by engineer. %@ uploaded + %@ caricati No comment provided by engineer. @@ -140,6 +120,11 @@ %@ si vuole connettere! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ e %lld membri @@ -160,11 +145,36 @@ %d giorni time interval + + %d file(s) are still being downloaded. + %d file è/sono ancora in scaricamento. + forward confirmation reason + + + %d file(s) failed to download. + %d file ha/hanno fallito lo scaricamento. + forward confirmation reason + + + %d file(s) were deleted. + %d file è/sono stato/i eliminato/i. + forward confirmation reason + + + %d file(s) were not downloaded. + %d file non è/sono stato/i scaricato/i. + forward confirmation reason + %d hours %d ore time interval + + %d messages not forwarded + %d messaggi non inoltrati + alert title + %d min %d min @@ -180,6 +190,11 @@ %d sec time interval + + %d seconds(s) + %d secondo/i + delete after time + %d skipped message(s) %d messaggio/i saltato/i @@ -250,11 +265,6 @@ %lld nuove lingue dell'interfaccia No comment provided by engineer. - - %lld second(s) - %lld secondo/i - No comment provided by engineer. - %lld seconds %lld secondi @@ -305,11 +315,6 @@ %u messaggi saltati. No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) (nuovo) @@ -320,19 +325,9 @@ (questo dispositivo v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - **Aggiungi contatto**: per creare un nuovo link di invito o connetterti tramite un link che hai ricevuto. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Aggiungi un contatto**: per creare il tuo codice QR o link una tantum per il tuo contatto. + + **Create 1-time link**: to create and share a new invitation link. + **Aggiungi contatto**: per creare un nuovo link di invito. No comment provided by engineer. @@ -340,18 +335,19 @@ **Crea gruppo**: per creare un nuovo gruppo. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **Più privato**: controlla messaggi nuovi ogni 20 minuti. Viene condiviso il token del dispositivo con il server di SimpleX Chat, ma non quanti contatti o messaggi hai. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **Il più privato**: non usare il server di notifica di SimpleX Chat, controlla i messaggi periodicamente in secondo piano (dipende da quanto spesso usi l'app). No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Nota bene**: usare lo stesso database su due dispositivi bloccherà la decifrazione dei messaggi dalle tue connessioni, come misura di sicurezza. No comment provided by engineer. @@ -359,11 +355,16 @@ **Nota bene**: NON potrai recuperare o cambiare la password se la perdi. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Consigliato**: vengono inviati il token del dispositivo e le notifiche al server di notifica di SimpleX Chat, ma non il contenuto del messaggio,la sua dimensione o il suo mittente. No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + **Scansiona / Incolla link**: per connetterti tramite un link che hai ricevuto. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **Attenzione**: le notifiche push istantanee richiedono una password salvata nel portachiavi. @@ -371,6 +372,7 @@ **Warning**: the archive will be removed. + **Attenzione**: l'archivio verrà rimosso. No comment provided by engineer. @@ -388,11 +390,6 @@ \*grassetto* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -429,11 +426,6 @@ - cronologia delle modifiche. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 sec @@ -447,7 +439,8 @@ 1 day 1 giorno - time interval + delete after time +time interval 1 hour @@ -462,12 +455,29 @@ 1 month 1 mese - time interval + delete after time +time interval 1 week 1 settimana - time interval + delete after time +time interval + + + 1 year + 1 anno + delete after time + + + 1-time link + Link una tantum + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + Il link una tantum può essere usato *con un solo contatto* - condividilo di persona o tramite qualsiasi messenger. + No comment provided by engineer. 5 minutes @@ -484,11 +494,6 @@ 30 secondi No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -538,31 +543,32 @@ Interrompere il cambio di indirizzo? No comment provided by engineer. - - About SimpleX - Riguardo SimpleX - No comment provided by engineer. - About SimpleX Chat Riguardo SimpleX Chat No comment provided by engineer. - - About SimpleX address - Info sull'indirizzo SimpleX + + About operators + Info sugli operatori No comment provided by engineer. - - Accent color - Colore principale + + Accent + Principale No comment provided by engineer. Accept Accetta accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Accetta le condizioni + No comment provided by engineer. Accept connection request? @@ -577,21 +583,47 @@ Accept incognito Accetta in incognito - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + Condizioni accettate + No comment provided by engineer. + + + Acknowledged + Riconosciuto + No comment provided by engineer. + + + Acknowledgement errors + Errori di riconoscimento + No comment provided by engineer. + + + Active + Attivo + token status text + + + Active connections + Connessioni attive + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Aggiungi l'indirizzo al tuo profilo, in modo che i tuoi contatti possano condividerlo con altre persone. L'aggiornamento del profilo verrà inviato ai tuoi contatti. No comment provided by engineer. - - Add contact - Aggiungi contatto + + Add friends + Aggiungi amici No comment provided by engineer. - - Add preset servers - Aggiungi server preimpostati + + Add list + Aggiungi elenco No comment provided by engineer. @@ -599,14 +631,19 @@ Aggiungi profilo No comment provided by engineer. + + Add server + Aggiungi server + No comment provided by engineer. + Add servers by scanning QR codes. Aggiungi server scansionando codici QR. No comment provided by engineer. - - Add server… - Aggiungi server… + + Add team members + Aggiungi membri del team No comment provided by engineer. @@ -614,11 +651,46 @@ Aggiungi ad un altro dispositivo No comment provided by engineer. + + Add to list + Aggiungi ad un elenco + No comment provided by engineer. + Add welcome message Aggiungi messaggio di benvenuto No comment provided by engineer. + + Add your team members to the conversations. + Aggiungi i membri del tuo team alle conversazioni. + No comment provided by engineer. + + + Added media & file servers + Server di multimediali e file aggiunti + No comment provided by engineer. + + + Added message servers + Server dei messaggi aggiunti + No comment provided by engineer. + + + Additional accent + Principale aggiuntivo + No comment provided by engineer. + + + Additional accent 2 + Principale aggiuntivo 2 + No comment provided by engineer. + + + Additional secondary + Secondario aggiuntivo + No comment provided by engineer. + Address Indirizzo @@ -629,8 +701,19 @@ Il cambio di indirizzo verrà interrotto. Verrà usato il vecchio indirizzo di ricezione. No comment provided by engineer. + + Address or 1-time link? + Indirizzo o link una tantum? + No comment provided by engineer. + + + Address settings + Impostazioni dell'indirizzo + No comment provided by engineer. + Admins can block a member for all. + Gli amministratori possono bloccare un membro per tutti. No comment provided by engineer. @@ -643,6 +726,16 @@ Impostazioni di rete avanzate No comment provided by engineer. + + Advanced settings + Impostazioni avanzate + No comment provided by engineer. + + + All + Tutte + No comment provided by engineer. + All app data is deleted. Tutti i dati dell'app vengono eliminati. @@ -653,16 +746,31 @@ Tutte le chat e i messaggi verranno eliminati. Non è reversibile! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + Tutte le chat verranno rimosse dall'elenco %@ e l'elenco eliminato. + alert message + All data is erased when it is entered. Tutti i dati vengono cancellati quando inserito. No comment provided by engineer. + + All data is kept private on your device. + Tutti i dati sono privati, nel tuo dispositivo. + No comment provided by engineer. + All group members will remain connected. Tutti i membri del gruppo resteranno connessi. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + Tutti i messaggi e i file vengono inviati **crittografati end-to-end**, con sicurezza resistenti alla quantistica nei messaggi diretti. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! Tutti i messaggi verranno eliminati, non è reversibile! @@ -678,6 +786,21 @@ Tutti i nuovi messaggi da %@ verrranno nascosti! No comment provided by engineer. + + All profiles + Tutti gli profili + profile dropdown + + + All reports will be archived for you. + Tutte le segnalazioni verranno archiviate per te. + No comment provided by engineer. + + + All servers + Tutti i server + No comment provided by engineer. + All your contacts will remain connected. Tutti i tuoi contatti resteranno connessi. @@ -690,6 +813,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Tutti i tuoi contatti, le conversazioni e i file verranno criptati in modo sicuro e caricati in blocchi sui relay XFTP configurati. No comment provided by engineer. @@ -702,11 +826,21 @@ Consenti le chiamate solo se il tuo contatto le consente. No comment provided by engineer. + + Allow calls? + Consentire le chiamate? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Consenti i messaggi a tempo solo se il contatto li consente a te. No comment provided by engineer. + + Allow downgrade + Consenti downgrade + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Consenti l'eliminazione irreversibile dei messaggi solo se il contatto la consente a te. (24 ore) @@ -732,11 +866,26 @@ Permetti l'invio di messaggi a tempo. No comment provided by engineer. + + Allow sharing + Consenti la condivisione + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Permetti di eliminare irreversibilmente i messaggi inviati. (24 ore) No comment provided by engineer. + + Allow to report messsages to moderators. + Consenti di segnalare messaggi ai moderatori. + No comment provided by engineer. + + + Allow to send SimpleX links. + Consenti di inviare link di SimpleX. + No comment provided by engineer. + Allow to send files and media. Consenti l'invio di file e contenuti multimediali. @@ -797,6 +946,11 @@ Già in ingresso nel gruppo! No comment provided by engineer. + + Always use private routing. + Usa sempre l'instradamento privato. + No comment provided by engineer. + Always use relay Connetti via relay @@ -807,11 +961,21 @@ Viene creato un profilo di chat vuoto con il nome scelto e l'app si apre come al solito. No comment provided by engineer. + + Another reason + Altro motivo + report reason + Answer call Rispondi alla chiamata No comment provided by engineer. + + Anybody can host servers. + Chiunque può installare i server. + No comment provided by engineer. + App build: %@ Build dell'app: %@ @@ -819,6 +983,7 @@ App data migration + Migrazione dati dell'app No comment provided by engineer. @@ -826,6 +991,11 @@ L'app cripta i nuovi file locali (eccetto i video). No comment provided by engineer. + + App group: + Gruppo app: + No comment provided by engineer. + App icon Icona app @@ -841,6 +1011,11 @@ Il codice di accesso dell'app viene sostituito da un codice di autodistruzione. No comment provided by engineer. + + App session + Sessione dell'app + No comment provided by engineer. + App version Versione dell'app @@ -858,14 +1033,62 @@ Apply + Applica + No comment provided by engineer. + + + Apply to + Applica a + No comment provided by engineer. + + + Archive + Archivia + No comment provided by engineer. + + + Archive %lld reports? + Archiviare %lld segnalazioni? + No comment provided by engineer. + + + Archive all reports? + Archiviare tutte le segnalazioni? No comment provided by engineer. Archive and upload + Archivia e carica + No comment provided by engineer. + + + Archive contacts to chat later. + Archivia contatti per chattare più tardi. + No comment provided by engineer. + + + Archive report + Archivia la segnalazione + No comment provided by engineer. + + + Archive report? + Archiviare la segnalazione? + No comment provided by engineer. + + + Archive reports + Archivia segnalazioni + swipe action + + + Archived contacts + Contatti archiviati No comment provided by engineer. Archiving database + Archiviazione del database No comment provided by engineer. @@ -920,19 +1143,29 @@ Auto-accept contact requests - Auto-accetta richieste di contatto + Auto-accetta le richieste di contatto No comment provided by engineer. Auto-accept images - Auto-accetta immagini + Auto-accetta le immagini No comment provided by engineer. + + Auto-accept settings + Accetta automaticamente le impostazioni + alert title + Back Indietro No comment provided by engineer. + + Background + Sfondo + No comment provided by engineer. + Bad desktop address Indirizzo desktop errato @@ -948,16 +1181,61 @@ Hash del messaggio errato No comment provided by engineer. + + Better calls + Chiamate migliorate + No comment provided by engineer. + Better groups Gruppi migliorati No comment provided by engineer. + + Better groups performance + Prestazioni dei gruppi migliorate + No comment provided by engineer. + + + Better message dates. + Date dei messaggi migliorate. + No comment provided by engineer. + Better messages Messaggi migliorati No comment provided by engineer. + + Better networking + Rete migliorata + No comment provided by engineer. + + + Better notifications + Notifiche migliorate + No comment provided by engineer. + + + Better privacy and security + Privacy e sicurezza migliori + No comment provided by engineer. + + + Better security ✅ + Sicurezza migliorata ✅ + No comment provided by engineer. + + + Better user experience + Esperienza utente migliorata + No comment provided by engineer. + + + Black + Nero + No comment provided by engineer. + Block Blocca @@ -993,6 +1271,16 @@ Bloccato dall'amministratore No comment provided by engineer. + + Blur for better privacy. + Sfoca per una privacy maggiore. + No comment provided by engineer. + + + Blur media + Sfocatura dei file multimediali + No comment provided by engineer. + Both you and your contact can add message reactions. Sia tu che il tuo contatto potete aggiungere reazioni ai messaggi. @@ -1023,11 +1311,35 @@ Bulgaro, finlandese, tailandese e ucraino - grazie agli utenti e a [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + Indirizzo di lavoro + No comment provided by engineer. + + + Business chats + Chat di lavoro + No comment provided by engineer. + + + Businesses + Lavorative + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). Per profilo di chat (predefinito) o [per connessione](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + Usando SimpleX Chat accetti di: +- inviare solo contenuto legale nei gruppi pubblici. +- rispettare gli altri utenti - niente spam. + No comment provided by engineer. + Call already ended! Chiamata già terminata! @@ -1038,11 +1350,26 @@ Chiamate No comment provided by engineer. + + Calls prohibited! + Chiamate proibite! + No comment provided by engineer. + Camera not available Fotocamera non disponibile No comment provided by engineer. + + Can't call contact + Impossibile chiamare il contatto + No comment provided by engineer. + + + Can't call member + Impossibile chiamare il membro + No comment provided by engineer. + Can't invite contact! Impossibile invitare il contatto! @@ -1053,13 +1380,20 @@ Impossibile invitare i contatti! No comment provided by engineer. + + Can't message member + Impossibile inviare un messaggio al membro + No comment provided by engineer. + Cancel Annulla - No comment provided by engineer. + alert action +alert button Cancel migration + Annulla migrazione No comment provided by engineer. @@ -1067,9 +1401,24 @@ Impossibile accedere al portachiavi per salvare la password del database No comment provided by engineer. + + Cannot forward message + Impossibile inoltrare il messaggio + No comment provided by engineer. + Cannot receive file Impossibile ricevere il file + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Quota superata - il destinatario non ha ricevuto i messaggi precedentemente inviati. + snd error text + + + Cellular + Mobile No comment provided by engineer. @@ -1077,6 +1426,16 @@ Cambia No comment provided by engineer. + + Change automatic message deletion? + Cambiare l'eliminazione automatica dei messaggi? + alert title + + + Change chat profiles + Modifica profili utente + authentication reason + Change database passphrase? Cambiare password del database? @@ -1121,11 +1480,26 @@ Change self-destruct passcode Cambia codice di autodistruzione authentication reason - set passcode view +set passcode view - - Chat archive - Archivio chat + + Chat + Chat + No comment provided by engineer. + + + Chat already exists + La chat esiste già + No comment provided by engineer. + + + Chat already exists! + La chat esiste già! + No comment provided by engineer. + + + Chat colors + Colori della chat No comment provided by engineer. @@ -1143,6 +1517,11 @@ Database della chat eliminato No comment provided by engineer. + + Chat database exported + Database della chat esportato + No comment provided by engineer. + Chat database imported Database della chat importato @@ -1163,8 +1542,14 @@ La chat è ferma. Se hai già usato questo database su un altro dispositivo, dovresti trasferirlo prima di avviare la chat. No comment provided by engineer. + + Chat list + Elenco delle chat + No comment provided by engineer. + Chat migrated! + Chat migrata! No comment provided by engineer. @@ -1172,15 +1557,50 @@ Preferenze della chat No comment provided by engineer. + + Chat preferences were changed. + Le preferenze della chat sono state cambiate. + alert message + + + Chat profile + Profilo utente + No comment provided by engineer. + + + Chat theme + Tema della chat + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + La chat verrà eliminata per tutti i membri, non è reversibile! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + La chat verrà eliminata solo per te, non è reversibile! + No comment provided by engineer. + Chats Chat No comment provided by engineer. + + Check messages every 20 min. + Controlla i messaggi ogni 20 min. + No comment provided by engineer. + + + Check messages when allowed. + Controlla i messaggi quando consentito. + No comment provided by engineer. + Check server address and try again. Controlla l'indirizzo del server e riprova. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1189,6 +1609,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + Scegli _Migra da un altro dispositivo_ sul nuovo dispositivo e scansione il codice QR. No comment provided by engineer. @@ -1201,10 +1622,25 @@ Scegli dalla libreria No comment provided by engineer. + + Chunks deleted + Blocchi eliminati + No comment provided by engineer. + + + Chunks downloaded + Blocchi scaricati + No comment provided by engineer. + + + Chunks uploaded + Blocchi inviati + No comment provided by engineer. + Clear Svuota - No comment provided by engineer. + swipe action Clear conversation @@ -1216,6 +1652,16 @@ Svuotare la conversazione? No comment provided by engineer. + + Clear group? + Svuotare il gruppo? + No comment provided by engineer. + + + Clear or delete group? + Svuotare o eliminare il gruppo? + No comment provided by engineer. + Clear private notes? Svuotare le note private? @@ -1226,11 +1672,21 @@ Annulla la verifica No comment provided by engineer. - - Colors - Colori + + Color chats with the new themes. + Colora le chat con i nuovi temi. No comment provided by engineer. + + Color mode + Modalità di colore + No comment provided by engineer. + + + Community guidelines violation + Violazione delle linee guida della comunità + report reason + Compare file Confronta file @@ -1241,11 +1697,56 @@ Confronta i codici di sicurezza con i tuoi contatti. No comment provided by engineer. + + Completed + Completato + No comment provided by engineer. + + + Conditions accepted on: %@. + Condizioni accettate il: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + Le condizioni sono state accettate per gli operatori: **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + Le condizioni sono già state accettate per i seguenti operatori: **%@**. + No comment provided by engineer. + + + Conditions of use + Condizioni d'uso + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + Le condizioni verranno accettate per gli operatori: **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + Le condizioni verranno accettate il: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + Le condizioni verranno accettate automaticamente per gli operatori attivi il: %@. + No comment provided by engineer. + Configure ICE servers Configura server ICE No comment provided by engineer. + + Configure server operators + Configura gli operatori dei server + No comment provided by engineer. + Confirm Conferma @@ -1256,13 +1757,24 @@ Conferma il codice di accesso No comment provided by engineer. + + Confirm contact deletion? + Confermare l'eliminazione del contatto? + No comment provided by engineer. + Confirm database upgrades Conferma aggiornamenti database No comment provided by engineer. + + Confirm files from unknown servers. + Conferma i file da server sconosciuti. + No comment provided by engineer. + Confirm network settings + Conferma le impostazioni di rete No comment provided by engineer. @@ -1277,12 +1789,19 @@ Confirm that you remember database passphrase to migrate it. + Conferma che ricordi la password del database da migrare. No comment provided by engineer. Confirm upload + Conferma caricamento No comment provided by engineer. + + Confirmed + Confermato + token status text + Connect Connetti @@ -1303,6 +1822,11 @@ Connetti al desktop No comment provided by engineer. + + Connect to your friends faster. + Connettiti più velocemente ai tuoi amici. + No comment provided by engineer. + Connect to yourself? Connettersi a te stesso? @@ -1342,16 +1866,31 @@ Questo è il tuo link una tantum! Connettersi con %@ No comment provided by engineer. + + Connected + Connesso + No comment provided by engineer. + Connected desktop Desktop connesso No comment provided by engineer. + + Connected servers + Server connessi + No comment provided by engineer. + Connected to desktop Connesso al desktop No comment provided by engineer. + + Connecting + In connessione + No comment provided by engineer. + Connecting to server… Connessione al server… @@ -1362,6 +1901,11 @@ Questo è il tuo link una tantum! Connessione al server… (errore: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + In collegamento con il contatto, attendi o controlla più tardi! + No comment provided by engineer. + Connecting to desktop Connessione al desktop @@ -1372,6 +1916,16 @@ Questo è il tuo link una tantum! Connessione No comment provided by engineer. + + Connection and servers status. + Stato della connessione e dei server. + No comment provided by engineer. + + + Connection blocked + Connessione bloccata + No comment provided by engineer. + Connection error Errore di connessione @@ -1382,11 +1936,38 @@ Questo è il tuo link una tantum! Errore di connessione (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + La connessione è bloccata dall'operatore del server: +%@ + No comment provided by engineer. + + + Connection not ready. + Connessione non pronta. + No comment provided by engineer. + + + Connection notifications + Notifiche di connessione + No comment provided by engineer. + Connection request sent! Richiesta di connessione inviata! No comment provided by engineer. + + Connection requires encryption renegotiation. + La connessione richiede la rinegoziazione della crittografia. + No comment provided by engineer. + + + Connection security + Sicurezza della connessione + No comment provided by engineer. + Connection terminated Connessione terminata @@ -1397,6 +1978,16 @@ Questo è il tuo link una tantum! Connessione scaduta No comment provided by engineer. + + Connection with desktop stopped + Connessione con il desktop fermata + No comment provided by engineer. + + + Connections + Connessioni + No comment provided by engineer. + Contact allows Il contatto lo consente @@ -1407,6 +1998,11 @@ Questo è il tuo link una tantum! Il contatto esiste già No comment provided by engineer. + + Contact deleted! + Contatto eliminato! + No comment provided by engineer. + Contact hidden: Contatto nascosto: @@ -1417,9 +2013,9 @@ Questo è il tuo link una tantum! Il contatto è connesso notification - - Contact is not connected yet! - Il contatto non è ancora connesso! + + Contact is deleted. + Il contatto è stato eliminato. No comment provided by engineer. @@ -1432,6 +2028,11 @@ Questo è il tuo link una tantum! Preferenze del contatto No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + Il contatto verrà eliminato - non è reversibile! + No comment provided by engineer. + Contacts Contatti @@ -1442,21 +2043,41 @@ Questo è il tuo link una tantum! I contatti possono contrassegnare i messaggi per l'eliminazione; potrai vederli. No comment provided by engineer. + + Content violates conditions of use + Il contenuto viola le condizioni di utilizzo + blocking reason + Continue Continua No comment provided by engineer. + + Conversation deleted! + Conversazione eliminata! + No comment provided by engineer. + Copy Copia - chat item action + No comment provided by engineer. + + + Copy error + Copia errore + No comment provided by engineer. Core version: v%@ Versione core: v%@ No comment provided by engineer. + + Corner + Angolo + No comment provided by engineer. + Correct name to %@? Correggere il nome a %@? @@ -1467,6 +2088,11 @@ Questo è il tuo link una tantum! Crea No comment provided by engineer. + + Create 1-time link + Crea link una tantum + No comment provided by engineer. + Create SimpleX address Crea indirizzo SimpleX @@ -1477,11 +2103,6 @@ Questo è il tuo link una tantum! Crea un gruppo usando un profilo casuale. No comment provided by engineer. - - Create an address to let people connect with you. - Crea un indirizzo per consentire alle persone di connettersi con te. - No comment provided by engineer. - Create file Crea file @@ -1502,6 +2123,11 @@ Questo è il tuo link una tantum! Crea link No comment provided by engineer. + + Create list + Crea elenco + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 Crea un nuovo profilo nell'[app desktop](https://simplex.chat/downloads/). 💻 @@ -1527,6 +2153,11 @@ Questo è il tuo link una tantum! Crea il tuo profilo No comment provided by engineer. + + Created + Creato + No comment provided by engineer. + Created at Creato il @@ -1537,13 +2168,9 @@ Questo è il tuo link una tantum! Creato il: %@ copied message info - - Created on %@ - Creato il %@ - No comment provided by engineer. - Creating archive link + Creazione link dell'archivio No comment provided by engineer. @@ -1556,11 +2183,21 @@ Questo è il tuo link una tantum! Codice di accesso attuale No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + Il testo delle condizioni attuali testo non è stato caricato, puoi consultare le condizioni tramite questo link: + No comment provided by engineer. + Current passphrase… Password attuale… No comment provided by engineer. + + Current profile + Profilo attuale + No comment provided by engineer. + Currently maximum supported file size is %@. Attualmente la dimensione massima supportata è di %@. @@ -1571,11 +2208,26 @@ Questo è il tuo link una tantum! Tempo personalizzato No comment provided by engineer. + + Customizable message shape. + Forma dei messaggi personalizzabile. + No comment provided by engineer. + + + Customize theme + Personalizza il tema + No comment provided by engineer. + Dark Scuro No comment provided by engineer. + + Dark mode colors + Colori modalità scura + No comment provided by engineer. + Database ID ID database @@ -1674,6 +2326,11 @@ Questo è il tuo link una tantum! Il database verrà migrato al riavvio dell'app No comment provided by engineer. + + Debug delivery + Debug della consegna + No comment provided by engineer. + Decentralized Decentralizzato @@ -1687,18 +2344,19 @@ Questo è il tuo link una tantum! Delete Elimina - chat item action + alert action +swipe action + + + Delete %lld messages of members? + Eliminare %lld messaggi dei membri? + No comment provided by engineer. Delete %lld messages? Eliminare %lld messaggi? No comment provided by engineer. - - Delete Contact - Elimina contatto - No comment provided by engineer. - Delete address Elimina indirizzo @@ -1724,14 +2382,14 @@ Questo è il tuo link una tantum! Elimina e avvisa il contatto No comment provided by engineer. - - Delete archive - Elimina archivio + + Delete chat + Elimina chat No comment provided by engineer. - - Delete chat archive? - Eliminare l'archivio della chat? + + Delete chat messages from your device. + Elimina i messaggi di chat dal tuo dispositivo. No comment provided by engineer. @@ -1744,6 +2402,11 @@ Questo è il tuo link una tantum! Eliminare il profilo di chat? No comment provided by engineer. + + Delete chat? + Eliminare la chat? + No comment provided by engineer. + Delete connection Elimina connessione @@ -1754,11 +2417,9 @@ Questo è il tuo link una tantum! Elimina contatto No comment provided by engineer. - - Delete contact? -This cannot be undone! - Eliminare il contatto? -Non è reversibile! + + Delete contact? + Eliminare il contatto? No comment provided by engineer. @@ -1768,6 +2429,7 @@ Non è reversibile! Delete database from this device + Elimina il database da questo dispositivo No comment provided by engineer. @@ -1820,6 +2482,11 @@ Non è reversibile! Eliminare il link? No comment provided by engineer. + + Delete list? + Eliminare l'elenco? + alert title + Delete member message? Eliminare il messaggio del membro? @@ -1833,7 +2500,7 @@ Non è reversibile! Delete messages Elimina messaggi - No comment provided by engineer. + alert button Delete messages after @@ -1850,9 +2517,9 @@ Non è reversibile! Eliminare il database vecchio? No comment provided by engineer. - - Delete pending connection - Elimina connessione in attesa + + Delete or moderate up to 200 messages. + Elimina o modera fino a 200 messaggi. No comment provided by engineer. @@ -1870,11 +2537,31 @@ Non è reversibile! Elimina coda server test step + + Delete report + Elimina la segnalazione + No comment provided by engineer. + + + Delete up to 20 messages at once. + Elimina fino a 20 messaggi contemporaneamente. + No comment provided by engineer. + Delete user profile? Eliminare il profilo utente? No comment provided by engineer. + + Delete without notification + Elimina senza avvisare + No comment provided by engineer. + + + Deleted + Eliminato + No comment provided by engineer. + Deleted at Eliminato il @@ -1885,6 +2572,16 @@ Non è reversibile! Eliminato il: %@ copied message info + + Deletion errors + Errori di eliminazione + No comment provided by engineer. + + + Delivered even when Apple drops them. + Consegnati anche quando Apple li scarta. + No comment provided by engineer. + Delivery Consegna @@ -1920,11 +2617,41 @@ Non è reversibile! Dispositivi desktop No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + L'indirizzo del server di destinazione di %@ è incompatibile con le impostazioni del server di inoltro %@. + No comment provided by engineer. + + + Destination server error: %@ + Errore del server di destinazione: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + La versione del server di destinazione di %@ è incompatibile con il server di inoltro %@. + No comment provided by engineer. + + + Detailed statistics + Statistiche dettagliate + No comment provided by engineer. + + + Details + Dettagli + No comment provided by engineer. + Develop Sviluppa No comment provided by engineer. + + Developer options + Opzioni sviluppatore + No comment provided by engineer. + Developer tools Strumenti di sviluppo @@ -1955,8 +2682,13 @@ Non è reversibile! Messaggi diretti chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + I messaggi diretti tra i membri sono vietati in questa chat. + No comment provided by engineer. + + + Direct messages between members are prohibited. I messaggi diretti tra i membri sono vietati in questo gruppo. No comment provided by engineer. @@ -1970,11 +2702,26 @@ Non è reversibile! Disattiva SimpleX Lock authentication reason + + Disable automatic message deletion? + Disattivare l'eliminazione automatica dei messaggi? + alert title + + + Disable delete messages + Disattiva eliminazione messaggi + alert button + Disable for all Disattiva per tutti No comment provided by engineer. + + Disabled + Disattivato + No comment provided by engineer. + Disappearing message Messaggio a tempo @@ -1990,8 +2737,8 @@ Non è reversibile! I messaggi a tempo sono vietati in questa chat. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. I messaggi a tempo sono vietati in questo gruppo. No comment provided by engineer. @@ -2025,11 +2772,21 @@ Non è reversibile! Individua via rete locale No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + NON inviare messaggi direttamente, anche se il tuo server o quello di destinazione non supporta l'instradamento privato. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. NON usare SimpleX per chiamate di emergenza. No comment provided by engineer. + + Do NOT use private routing. + NON usare l'instradamento privato. + No comment provided by engineer. + Do it later Fallo dopo @@ -2040,6 +2797,16 @@ Non è reversibile! Non inviare la cronologia ai nuovi membri. No comment provided by engineer. + + Do not use credentials with proxy. + Non usare credenziali con proxy. + No comment provided by engineer. + + + Documents: + Documenti: + No comment provided by engineer. + Don't create address Non creare un indirizzo @@ -2050,18 +2817,40 @@ Non è reversibile! Non attivare No comment provided by engineer. + + Don't miss important messages. + Non perdere messaggi importanti. + No comment provided by engineer. + Don't show again Non mostrare più No comment provided by engineer. + + Done + Fatto + No comment provided by engineer. + Downgrade and open chat Esegui downgrade e apri chat No comment provided by engineer. + + Download + Scarica + alert button +chat item action + + + Download errors + Errori di scaricamento + No comment provided by engineer. + Download failed + Scaricamento fallito No comment provided by engineer. @@ -2069,12 +2858,29 @@ Non è reversibile! Scarica file server test step + + Download files + Scarica i file + alert action + + + Downloaded + Scaricato + No comment provided by engineer. + + + Downloaded files + File scaricati + No comment provided by engineer. + Downloading archive + Scaricamento archivio No comment provided by engineer. Downloading link details + Scaricamento dettagli del link No comment provided by engineer. @@ -2087,6 +2893,11 @@ Non è reversibile! Durata No comment provided by engineer. + + E2E encrypted notifications. + Notifiche crittografate E2E. + No comment provided by engineer. + Edit Modifica @@ -2107,6 +2918,11 @@ Non è reversibile! Attiva (mantieni sostituzioni) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + Attiva Flux nelle impostazioni "Rete e server" per una migliore privacy dei metadati. + No comment provided by engineer. + Enable SimpleX Lock Attiva SimpleX Lock @@ -2120,7 +2936,7 @@ Non è reversibile! Enable automatic message deletion? Attivare l'eliminazione automatica dei messaggi? - No comment provided by engineer. + alert title Enable camera access @@ -2134,6 +2950,7 @@ Non è reversibile! Enable in direct chats (BETA)! + Attivala nelle chat dirette (BETA)! No comment provided by engineer. @@ -2166,6 +2983,16 @@ Non è reversibile! Attiva il codice di autodistruzione set passcode view + + Enabled + Attivato + No comment provided by engineer. + + + Enabled for + Attivo per + No comment provided by engineer. + Encrypt Crittografare @@ -2236,6 +3063,11 @@ Non è reversibile! Rinegoziazione crittografia fallita. No comment provided by engineer. + + Encryption renegotiation in progress. + Rinegoziazione della crittografia in corso. + No comment provided by engineer. + Enter Passcode Inserisci il codice di accesso @@ -2253,6 +3085,7 @@ Non è reversibile! Enter passphrase + Inserisci password No comment provided by engineer. @@ -2300,30 +3133,36 @@ Non è reversibile! Errore nell'interruzione del cambio di indirizzo No comment provided by engineer. + + Error accepting conditions + Errore di accettazione delle condizioni + alert title + Error accepting contact request Errore nell'accettazione della richiesta di contatto No comment provided by engineer. - - Error accessing database file - Errore nell'accesso al file del database - No comment provided by engineer. - Error adding member(s) Errore di aggiunta membro/i No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + Errore di aggiunta del server + alert title Error changing address Errore nella modifica dell'indirizzo No comment provided by engineer. + + Error changing connection profile + Errore nel cambio di profilo di connessione + No comment provided by engineer. + Error changing role Errore nel cambio di ruolo @@ -2334,6 +3173,21 @@ Non è reversibile! Errore nella modifica dell'impostazione No comment provided by engineer. + + Error changing to incognito! + Errore nel passaggio a incognito! + No comment provided by engineer. + + + Error checking token status + Errore di controllo dello stato del token + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + Errore di connessione al server di inoltro %@. Riprova più tardi. + No comment provided by engineer. + Error creating address Errore nella creazione dell'indirizzo @@ -2349,6 +3203,11 @@ Non è reversibile! Errore nella creazione del link del gruppo No comment provided by engineer. + + Error creating list + Errore nella creazione dell'elenco + alert title + Error creating member contact Errore di creazione del contatto @@ -2364,6 +3223,11 @@ Non è reversibile! Errore nella creazione del profilo! No comment provided by engineer. + + Error creating report + Errore nella creazione del resoconto + No comment provided by engineer. + Error decrypting file Errore decifrando il file @@ -2384,11 +3248,6 @@ Non è reversibile! Errore nell'eliminazione della connessione No comment provided by engineer. - - Error deleting contact - Errore nell'eliminazione del contatto - No comment provided by engineer. - Error deleting database Errore nell'eliminazione del database @@ -2411,6 +3270,7 @@ Non è reversibile! Error downloading the archive + Errore di scaricamento dell'archivio No comment provided by engineer. @@ -2433,6 +3293,11 @@ Non è reversibile! Errore nell'esportazione del database della chat No comment provided by engineer. + + Error exporting theme: %@ + Errore di esportazione del tema: %@ + No comment provided by engineer. + Error importing chat database Errore nell'importazione del database della chat @@ -2443,9 +3308,14 @@ Non è reversibile! Errore di ingresso nel gruppo No comment provided by engineer. - - Error loading %@ servers - Errore nel caricamento dei server %@ + + Error loading servers + Errore nel caricamento dei server + alert title + + + Error migrating settings + Errore nella migrazione delle impostazioni No comment provided by engineer. @@ -2456,16 +3326,36 @@ Non è reversibile! Error receiving file Errore nella ricezione del file + alert title + + + Error reconnecting server + Errore di riconnessione al server No comment provided by engineer. + + Error reconnecting servers + Errore di riconnessione ai server + No comment provided by engineer. + + + Error registering for notifications + Errore di registrazione per le notifiche + alert title + Error removing member Errore nella rimozione del membro No comment provided by engineer. - - Error saving %@ servers - Errore nel salvataggio dei server %@ + + Error reordering lists + Errore riordinando gli elenchi + alert title + + + Error resetting statistics + Errore di azzeramento statistiche No comment provided by engineer. @@ -2473,6 +3363,11 @@ Non è reversibile! Errore nel salvataggio dei server ICE No comment provided by engineer. + + Error saving chat list + Errore nel salvataggio dell'elenco di chat + alert title + Error saving group profile Errore nel salvataggio del profilo del gruppo @@ -2488,8 +3383,14 @@ Non è reversibile! Errore nel salvataggio della password nel portachiavi No comment provided by engineer. + + Error saving servers + Errore di salvataggio dei server + alert title + Error saving settings + Errore di salvataggio delle impostazioni when migrating @@ -2532,16 +3433,26 @@ Non è reversibile! Errore nell'interruzione della chat No comment provided by engineer. + + Error switching profile + Errore nel cambio di profilo + No comment provided by engineer. + Error switching profile! Errore nel cambio di profilo! - No comment provided by engineer. + alertTitle Error synchronizing connection Errore nella sincronizzazione della connessione No comment provided by engineer. + + Error testing server connection + Errore provando la connessione al server + No comment provided by engineer. + Error updating group link Errore nell'aggiornamento del link del gruppo @@ -2552,6 +3463,11 @@ Non è reversibile! Errore nell'aggiornamento del messaggio No comment provided by engineer. + + Error updating server + Errore di aggiornamento del server + alert title + Error updating settings Errore nell'aggiornamento delle impostazioni @@ -2564,10 +3480,12 @@ Non è reversibile! Error uploading the archive + Errore di invio dell'archivio No comment provided by engineer. Error verifying passphrase: + Errore di verifica della password: No comment provided by engineer. @@ -2578,7 +3496,9 @@ Non è reversibile! Error: %@ Errore: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2590,6 +3510,16 @@ Non è reversibile! Errore: nessun file di database No comment provided by engineer. + + Errors + Errori + No comment provided by engineer. + + + Errors in servers configuration. + Errori nella configurazione dei server. + servers error + Even when disabled in the conversation. Anche quando disattivato nella conversazione. @@ -2605,6 +3535,11 @@ Non è reversibile! Espandi chat item action + + Expired + Scaduto + token status text + Export database Esporta database @@ -2615,6 +3550,11 @@ Non è reversibile! Errore di esportazione: No comment provided by engineer. + + Export theme + Esporta tema + No comment provided by engineer. + Exported database archive. Archivio database esportato. @@ -2622,6 +3562,7 @@ Non è reversibile! Exported file doesn't exist + Il file esportato non esiste No comment provided by engineer. @@ -2639,16 +3580,70 @@ Non è reversibile! Veloce e senza aspettare che il mittente sia in linea! No comment provided by engineer. + + Faster deletion of groups. + Eliminazione dei gruppi più veloce. + No comment provided by engineer. + Faster joining and more reliable messages. Ingresso più veloce e messaggi più affidabili. No comment provided by engineer. + + Faster sending messages. + Invio dei messaggi più veloce. + No comment provided by engineer. + Favorite Preferito + swipe action + + + Favorites + Preferite No comment provided by engineer. + + File error + Errore del file + file error alert title + + + File errors: +%@ + Errori di file: +%@ + alert message + + + File is blocked by server operator: +%@. + Il file è bloccato dall'operatore del server: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + File non trovato - probabilmente è stato eliminato o annullato. + file error text + + + File server error: %@ + Errore del server dei file: %@ + file error text + + + File status + Stato del file + No comment provided by engineer. + + + File status: %@ + Stato del file: %@ + copied message info + File will be deleted from servers. Il file verrà eliminato dai server. @@ -2669,6 +3664,11 @@ Non è reversibile! File: %@ No comment provided by engineer. + + Files + File + No comment provided by engineer. + Files & media File e multimediali @@ -2679,11 +3679,16 @@ Non è reversibile! File e multimediali chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. File e contenuti multimediali sono vietati in questo gruppo. No comment provided by engineer. + + Files and media not allowed + File e multimediali non consentiti + No comment provided by engineer. + Files and media prohibited! File e contenuti multimediali vietati! @@ -2696,10 +3701,12 @@ Non è reversibile! Finalize migration + Finalizza la migrazione No comment provided by engineer. Finalize migration on another device. + Finalizza la migrazione su un altro dispositivo. No comment provided by engineer. @@ -2742,11 +3749,115 @@ Non è reversibile! Correzione non supportata dal membro del gruppo No comment provided by engineer. + + For all moderators + Per tutti i moderatori + No comment provided by engineer. + + + For chat profile %@: + Per il profilo di chat %@: + servers error + For console Per console No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + Ad esempio, se il tuo contatto riceve messaggi tramite un server di SimpleX Chat, la tua app li consegnerà tramite un server Flux. + No comment provided by engineer. + + + For me + Per me + No comment provided by engineer. + + + For private routing + Per l'instradamento privato + No comment provided by engineer. + + + For social media + Per i social media + No comment provided by engineer. + + + Forward + Inoltra + chat item action + + + Forward %d message(s)? + Inoltrare %d messaggio/i? + alert title + + + Forward and save messages + Inoltra e salva i messaggi + No comment provided by engineer. + + + Forward messages + Inoltra i messaggi + alert action + + + Forward messages without files? + Inoltrare i messaggi senza file? + alert message + + + Forward up to 20 messages at once. + Inoltra fino a 20 messaggi alla volta. + No comment provided by engineer. + + + Forwarded + Inoltrato + No comment provided by engineer. + + + Forwarded from + Inoltrato da + No comment provided by engineer. + + + Forwarding %lld messages + Inoltro di %lld messaggi + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + Il server di inoltro %@ non è riuscito a connettersi al server di destinazione %@. Riprova più tardi. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + L'indirizzo del server di inoltro è incompatibile con le impostazioni di rete: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + La versione del server di inoltro è incompatibile con le impostazioni di rete: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + Server di inoltro: %1$@ +Errore del server di destinazione: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + Server di inoltro: %1$@ +Errore: %2$@ + snd error text + Found desktop Desktop trovato @@ -2767,11 +3878,6 @@ Non è reversibile! Nome completo (facoltativo) No comment provided by engineer. - - Full name: - Nome completo: - No comment provided by engineer. - Fully decentralized – visible only to members. Completamente decentralizzato: visibile solo ai membri. @@ -2792,6 +3898,21 @@ Non è reversibile! GIF e adesivi No comment provided by engineer. + + Get notified when mentioned. + Ricevi una notifica quando menzionato. + No comment provided by engineer. + + + Good afternoon! + Buon pomeriggio! + message preview + + + Good morning! + Buongiorno! + message preview + Group Gruppo @@ -2847,36 +3968,6 @@ Non è reversibile! Link del gruppo No comment provided by engineer. - - Group members can add message reactions. - I membri del gruppo possono aggiungere reazioni ai messaggi. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - I membri del gruppo possono eliminare irreversibilmente i messaggi inviati. (24 ore) - No comment provided by engineer. - - - Group members can send direct messages. - I membri del gruppo possono inviare messaggi diretti. - No comment provided by engineer. - - - Group members can send disappearing messages. - I membri del gruppo possono inviare messaggi a tempo. - No comment provided by engineer. - - - Group members can send files and media. - I membri del gruppo possono inviare file e contenuti multimediali. - No comment provided by engineer. - - - Group members can send voice messages. - I membri del gruppo possono inviare messaggi vocali. - No comment provided by engineer. - Group message: Messaggio del gruppo: @@ -2917,11 +4008,21 @@ Non è reversibile! Il gruppo verrà eliminato per te. Non è reversibile! No comment provided by engineer. + + Groups + Gruppi + No comment provided by engineer. + Help Aiuto No comment provided by engineer. + + Help admins moderating their groups. + Aiuta gli amministratori a moderare i loro gruppi. + No comment provided by engineer. + Hidden Nascosta @@ -2972,10 +4073,20 @@ Non è reversibile! Come funziona SimpleX No comment provided by engineer. + + How it affects privacy + Come influisce sulla privacy + No comment provided by engineer. + + + How it helps privacy + Come aiuta la privacy + No comment provided by engineer. + How it works Come funziona - No comment provided by engineer. + alert button How to @@ -2994,6 +4105,7 @@ Non è reversibile! Hungarian interface + Interfaccia in ungherese No comment provided by engineer. @@ -3001,6 +4113,11 @@ Non è reversibile! Server ICE (uno per riga) No comment provided by engineer. + + IP address + Indirizzo IP + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Se non potete incontrarvi di persona, mostra il codice QR in una videochiamata o condividi il link. @@ -3041,8 +4158,8 @@ Non è reversibile! Immediatamente No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Immune a spam e abusi No comment provided by engineer. @@ -3063,10 +4180,24 @@ Non è reversibile! Import failed + Importazione fallita + No comment provided by engineer. + + + Import theme + Importa tema No comment provided by engineer. Importing archive + Importazione archivio + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Consegna migliorata, utilizzo di traffico ridotto. +Altri miglioramenti sono in arrivo! No comment provided by engineer. @@ -3086,6 +4217,7 @@ Non è reversibile! In order to continue, chat should be stopped. + Per continuare, la chat deve essere fermata. No comment provided by engineer. @@ -3093,6 +4225,21 @@ Non è reversibile! In risposta a No comment provided by engineer. + + In-call sounds + Suoni nelle chiamate + No comment provided by engineer. + + + Inappropriate content + Contenuto inappropriato + report reason + + + Inappropriate profile + Profilo inappropriato + report reason + Incognito Incognito @@ -3163,6 +4310,11 @@ Non è reversibile! Installa [Simplex Chat per terminale](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Istantaneamente + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3170,16 +4322,41 @@ Non è reversibile! No comment provided by engineer. - - Instantly - Istantaneamente - No comment provided by engineer. - Interface Interfaccia No comment provided by engineer. + + Interface colors + Colori dell'interfaccia + No comment provided by engineer. + + + Invalid + Non valido + token status text + + + Invalid (bad token) + Non valido (token corrotto) + token status text + + + Invalid (expired) + Non valido (scaduto) + token status text + + + Invalid (unregistered) + Non valido (non registrato) + token status text + + + Invalid (wrong topic) + Non valido (argomento sbagliato) + token status text + Invalid QR code Codice QR non valido @@ -3202,6 +4379,7 @@ Non è reversibile! Invalid migration confirmation + Conferma di migrazione non valida No comment provided by engineer. @@ -3217,7 +4395,7 @@ Non è reversibile! Invalid server address! Indirizzo del server non valido! - No comment provided by engineer. + alert title Invalid status @@ -3239,6 +4417,11 @@ Non è reversibile! Invita membri No comment provided by engineer. + + Invite to chat + Invita in chat + No comment provided by engineer. + Invite to group Invita al gruppo @@ -3254,8 +4437,8 @@ Non è reversibile! L'eliminazione irreversibile dei messaggi è vietata in questa chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. L'eliminazione irreversibile dei messaggi è vietata in questo gruppo. No comment provided by engineer. @@ -3280,6 +4463,11 @@ Non è reversibile! 3. La connessione è stata compromessa. No comment provided by engineer. + + It protects your IP address and connections. + Protegge il tuo indirizzo IP e le connessioni. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Sembra che tu sia già connesso tramite questo link. In caso contrario, c'è stato un errore (%@). @@ -3298,7 +4486,7 @@ Non è reversibile! Join Entra - No comment provided by engineer. + swipe action Join group @@ -3340,6 +4528,11 @@ Questo è il tuo link per il gruppo %@! Keep Tieni + alert action + + + Keep conversation + Tieni la conversazione No comment provided by engineer. @@ -3350,7 +4543,7 @@ Questo è il tuo link per il gruppo %@! Keep unused invitation? Tenere l'invito inutilizzato? - No comment provided by engineer. + alert title Keep your connections @@ -3385,6 +4578,16 @@ Questo è il tuo link per il gruppo %@! Leave Esci + swipe action + + + Leave chat + Esci dalla chat + No comment provided by engineer. + + + Leave chat? + Uscire dalla chat? No comment provided by engineer. @@ -3427,6 +4630,21 @@ Questo è il tuo link per il gruppo %@! Desktop collegati No comment provided by engineer. + + List + Elenco + swipe action + + + List name and emoji should be different for all lists. + Il nome dell'elenco e l'emoji dovrebbero essere diversi per tutte le liste. + No comment provided by engineer. + + + List name... + Nome elenco... + No comment provided by engineer. + Live message! Messaggio in diretta! @@ -3437,11 +4655,6 @@ Questo è il tuo link per il gruppo %@! Messaggi in diretta No comment provided by engineer. - - Local - Locale - No comment provided by engineer. - Local name Nome locale @@ -3462,11 +4675,6 @@ Questo è il tuo link per il gruppo %@! Modalità di blocco No comment provided by engineer. - - Make a private connection - Crea una connessione privata - No comment provided by engineer. - Make one message disappear Fai sparire un messaggio @@ -3477,21 +4685,11 @@ Questo è il tuo link per il gruppo %@! Rendi privato il profilo! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Assicurati che gli indirizzi dei server %@ siano nel formato corretto, uno per riga e non doppi (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Assicurati che gli indirizzi dei server WebRTC ICE siano nel formato corretto, uno per riga e non doppi. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Molte persone hanno chiesto: *se SimpleX non ha identificatori utente, come può recapitare i messaggi?* - No comment provided by engineer. - Mark deleted for everyone Contrassegna eliminato per tutti @@ -3517,11 +4715,36 @@ Questo è il tuo link per il gruppo %@! Max 30 secondi, ricevuto istantaneamente. No comment provided by engineer. + + Media & file servers + Server di multimediali e file + No comment provided by engineer. + + + Medium + Media + blur media + Member Membro No comment provided by engineer. + + Member inactive + Membro inattivo + item status text + + + Member reports + Segnalazioni dei membri + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + Il ruolo del membro verrà cambiato in "%@". Verranno notificati tutti i membri della chat. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. Il ruolo del membro verrà cambiato in "%@". Tutti i membri del gruppo verranno avvisati. @@ -3532,11 +4755,66 @@ Questo è il tuo link per il gruppo %@! Il ruolo del membro verrà cambiato in "%@". Il membro riceverà un invito nuovo. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + Il membro verrà rimosso dalla chat, non è reversibile! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! Il membro verrà rimosso dal gruppo, non è reversibile! No comment provided by engineer. + + Members can add message reactions. + I membri del gruppo possono aggiungere reazioni ai messaggi. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + I membri del gruppo possono eliminare irreversibilmente i messaggi inviati. (24 ore) + No comment provided by engineer. + + + Members can report messsages to moderators. + I membri possono segnalare messaggi ai moderatori. + No comment provided by engineer. + + + Members can send SimpleX links. + I membri del gruppo possono inviare link di Simplex. + No comment provided by engineer. + + + Members can send direct messages. + I membri del gruppo possono inviare messaggi diretti. + No comment provided by engineer. + + + Members can send disappearing messages. + I membri del gruppo possono inviare messaggi a tempo. + No comment provided by engineer. + + + Members can send files and media. + I membri del gruppo possono inviare file e contenuti multimediali. + No comment provided by engineer. + + + Members can send voice messages. + I membri del gruppo possono inviare messaggi vocali. + No comment provided by engineer. + + + Mention members 👋 + Menziona i membri 👋 + No comment provided by engineer. + + + Menus + Menu + No comment provided by engineer. + Message delivery error Errore di recapito del messaggio @@ -3547,9 +4825,29 @@ Questo è il tuo link per il gruppo %@! Ricevute di consegna dei messaggi! No comment provided by engineer. + + Message delivery warning + Avviso di consegna del messaggio + item status text + Message draft - Bozza dei messaggi + Bozza del messaggio + No comment provided by engineer. + + + Message forwarded + Messaggio inoltrato + item status text + + + Message may be delivered later if member becomes active. + Il messaggio può essere consegnato più tardi se il membro diventa attivo. + item status description + + + Message queue info + Info coda messaggi No comment provided by engineer. @@ -3562,11 +4860,41 @@ Questo è il tuo link per il gruppo %@! Le reazioni ai messaggi sono vietate in questa chat. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. Le reazioni ai messaggi sono vietate in questo gruppo. No comment provided by engineer. + + Message reception + Ricezione messaggi + No comment provided by engineer. + + + Message servers + Server dei messaggi + No comment provided by engineer. + + + Message shape + Forma del messaggio + No comment provided by engineer. + + + Message source remains private. + La fonte del messaggio resta privata. + No comment provided by engineer. + + + Message status + Stato del messaggio + No comment provided by engineer. + + + Message status: %@ + Stato del messaggio: %@ + copied message info + Message text Testo del messaggio @@ -3574,6 +4902,7 @@ Questo è il tuo link per il gruppo %@! Message too large + Messaggio troppo grande No comment provided by engineer. @@ -3591,36 +4920,64 @@ Questo è il tuo link per il gruppo %@! I messaggi da %@ verranno mostrati! No comment provided by engineer. + + Messages in this chat will never be deleted. + I messaggi in questa chat non verranno mai eliminati. + alert message + + + Messages received + Messaggi ricevuti + No comment provided by engineer. + + + Messages sent + Messaggi inviati + No comment provided by engineer. + + + Messages were deleted after you selected them. + I messaggi sono stati eliminati dopo che li hai selezionati. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + I messaggi, i file e le chiamate sono protetti da **crittografia end-to-end** con perfect forward secrecy, ripudio e recupero da intrusione. No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + I messaggi, i file e le chiamate sono protetti da **crittografia e2e resistente alla quantistica** con perfect forward secrecy, ripudio e recupero da intrusione. No comment provided by engineer. Migrate device + Migra dispositivo No comment provided by engineer. Migrate from another device + Migra da un altro dispositivo No comment provided by engineer. Migrate here + Migra qui No comment provided by engineer. Migrate to another device + Migra ad un altro dispositivo No comment provided by engineer. Migrate to another device via QR code. + Migra ad un altro dispositivo via codice QR. No comment provided by engineer. Migrating + Migrazione No comment provided by engineer. @@ -3630,6 +4987,7 @@ Questo è il tuo link per il gruppo %@! Migration complete + Migrazione completata No comment provided by engineer. @@ -3647,9 +5005,9 @@ Questo è il tuo link per il gruppo %@! La migrazione è completata No comment provided by engineer. - - Migrations: %@ - Migrazioni: %@ + + Migrations: + Migrazioni: No comment provided by engineer. @@ -3667,21 +5025,31 @@ Questo è il tuo link per il gruppo %@! Moderato il: %@ copied message info + + More + Altro + swipe action + More improvements are coming soon! Altri miglioramenti sono in arrivo! No comment provided by engineer. + + More reliable network connection. + Connessione di rete più affidabile. + No comment provided by engineer. + + + More reliable notifications + Notifiche più affidabili + No comment provided by engineer. + Most likely this connection is deleted. Probabilmente questa connessione è stata eliminata. item status description - - Most likely this contact has deleted the connection with you. - Probabilmente questo contatto ha eliminato la connessione con te. - No comment provided by engineer. - Multiple chat profiles Profili di chat multipli @@ -3690,7 +5058,12 @@ Questo è il tuo link per il gruppo %@! Mute Silenzia - No comment provided by engineer. + notification label action + + + Mute all + Silenzia tutto + notification label action Muted when inactive! @@ -3700,13 +5073,38 @@ Questo è il tuo link per il gruppo %@! Name Nome - No comment provided by engineer. + swipe action Network & servers Rete e server No comment provided by engineer. + + Network connection + Connessione di rete + No comment provided by engineer. + + + Network decentralization + Decentralizzazione della rete + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + Problemi di rete - messaggio scaduto dopo molti tentativi di inviarlo. + snd error text + + + Network management + Gestione della rete + No comment provided by engineer. + + + Network operator + Operatore di rete + No comment provided by engineer. + Network settings Impostazioni di rete @@ -3717,16 +5115,36 @@ Questo è il tuo link per il gruppo %@! Stato della rete No comment provided by engineer. + + New + Nuovo + token status text + New Passcode Nuovo codice di accesso No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Le nuove credenziali SOCKS verranno usate ogni volta che avvii l'app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Le nuove credenziali SOCKS verranno usate per ogni server. + No comment provided by engineer. + New chat Nuova chat No comment provided by engineer. + + New chat experience 🎉 + Una nuova esperienza di chat 🎉 + No comment provided by engineer. + New contact request Nuova richiesta di contatto @@ -3737,11 +5155,6 @@ Questo è il tuo link per il gruppo %@! Nuovo contatto: notification - - New database archive - Nuovo archivio database - No comment provided by engineer. - New desktop app! Nuova app desktop! @@ -3752,11 +5165,21 @@ Questo è il tuo link per il gruppo %@! Nuovo nome da mostrare No comment provided by engineer. + + New events + Nuovi eventi + notification + New in %@ Novità nella %@ No comment provided by engineer. + + New media options + Nuove opzioni multimediali + No comment provided by engineer. + New member role Nuovo ruolo del membro @@ -3772,6 +5195,11 @@ Questo è il tuo link per il gruppo %@! Nuova password… No comment provided by engineer. + + New server + Nuovo server + No comment provided by engineer. + No No @@ -3782,6 +5210,21 @@ Questo è il tuo link per il gruppo %@! Nessuna password dell'app Authentication unavailable + + No chats + Nessuna chat + No comment provided by engineer. + + + No chats found + Nessuna chat trovata + No comment provided by engineer. + + + No chats in list %@ + Nessuna chat nell'elenco %@ + No comment provided by engineer. + No contacts selected Nessun contatto selezionato @@ -3802,6 +5245,11 @@ Questo è il tuo link per il gruppo %@! Nessun token del dispositivo! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + Ancora nessuna connessione diretta, il messaggio viene inoltrato dall'amministratore. + item status description + No filtered chats Nessuna chat filtrata @@ -3817,21 +5265,111 @@ Questo è il tuo link per il gruppo %@! Nessuna cronologia No comment provided by engineer. + + No info, try to reload + Nessuna informazione, prova a ricaricare + No comment provided by engineer. + + + No media & file servers. + Nessun server di multimediali e file. + servers error + + + No message + Nessun messaggio + No comment provided by engineer. + + + No message servers. + Nessun server dei messaggi. + servers error + + + No network connection + Nessuna connessione di rete + No comment provided by engineer. + + + No permission to record speech + Nessuna autorizzazione per registrare l'audio + No comment provided by engineer. + + + No permission to record video + Nessuna autorizzazione per registrare il video + No comment provided by engineer. + No permission to record voice message Nessuna autorizzazione per registrare messaggi vocali No comment provided by engineer. + + No push server + Locale + No comment provided by engineer. + No received or sent files Nessun file ricevuto o inviato No comment provided by engineer. + + No servers for private message routing. + Nessun server per l'instradamento dei messaggi privati. + servers error + + + No servers to receive files. + Nessun server per ricevere file. + servers error + + + No servers to receive messages. + Nessun server per ricevere messaggi. + servers error + + + No servers to send files. + Nessun server per inviare file. + servers error + + + No token! + Nessun token! + alert title + + + No unread chats + Nessuna chat non letta + No comment provided by engineer. + + + No user identifiers. + Nessun identificatore utente. + No comment provided by engineer. + Not compatible! Non compatibile! No comment provided by engineer. + + Notes + Note + No comment provided by engineer. + + + Nothing selected + Nessuna selezione + No comment provided by engineer. + + + Nothing to forward! + Niente da inoltrare! + alert title + Notifications Notifiche @@ -3842,6 +5380,21 @@ Questo è il tuo link per il gruppo %@! Le notifiche sono disattivate! No comment provided by engineer. + + Notifications error + Errore delle notifiche + alert title + + + Notifications privacy + Privacy delle notifiche + No comment provided by engineer. + + + Notifications status + Stato delle notifiche + alert title + Now admins can: - delete members' messages. @@ -3859,36 +5412,35 @@ Questo è il tuo link per il gruppo %@! Off Off - No comment provided by engineer. + blur media Ok Ok - No comment provided by engineer. + alert button Old database Database vecchio No comment provided by engineer. - - Old database archive - Vecchio archivio del database - No comment provided by engineer. - One-time invitation link Link di invito una tantum No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Gli host Onion saranno necessari per la connessione. Richiede l'attivazione della VPN. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Gli host Onion saranno **necessari** per la connessione. +Richiede l'attivazione della VPN. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Gli host Onion verranno usati quando disponibili. Richiede l'attivazione della VPN. + + Onion hosts will be used when available. +Requires compatible VPN. + Gli host Onion verranno usati quando disponibili. +Richiede l'attivazione della VPN. No comment provided by engineer. @@ -3896,11 +5448,21 @@ Questo è il tuo link per il gruppo %@! Gli host Onion non verranno usati. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + Solo i proprietari della chat possono modificarne le preferenze. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. Solo i dispositivi client archiviano profili utente, i contatti, i gruppi e i messaggi inviati con la **crittografia end-to-end a 2 livelli**. No comment provided by engineer. + + Only delete conversation + Elimina solo la conversazione + No comment provided by engineer. + Only group owners can change group preferences. Solo i proprietari del gruppo possono modificarne le preferenze. @@ -3916,6 +5478,16 @@ Questo è il tuo link per il gruppo %@! Solo i proprietari del gruppo possono attivare i messaggi vocali. No comment provided by engineer. + + Only sender and moderators see it + Solo il mittente e i moderatori lo vedono + No comment provided by engineer. + + + Only you and moderators see it + Solo tu e i moderatori lo vedete + No comment provided by engineer. + Only you can add message reactions. Solo tu puoi aggiungere reazioni ai messaggi. @@ -3969,13 +5541,18 @@ Questo è il tuo link per il gruppo %@! Open Apri - No comment provided by engineer. + alert action Open Settings Apri le impostazioni No comment provided by engineer. + + Open changes + Apri le modifiche + No comment provided by engineer. + Open chat Apri chat @@ -3986,32 +5563,48 @@ Questo è il tuo link per il gruppo %@! Apri la console della chat authentication reason + + Open conditions + Apri le condizioni + No comment provided by engineer. + Open group Apri gruppo No comment provided by engineer. + + Open link? + alert title + Open migration to another device + Apri migrazione ad un altro dispositivo authentication reason - - Open user profiles - Apri i profili utente - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Protocollo e codice open source: chiunque può gestire i server. - No comment provided by engineer. - Opening app… Apertura dell'app… No comment provided by engineer. + + Operator + Operatore + No comment provided by engineer. + + + Operator server + Server dell'operatore + alert title + + + Or import archive file + O importa file archivio + No comment provided by engineer. + Or paste archive link + O incolla il link dell'archivio No comment provided by engineer. @@ -4021,6 +5614,7 @@ Questo è il tuo link per il gruppo %@! Or securely share this file link + O condividi in modo sicuro questo link del file No comment provided by engineer. @@ -4028,6 +5622,28 @@ Questo è il tuo link per il gruppo %@! O mostra questo codice No comment provided by engineer. + + Or to share privately + O per condividere in modo privato + No comment provided by engineer. + + + Organize chats into lists + Organizza le chat in elenchi + No comment provided by engineer. + + + Other + Altro + No comment provided by engineer. + + + Other file errors: +%@ + Altri errori di file: +%@ + alert message + PING count Conteggio PING @@ -4063,6 +5679,11 @@ Questo è il tuo link per il gruppo %@! Codice di accesso impostato! No comment provided by engineer. + + Password + Password + No comment provided by engineer. + Password to show Password per mostrare @@ -4093,13 +5714,13 @@ Questo è il tuo link per il gruppo %@! Incolla il link che hai ricevuto No comment provided by engineer. - - People can connect to you only via the links you share. - Le persone possono connettersi a te solo tramite i link che condividi. + + Pending + In attesa No comment provided by engineer. - - Periodically + + Periodic Periodicamente No comment provided by engineer. @@ -4110,6 +5731,17 @@ Questo è il tuo link per il gruppo %@! Picture-in-picture calls + Chiamate picture-in-picture + No comment provided by engineer. + + + Play from the chat list. + Riproduci dall'elenco delle chat. + No comment provided by engineer. + + + Please ask your contact to enable calls. + Chiedi al contatto di attivare le chiamate. No comment provided by engineer. @@ -4117,6 +5749,13 @@ Questo è il tuo link per il gruppo %@! Chiedi al tuo contatto di attivare l'invio dei messaggi vocali. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + Controlla che mobile e desktop siano collegati alla stessa rete locale e che il firewall del desktop consenta la connessione. +Si prega di condividere qualsiasi altro problema con gli sviluppatori. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Controlla di aver usato il link giusto o chiedi al tuo contatto di inviartene un altro. @@ -4134,6 +5773,7 @@ Questo è il tuo link per il gruppo %@! Please confirm that network settings are correct for this device. + Conferma che le impostazioni di rete sono corrette per questo dispositivo. No comment provided by engineer. @@ -4183,60 +5823,121 @@ Errore: %@ Conserva la password in modo sicuro, NON potrai cambiarla se la perdi. No comment provided by engineer. + + Please try to disable and re-enable notfications. + Prova a disattivare e riattivare le notifiche. + token info + + + Please wait for token activation to complete. + Attendi il completamento dell'attivazione del token. + token info + + + Please wait for token to be registered. + Attendi la registrazione del token. + token info + Polish interface Interfaccia polacca No comment provided by engineer. + + Port + Porta + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Probabilmente l'impronta del certificato nell'indirizzo del server è sbagliata server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Conserva la bozza dell'ultimo messaggio, con gli allegati. No comment provided by engineer. - - Preset server - Server preimpostato - No comment provided by engineer. - Preset server address Indirizzo server preimpostato No comment provided by engineer. + + Preset servers + Server preimpostati + No comment provided by engineer. + Preview Anteprima No comment provided by engineer. + + Previously connected servers + Server precedentemente connessi + No comment provided by engineer. + Privacy & security Privacy e sicurezza No comment provided by engineer. + + Privacy for your customers. + Privacy per i tuoi clienti. + No comment provided by engineer. + + + Privacy policy and conditions of use. + Informativa sulla privacy e condizioni d'uso. + No comment provided by engineer. + Privacy redefined Privacy ridefinita No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + Le chat private, i gruppi e i tuoi contatti non sono accessibili agli operatori dei server. + No comment provided by engineer. + Private filenames Nomi di file privati No comment provided by engineer. + + Private media file names. + Nomi privati dei file multimediali. + No comment provided by engineer. + + + Private message routing + Instradamento privato dei messaggi + No comment provided by engineer. + + + Private message routing 🚀 + Instradamento privato dei messaggi 🚀 + No comment provided by engineer. + Private notes Note private name of notes to self + + Private routing + Instradamento privato + No comment provided by engineer. + + + Private routing error + Errore di instradamento privato + No comment provided by engineer. + Profile and server connections Profilo e connessioni al server @@ -4247,14 +5948,9 @@ Errore: %@ Immagine del profilo No comment provided by engineer. - - Profile name - Nome del profilo - No comment provided by engineer. - - - Profile name: - Nome del profilo: + + Profile images + Immagini del profilo No comment provided by engineer. @@ -4262,10 +5958,15 @@ Errore: %@ Password del profilo No comment provided by engineer. + + Profile theme + Tema del profilo + No comment provided by engineer. + Profile update will be sent to your contacts. L'aggiornamento del profilo verrà inviato ai tuoi contatti. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4287,6 +5988,16 @@ Errore: %@ Proibisci le reazioni ai messaggi. No comment provided by engineer. + + Prohibit reporting messages to moderators. + Vieta di segnalare messaggi ai moderatori. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + Vieta l'invio di link di SimpleX. + No comment provided by engineer. + Prohibit sending direct messages to members. Proibisci l'invio di messaggi diretti ai membri. @@ -4307,11 +6018,23 @@ Errore: %@ Proibisci l'invio di messaggi vocali. No comment provided by engineer. + + Protect IP address + Proteggi l'indirizzo IP + No comment provided by engineer. + Protect app screen Proteggi la schermata dell'app No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + Proteggi il tuo indirizzo IP dai relay di messaggistica scelti dai tuoi contatti. +Attivalo nelle impostazioni *Rete e server*. + No comment provided by engineer. + Protect your chat profiles with a password! Proteggi i tuoi profili di chat con una password! @@ -4327,6 +6050,21 @@ Errore: %@ Scadenza del protocollo per KB No comment provided by engineer. + + Proxied + Via proxy + No comment provided by engineer. + + + Proxied servers + Server via proxy + No comment provided by engineer. + + + Proxy requires password + Il proxy richiede una password + No comment provided by engineer. + Push notifications Notifiche push @@ -4334,10 +6072,12 @@ Errore: %@ Push server + Server push No comment provided by engineer. Quantum resistant encryption + Crittografia resistente alla quantistica No comment provided by engineer. @@ -4345,6 +6085,11 @@ Errore: %@ Valuta l'app No comment provided by engineer. + + Reachable chat toolbar + Barra degli strumenti di chat accessibile + No comment provided by engineer. + React… Reagisci… @@ -4353,33 +6098,28 @@ Errore: %@ Read Leggi - No comment provided by engineer. + swipe action Read more Leggi tutto No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Maggiori informazioni nella [Guida per l'utente](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). Leggi di più nella [Guida utente](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Maggiori informazioni nella [Guida per l'utente](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). Maggiori informazioni nella [Guida per l'utente](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - - Read more in our GitHub repository. - Maggiori informazioni nel nostro repository GitHub. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). Maggiori informazioni nel nostro [repository GitHub](https://github.com/simplex-chat/simplex-chat#readme). @@ -4390,6 +6130,11 @@ Errore: %@ Le ricevute sono disattivate No comment provided by engineer. + + Receive errors + Errori di ricezione + No comment provided by engineer. + Received at Ricevuto il @@ -4410,6 +6155,21 @@ Errore: %@ Messaggio ricevuto message info title + + Received messages + Messaggi ricevuti + No comment provided by engineer. + + + Received reply + Risposta ricevuta + No comment provided by engineer. + + + Received total + Totale ricevuto + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. L'indirizzo di ricezione verrà cambiato in un server diverso. La modifica dell'indirizzo verrà completata dopo che il mittente sarà in linea. @@ -4430,16 +6190,46 @@ Errore: %@ Cronologia recente e [bot della directory](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) migliorato. No comment provided by engineer. + + Recipient(s) can't see who this message is from. + I destinatari non possono vedere da chi proviene questo messaggio. + No comment provided by engineer. + Recipients see updates as you type them. I destinatari vedono gli aggiornamenti mentre li digiti. No comment provided by engineer. + + Reconnect + Riconnetti + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Riconnetti tutti i server connessi per imporre il recapito dei messaggi. Utilizza traffico aggiuntivo. No comment provided by engineer. + + Reconnect all servers + Riconnetti tutti i server + No comment provided by engineer. + + + Reconnect all servers? + Riconnettere tutti i server? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + Riconnetti il server per forzare la consegna dei messaggi. Usa traffico aggiuntivo. + No comment provided by engineer. + + + Reconnect server? + Riconnettere il server? + No comment provided by engineer. + Reconnect servers? Riconnettere i server? @@ -4460,10 +6250,26 @@ Errore: %@ Consumo di batteria ridotto No comment provided by engineer. + + Register + Registra + No comment provided by engineer. + + + Register notification token? + Registrare il token di notifica? + token info + + + Registered + Registrato + token status text + Reject Rifiuta - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4490,6 +6296,16 @@ Errore: %@ Rimuovi No comment provided by engineer. + + Remove archive? + Rimuovere l'archivio? + No comment provided by engineer. + + + Remove image + Rimuovi immagine + No comment provided by engineer. + Remove member Rimuovi membro @@ -4527,10 +6343,12 @@ Errore: %@ Repeat download + Ripeti scaricamento No comment provided by engineer. Repeat import + Ripeti importazione No comment provided by engineer. @@ -4540,6 +6358,7 @@ Errore: %@ Repeat upload + Ripeti caricamento No comment provided by engineer. @@ -4547,6 +6366,56 @@ Errore: %@ Rispondi chat item action + + Report + Segnala + chat item action + + + Report content: only group moderators will see it. + Segnala contenuto: solo i moderatori del gruppo lo vedranno. + report reason + + + Report member profile: only group moderators will see it. + Segnala profilo: solo i moderatori del gruppo lo vedranno. + report reason + + + Report other: only group moderators will see it. + Segnala altro: solo i moderatori del gruppo lo vedranno. + report reason + + + Report reason? + Motivo della segnalazione? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + Segnala spam: solo i moderatori del gruppo lo vedranno. + report reason + + + Report violation: only group moderators will see it. + Segnala violazione: solo i moderatori del gruppo lo vedranno. + report reason + + + Report: %@ + Segnalazione: %@ + report in notification + + + Reporting messages to moderators is prohibited. + È vietato segnalare messaggi ai moderatori. + No comment provided by engineer. + + + Reports + Segnalazioni + No comment provided by engineer. + Required Obbligatorio @@ -4557,16 +6426,41 @@ Errore: %@ Ripristina No comment provided by engineer. + + Reset all hints + Ripristina tutti i suggerimenti + No comment provided by engineer. + + + Reset all statistics + Azzera tutte le statistiche + No comment provided by engineer. + + + Reset all statistics? + Azzerare tutte le statistiche? + No comment provided by engineer. + Reset colors Ripristina i colori No comment provided by engineer. + + Reset to app theme + Ripristina al tema dell'app + No comment provided by engineer. + Reset to defaults Ripristina i predefiniti No comment provided by engineer. + + Reset to user theme + Ripristina al tema dell'utente + No comment provided by engineer. + Restart the app to create a new chat profile Riavvia l'app per creare un nuovo profilo di chat @@ -4607,9 +6501,9 @@ Errore: %@ Rivela chat item action - - Revert - Ripristina + + Review conditions + Leggi le condizioni No comment provided by engineer. @@ -4637,55 +6531,67 @@ Errore: %@ Avvia chat No comment provided by engineer. - - SMP servers + + SMP server Server SMP No comment provided by engineer. + + SOCKS proxy + Proxy SOCKS + No comment provided by engineer. + + + Safely receive files + Ricevi i file in sicurezza + No comment provided by engineer. + Safer groups + Gruppi più sicuri No comment provided by engineer. Save Salva - chat item action + alert button +chat item action Save (and notify contacts) Salva (e avvisa i contatti) - No comment provided by engineer. + alert button Save and notify contact Salva e avvisa il contatto - No comment provided by engineer. + alert button Save and notify group members Salva e avvisa i membri del gruppo No comment provided by engineer. + + Save and reconnect + Salva e riconnetti + No comment provided by engineer. + Save and update group profile Salva e aggiorna il profilo del gruppo No comment provided by engineer. - - Save archive - Salva archivio - No comment provided by engineer. - - - Save auto-accept settings - Salva le impostazioni di accettazione automatica - No comment provided by engineer. - Save group profile Salva il profilo del gruppo No comment provided by engineer. + + Save list + Salva elenco + No comment provided by engineer. + Save passphrase and open chat Salva la password e apri la chat @@ -4699,7 +6605,7 @@ Errore: %@ Save preferences? Salvare le preferenze? - No comment provided by engineer. + alert title Save profile password @@ -4714,28 +6620,53 @@ Errore: %@ Save servers? Salvare i server? - No comment provided by engineer. - - - Save settings? - Salvare le impostazioni? - No comment provided by engineer. + alert title Save welcome message? Salvare il messaggio di benvenuto? No comment provided by engineer. + + Save your profile? + Salvare il profilo? + alert title + + + Saved + Salvato + No comment provided by engineer. + Saved WebRTC ICE servers will be removed I server WebRTC ICE salvati verranno rimossi No comment provided by engineer. + + Saved from + Salvato da + No comment provided by engineer. + Saved message Messaggio salvato message info title + + Saving %lld messages + Salvataggio di %lld messaggi + No comment provided by engineer. + + + Scale + Scala + No comment provided by engineer. + + + Scan / Paste link + Scansiona / Incolla link + No comment provided by engineer. + Scan QR code Scansiona codice QR @@ -4776,11 +6707,21 @@ Errore: %@ Cerca o incolla un link SimpleX No comment provided by engineer. + + Secondary + Secondario + No comment provided by engineer. + Secure queue Coda sicura server test step + + Secured + Protetto + No comment provided by engineer. + Security assessment Valutazione della sicurezza @@ -4794,6 +6735,21 @@ Errore: %@ Select Seleziona + chat item action + + + Select chat profile + Seleziona il profilo di chat + No comment provided by engineer. + + + Selected %lld + %lld selezionato + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Le preferenze della chat selezionata vietano questo messaggio. No comment provided by engineer. @@ -4831,11 +6787,6 @@ Errore: %@ Invia ricevute di consegna a No comment provided by engineer. - - Send direct message - Invia messaggio diretto - No comment provided by engineer. - Send direct message to connect Invia messaggio diretto per connetterti @@ -4846,9 +6797,14 @@ Errore: %@ Invia messaggio a tempo No comment provided by engineer. + + Send errors + Errori di invio + No comment provided by engineer. + Send link previews - Invia anteprime dei link + Invia le anteprime dei link No comment provided by engineer. @@ -4856,14 +6812,29 @@ Errore: %@ Invia messaggio in diretta No comment provided by engineer. + + Send message to enable calls. + Invia un messaggio per attivare le chiamate. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + Invia messaggi direttamente quando l'indirizzo IP è protetto e il tuo server o quello di destinazione non supporta l'instradamento privato. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + Invia messaggi direttamente quando il tuo server o quello di destinazione non supporta l'instradamento privato. + No comment provided by engineer. + Send notifications Invia notifiche No comment provided by engineer. - - Send notifications: - Invia notifiche: + + Send private reports + Invia segnalazioni private No comment provided by engineer. @@ -4889,7 +6860,7 @@ Errore: %@ Sender cancelled file transfer. Il mittente ha annullato il trasferimento del file. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4946,6 +6917,11 @@ Errore: %@ Inviato il: %@ copied message info + + Sent directly + Inviato direttamente + No comment provided by engineer. + Sent file event Evento file inviato @@ -4956,11 +6932,71 @@ Errore: %@ Messaggio inviato message info title + + Sent messages + Messaggi inviati + No comment provided by engineer. + Sent messages will be deleted after set time. I messaggi inviati verranno eliminati dopo il tempo impostato. No comment provided by engineer. + + Sent reply + Risposta inviata + No comment provided by engineer. + + + Sent total + Totale inviato + No comment provided by engineer. + + + Sent via proxy + Inviato via proxy + No comment provided by engineer. + + + Server + Server + No comment provided by engineer. + + + Server added to operator %@. + Server aggiunto all'operatore %@. + alert message + + + Server address + Indirizzo server + No comment provided by engineer. + + + Server address is incompatible with network settings. + L'indirizzo del server non è compatibile con le impostazioni di rete. + srv error text. + + + Server address is incompatible with network settings: %@. + L'indirizzo del server è incompatibile con le impostazioni di rete: %@. + No comment provided by engineer. + + + Server operator changed. + L'operatore del server è cambiato. + alert title + + + Server operators + Operatori server + No comment provided by engineer. + + + Server protocol changed. + Il protocollo del server è cambiato. + alert title + Server requires authorization to create queues, check password Il server richiede l'autorizzazione di creare code, controlla la password @@ -4976,11 +7012,36 @@ Errore: %@ Test del server fallito! No comment provided by engineer. + + Server type + Tipo server + No comment provided by engineer. + + + Server version is incompatible with network settings. + La versione del server non è compatibile con le impostazioni di rete. + srv error text + + + Server version is incompatible with your app: %@. + La versione del server è incompatibile con la tua app: %@. + No comment provided by engineer. + Servers Server No comment provided by engineer. + + Servers info + Info dei server + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + Le statistiche dei server verranno azzerate - è irreversibile! + No comment provided by engineer. + Session code Codice di sessione @@ -4991,11 +7052,21 @@ Errore: %@ Imposta 1 giorno No comment provided by engineer. + + Set chat name… + Imposta il nome della chat… + No comment provided by engineer. + Set contact name… Imposta nome del contatto… No comment provided by engineer. + + Set default theme + Imposta tema predefinito + No comment provided by engineer. + Set group preferences Imposta le preferenze del gruppo @@ -5006,6 +7077,11 @@ Errore: %@ Impostalo al posto dell'autenticazione di sistema. No comment provided by engineer. + + Set message expiration in chats. + Imposta la scadenza dei messaggi nelle chat. + No comment provided by engineer. + Set passcode Imposta codice @@ -5013,6 +7089,7 @@ Errore: %@ Set passphrase + Imposta password No comment provided by engineer. @@ -5035,24 +7112,55 @@ Errore: %@ Impostazioni No comment provided by engineer. + + Settings were changed. + Le impostazioni sono state cambiate. + alert message + + + Shape profile images + Forma delle immagini del profilo + No comment provided by engineer. + Share Condividi - chat item action + alert action +chat item action Share 1-time link Condividi link una tantum No comment provided by engineer. + + Share 1-time link with a friend + Condividi link una tantum con un amico + No comment provided by engineer. + + + Share SimpleX address on social media. + Condividi l'indirizzo SimpleX sui social media. + No comment provided by engineer. + Share address Condividi indirizzo No comment provided by engineer. + + Share address publicly + Condividi indirizzo pubblicamente + No comment provided by engineer. + Share address with contacts? Condividere l'indirizzo con i contatti? + alert title + + + Share from other apps. + Condividi da altre app. No comment provided by engineer. @@ -5060,18 +7168,34 @@ Errore: %@ Condividi link No comment provided by engineer. + + Share profile + Condividi il profilo + No comment provided by engineer. + Share this 1-time invite link Condividi questo link di invito una tantum No comment provided by engineer. + + Share to SimpleX + Condividi in SimpleX + No comment provided by engineer. + Share with contacts Condividi con i contatti No comment provided by engineer. + + Short link + Link breve + No comment provided by engineer. + Show QR code + Mostra codice QR No comment provided by engineer. @@ -5089,21 +7213,46 @@ Errore: %@ Mostra ultimi messaggi No comment provided by engineer. + + Show message status + Mostra stato del messaggio + No comment provided by engineer. + + + Show percentage + Mostra percentuale + No comment provided by engineer. + Show preview Mostra anteprima No comment provided by engineer. + + Show → on messages sent via private routing. + Mostra → nei messaggi inviati via instradamento privato. + No comment provided by engineer. + Show: Mostra: No comment provided by engineer. + + SimpleX + SimpleX + No comment provided by engineer. + SimpleX Address Indirizzo SimpleX No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + SimpleX Chat e Flux hanno concluso un accordo per includere server gestiti da Flux nell'app. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. La sicurezza di SimpleX Chat è stata verificata da Trail of Bits. @@ -5134,6 +7283,21 @@ Errore: %@ Indirizzo SimpleX No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + L'indirizzo SimpleX e i link una tantum sono sicuri da condividere tramite qualsiasi messenger. + No comment provided by engineer. + + + SimpleX address or 1-time link? + Indirizzo SimpleX o link una tantum? + No comment provided by engineer. + + + SimpleX channel link + Link del canale SimpleX + simplex link type + SimpleX contact address Indirizzo di contatto SimpleX @@ -5152,6 +7316,16 @@ Errore: %@ SimpleX links Link di SimpleX + chat feature + + + SimpleX links are prohibited. + I link di SimpleX sono vietati in questo gruppo. + No comment provided by engineer. + + + SimpleX links not allowed + Link di SimpleX non consentiti No comment provided by engineer. @@ -5159,11 +7333,21 @@ Errore: %@ Invito SimpleX una tantum simplex link type + + SimpleX protocols reviewed by Trail of Bits. + Protocolli di SimpleX esaminati da Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Modalità incognito semplificata No comment provided by engineer. + + Size + Dimensione + No comment provided by engineer. + Skip Salta @@ -5179,16 +7363,54 @@ Errore: %@ Piccoli gruppi (max 20) No comment provided by engineer. + + Soft + Leggera + blur media + + + Some app settings were not migrated. + Alcune impostazioni dell'app non sono state migrate. + No comment provided by engineer. + + + Some file(s) were not exported: + Alcuni file non sono stati esportati: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. Si sono verificati alcuni errori non gravi durante l'importazione: vedi la console della chat per i dettagli. No comment provided by engineer. + + Some non-fatal errors occurred during import: + Si sono verificati alcuni errori non fatali durante l'importazione: + No comment provided by engineer. + + + Some servers failed the test: +%@ + Alcuni server hanno fallito il test: +%@ + alert message + Somebody Qualcuno notification title + + Spam + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + Quadrata, circolare o qualsiasi forma tra le due. + No comment provided by engineer. + Start chat Avvia chat @@ -5204,6 +7426,16 @@ Errore: %@ Avvia la migrazione No comment provided by engineer. + + Starting from %@. + Inizio da %@. + No comment provided by engineer. + + + Statistics + Statistiche + No comment provided by engineer. + Stop Ferma @@ -5216,11 +7448,7 @@ Errore: %@ Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - Ferma la chat per attivare le azioni del database + Ferma la chat No comment provided by engineer. @@ -5251,27 +7479,63 @@ Errore: %@ Stop sharing Smetti di condividere - No comment provided by engineer. + alert action Stop sharing address? Smettere di condividere l'indirizzo? - No comment provided by engineer. + alert title Stopping chat + Arresto della chat No comment provided by engineer. + + Storage + Archiviazione + No comment provided by engineer. + + + Strong + Forte + blur media + Submit Invia No comment provided by engineer. + + Subscribed + Iscritto + No comment provided by engineer. + + + Subscription errors + Errori di iscrizione + No comment provided by engineer. + + + Subscriptions ignored + Iscrizioni ignorate + No comment provided by engineer. + Support SimpleX Chat Supporta SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Cambia tra audio e video durante la chiamata. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Cambia profilo di chat per inviti una tantum. + No comment provided by engineer. + System Sistema @@ -5282,11 +7546,21 @@ Errore: %@ Autenticazione di sistema No comment provided by engineer. + + TCP connection + Connessione TCP + No comment provided by engineer. + TCP connection timeout Scadenza connessione TCP No comment provided by engineer. + + TCP port for messaging + Porta TCP per i messaggi + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5302,11 +7576,21 @@ Errore: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + Coda + No comment provided by engineer. + Take picture Scatta foto No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + Tocca Crea indirizzo SimpleX nel menu per crearlo più tardi. + No comment provided by engineer. + Tap button Tocca il pulsante @@ -5342,16 +7626,21 @@ Errore: %@ Tocca per scansionare No comment provided by engineer. - - Tap to start a new chat - Tocca per iniziare una chat - No comment provided by engineer. + + Temporary file error + Errore del file temporaneo + file error alert title Test failed at step %@. Test fallito al passo %@. server test failure + + Test notifications + Prova le notifiche + No comment provided by engineer. + Test server Prova server @@ -5365,7 +7654,7 @@ Errore: %@ Tests failed! Test falliti! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5382,11 +7671,6 @@ Errore: %@ Grazie agli utenti – contribuite via Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - La prima piattaforma senza alcun identificatore utente – privata by design. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5399,6 +7683,16 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.L'app può avvisarti quando ricevi messaggi o richieste di contatto: apri le impostazioni per attivare. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + L'app protegge la tua privacy usando diversi operatori in ogni conversazione. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + L'app chiederà di confermare i download da server di file sconosciuti (eccetto .onion). + No comment provided by engineer. + The attempt to change database passphrase was not completed. Il tentativo di cambiare la password del database non è stato completato. @@ -5409,6 +7703,11 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.Il codice che hai scansionato non è un codice QR di link SimpleX. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + La connessione ha raggiunto il limite di messaggi non consegnati, il contatto potrebbe essere offline. + No comment provided by engineer. + The connection you accepted will be cancelled! La connessione che hai accettato verrà annullata! @@ -5429,6 +7728,11 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.La crittografia funziona e il nuovo accordo sulla crittografia non è richiesto. Potrebbero verificarsi errori di connessione! No comment provided by engineer. + + The future of messaging + La nuova generazione di messaggistica privata + No comment provided by engineer. + The hash of the previous message is different. L'hash del messaggio precedente è diverso. @@ -5444,9 +7748,14 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.Il messaggio sarà segnato come moderato per tutti i membri. No comment provided by engineer. - - The next generation of private messaging - La nuova generazione di messaggistica privata + + The messages will be deleted for all members. + I messaggi verranno eliminati per tutti i membri. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. + I messaggi verranno contrassegnati come moderati per tutti i membri. No comment provided by engineer. @@ -5454,9 +7763,14 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.Il database vecchio non è stato rimosso durante la migrazione, può essere eliminato. No comment provided by engineer. - - The profile is only shared with your contacts. - Il profilo è condiviso solo con i tuoi contatti. + + The same conditions will apply to operator **%@**. + Le stesse condizioni si applicheranno all'operatore **%@**. + No comment provided by engineer. + + + The second preset operator in the app! + Il secondo operatore preimpostato nell'app! No comment provided by engineer. @@ -5474,14 +7788,29 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.I server per le nuove connessioni del profilo di chat attuale **%@**. No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + I server per nuovi file del tuo profilo di chat attuale **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. Il testo che hai incollato non è un link SimpleX. No comment provided by engineer. - - Theme - Tema + + The uploaded database archive will be permanently removed from the servers. + L'archivio del database caricato verrà rimosso definitivamente dai server. + No comment provided by engineer. + + + Themes + Temi + No comment provided by engineer. + + + These conditions will also apply for: **%@**. + Queste condizioni si applicheranno anche per: **%@**. No comment provided by engineer. @@ -5504,6 +7833,11 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.Questa azione non può essere annullata: i messaggi inviati e ricevuti prima di quanto selezionato verranno eliminati. Potrebbe richiedere diversi minuti. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + Questa azione non è reversibile: i messaggi inviati e ricevuti in questa chat prima della selezione verranno eliminati. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. Questa azione non può essere annullata: il tuo profilo, i contatti, i messaggi e i file andranno persi in modo irreversibile. @@ -5511,10 +7845,12 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa. This chat is protected by end-to-end encryption. + Questa chat è protetta da crittografia end-to-end. E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + Questa chat è protetta da crittografia end-to-end resistente alla quantistica. E2EE info chat item @@ -5547,11 +7883,31 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.Questo è il tuo link una tantum! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + Questo link richiede una versione più recente dell'app. Aggiornala o chiedi al tuo contatto di inviare un link compatibile. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + Questo link è stato usato con un altro dispositivo mobile, creane uno nuovo sul desktop. + No comment provided by engineer. + + + This message was deleted or not received yet. + Questo messaggio è stato eliminato o non ancora ricevuto. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. Questa impostazione si applica ai messaggi del profilo di chat attuale **%@**. No comment provided by engineer. + + Title + Titoli + No comment provided by engineer. + To ask any questions and to receive updates: Per porre domande e ricevere aggiornamenti: @@ -5572,9 +7928,9 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.Per creare una nuova connessione No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Per proteggere la privacy, invece degli ID utente utilizzati da tutte le altre piattaforme, SimpleX ha identificatori per le code di messaggi, separati per ciascuno dei tuoi contatti. + + To protect against your link being replaced, you can compare contact security codes. + Per proteggerti dalla sostituzione del tuo link, puoi confrontare i codici di sicurezza del contatto. No comment provided by engineer. @@ -5582,6 +7938,11 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.Per proteggere il fuso orario, i file immagine/vocali usano UTC. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + Per proteggere il tuo indirizzo IP, l'instradamento privato usa i tuoi server SMP per consegnare i messaggi. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5589,6 +7950,26 @@ You will be prompted to complete authentication before this feature is enabled.< Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzionalità. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Per proteggere la privacy, invece degli ID utente utilizzati da tutte le altre piattaforme, SimpleX ha identificatori per le code di messaggi, separati per ciascuno dei tuoi contatti. + No comment provided by engineer. + + + To receive + Per ricevere + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + Per registrare l'audio, concedi l'autorizzazione di usare il microfono. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Per registrare il video, concedi l'autorizzazione di usare la fotocamera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Per registrare un messaggio vocale, concedi l'autorizzazione all'uso del microfono. @@ -5599,26 +7980,61 @@ Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzio Per rivelare il tuo profilo nascosto, inserisci una password completa in un campo di ricerca nella pagina **I tuoi profili di chat**. No comment provided by engineer. + + To send + Per inviare + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. Per supportare le notifiche push istantanee, il database della chat deve essere migrato. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + Per usare i server di **%@**, accetta le condizioni d'uso. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. Per verificare la crittografia end-to-end con il tuo contatto, confrontate (o scansionate) il codice sui vostri dispositivi. No comment provided by engineer. + + Toggle chat list: + Cambia l'elenco delle chat: + No comment provided by engineer. + Toggle incognito when connecting. Attiva/disattiva l'incognito quando ti colleghi. No comment provided by engineer. + + Token status: %@. + Stato del token: %@. + token status + + + Toolbar opacity + Opacità barra degli strumenti + No comment provided by engineer. + + + Total + Totale + No comment provided by engineer. + Transport isolation Isolamento del trasporto No comment provided by engineer. + + Transport sessions + Sessioni di trasporto + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Tentativo di connessione al server usato per ricevere messaggi da questo contatto (errore: %@). @@ -5674,10 +8090,10 @@ Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzio Sbloccare il membro? No comment provided by engineer. - - Unexpected error: %@ - Errore imprevisto: % @ - item status description + + Undelivered messages + Messaggi non consegnati + No comment provided by engineer. Unexpected migration state @@ -5687,7 +8103,7 @@ Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzio Unfav. Non pref. - No comment provided by engineer. + swipe action Unhide @@ -5724,6 +8140,11 @@ Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzio Errore sconosciuto No comment provided by engineer. + + Unknown servers! + Server sconosciuti! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. A meno che non utilizzi l'interfaccia di chiamata iOS, attiva la modalità Non disturbare per evitare interruzioni. @@ -5759,11 +8180,16 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Unmute Riattiva notifiche - No comment provided by engineer. + notification label action Unread Non letto + swipe action + + + Unsupported connection link + Link di connessione non supportato No comment provided by engineer. @@ -5776,11 +8202,6 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Aggiorna No comment provided by engineer. - - Update .onion hosts setting? - Aggiornare l'impostazione degli host .onion? - No comment provided by engineer. - Update database passphrase Aggiorna la password del database @@ -5791,9 +8212,14 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Aggiornare le impostazioni di rete? No comment provided by engineer. - - Update transport isolation mode? - Aggiornare la modalità di isolamento del trasporto? + + Update settings? + Aggiornare le impostazioni? + No comment provided by engineer. + + + Updated conditions + Condizioni aggiornate No comment provided by engineer. @@ -5801,18 +8227,19 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e L'aggiornamento delle impostazioni riconnetterà il client a tutti i server. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - L'aggiornamento di questa impostazione riconnetterà il client a tutti i server. - No comment provided by engineer. - Upgrade and open chat Aggiorna e apri chat No comment provided by engineer. + + Upload errors + Errori di invio + No comment provided by engineer. + Upload failed + Invio fallito No comment provided by engineer. @@ -5820,8 +8247,24 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Invia file server test step + + Uploaded + Inviato + No comment provided by engineer. + + + Uploaded files + File inviati + No comment provided by engineer. + Uploading archive + Invio dell'archivio + No comment provided by engineer. + + + Use %@ + Usa %@ No comment provided by engineer. @@ -5829,11 +8272,26 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Usa gli host .onion No comment provided by engineer. + + Use SOCKS proxy + Usa proxy SOCKS + No comment provided by engineer. + Use SimpleX Chat servers? Usare i server di SimpleX Chat? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + Usa la porta TCP %@ quando non è specificata alcuna porta. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + Usa la porta TCP 443 solo per i server preimpostati. + No comment provided by engineer. + Use chat Usa la chat @@ -5844,6 +8302,16 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Usa il profilo attuale No comment provided by engineer. + + Use for files + Usa per i file + No comment provided by engineer. + + + Use for messages + Usa per i messaggi + No comment provided by engineer. + Use for new connections Usa per connessioni nuove @@ -5869,23 +8337,54 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Usare solo notifiche locali? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + Usa l'instradamento privato con server sconosciuti quando l'indirizzo IP non è protetto. + No comment provided by engineer. + + + Use private routing with unknown servers. + Usa l'instradamento privato con server sconosciuti. + No comment provided by engineer. + Use server Usa il server No comment provided by engineer. + + Use servers + Usa i server + No comment provided by engineer. + + + Use short links (BETA) + Usa link brevi (BETA) + No comment provided by engineer. + Use the app while in the call. + Usa l'app mentre sei in chiamata. No comment provided by engineer. - - User profile - Profilo utente + + Use the app with one hand. + Usa l'app con una mano sola. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - L'uso di host .onion richiede un fornitore di VPN compatibile. + + Use web port + Usa porta web + No comment provided by engineer. + + + User selection + Selezione utente + No comment provided by engineer. + + + Username + Nome utente No comment provided by engineer. @@ -5915,10 +8414,12 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Verify database passphrase + Verifica password del database No comment provided by engineer. Verify passphrase + Verifica password No comment provided by engineer. @@ -5956,11 +8457,21 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Video e file fino a 1 GB No comment provided by engineer. + + View conditions + Vedi le condizioni + No comment provided by engineer. + View security code Vedi codice di sicurezza No comment provided by engineer. + + View updated conditions + Vedi le condizioni aggiornate + No comment provided by engineer. + Visible history Cronologia visibile @@ -5976,11 +8487,16 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e I messaggi vocali sono vietati in questa chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. I messaggi vocali sono vietati in questo gruppo. No comment provided by engineer. + + Voice messages not allowed + Messaggi vocali non consentiti + No comment provided by engineer. + Voice messages prohibited! Messaggi vocali vietati! @@ -6011,8 +8527,19 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e In attesa del video No comment provided by engineer. + + Wallpaper accent + Tinta dello sfondo + No comment provided by engineer. + + + Wallpaper background + Retro dello sfondo + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures + Attenzione: avviare la chat su più dispositivi non è supportato e provocherà problemi di recapito dei messaggi No comment provided by engineer. @@ -6037,6 +8564,7 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Welcome message is too long + Il messaggio di benvenuto è troppo lungo No comment provided by engineer. @@ -6049,9 +8577,14 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Quando disponibili No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Quando le persone chiedono di connettersi, puoi accettare o rifiutare. + + When connecting audio and video calls. + Quando si connettono le chiamate audio e video. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. + Quando più di un operatore è attivato, nessuno di essi ha metadati per scoprire chi comunica con chi. No comment provided by engineer. @@ -6059,6 +8592,21 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Quando condividi un profilo in incognito con qualcuno, questo profilo verrà utilizzato per i gruppi a cui ti invitano. No comment provided by engineer. + + WiFi + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + Viene attivata nelle chat dirette! + No comment provided by engineer. + + + Wired ethernet + Cavo ethernet + No comment provided by engineer. + With encrypted files and media. Con file e multimediali criptati. @@ -6074,28 +8622,44 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Con consumo di batteria ridotto. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + Senza Tor o VPN, il tuo indirizzo IP sarà visibile ai server di file. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + Senza Tor o VPN, il tuo indirizzo IP sarà visibile a questi relay XFTP: %@. + alert message + Wrong database passphrase Password del database sbagliata No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + Chiave sbagliata o connessione sconosciuta - molto probabilmente questa connessione è stata eliminata. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + Chiave sbagliata o indirizzo sconosciuto per frammento del file - probabilmente il file è stato eliminato. + file error text + Wrong passphrase! Password sbagliata! No comment provided by engineer. - - XFTP servers + + XFTP server Server XFTP No comment provided by engineer. - - You - Tu - No comment provided by engineer. - You **must not** use the same database on two devices. + **Non devi** usare lo stesso database su due dispositivi. No comment provided by engineer. @@ -6118,6 +8682,11 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Sei già connesso/a a %@. No comment provided by engineer. + + You are already connected with %@. + Sei già connesso/a con %@. + No comment provided by engineer. + You are already connecting to %@. Ti stai già connettendo a %@. @@ -6165,11 +8734,26 @@ Ripetere la richiesta di ingresso? Sei stato/a invitato/a al gruppo No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + Non sei connesso/a a questi server. L'instradamento privato è usato per consegnare loro i messaggi. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. Puoi accettare chiamate dalla schermata di blocco, senza l'autenticazione del dispositivo e dell'app. No comment provided by engineer. + + You can change it in Appearance settings. + Puoi cambiarlo nelle impostazioni dell'aspetto. + No comment provided by engineer. + + + You can configure servers via settings. + Puoi configurare i server nelle impostazioni. + No comment provided by engineer. + You can create it later Puoi crearlo più tardi @@ -6187,6 +8771,7 @@ Ripetere la richiesta di ingresso? You can give another try. + Puoi fare un altro tentativo. No comment provided by engineer. @@ -6199,11 +8784,21 @@ Ripetere la richiesta di ingresso? Puoi renderlo visibile ai tuoi contatti SimpleX nelle impostazioni. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ Ora puoi inviare messaggi a %@ notification body + + You can send messages to %@ from Archived contacts. + Puoi inviare messaggi a %@ dai contatti archiviati. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + Puoi impostare il nome della connessione per ricordare con chi è stato condiviso il link. + No comment provided by engineer. + You can set lock screen notification preview via settings. Puoi impostare l'anteprima della notifica nella schermata di blocco tramite le impostazioni. @@ -6219,16 +8814,16 @@ Ripetere la richiesta di ingresso? Puoi condividere questo indirizzo con i tuoi contatti per consentire loro di connettersi con **%@**. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - Puoi condividere il tuo indirizzo come link o come codice QR: chiunque potrà connettersi a te. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app Puoi avviare la chat via Impostazioni / Database o riavviando l'app No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + Puoi ancora vedere la conversazione con %@ nell'elenco delle chat. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. Puoi attivare SimpleX Lock tramite le impostazioni. @@ -6242,23 +8837,23 @@ Ripetere la richiesta di ingresso? You can view invitation link again in connection details. Puoi vedere di nuovo il link di invito nei dettagli di connessione. - No comment provided by engineer. + alert message You can't send messages! Non puoi inviare messaggi! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Tu decidi attraverso quale/i server **ricevere** i messaggi, i tuoi contatti quali server usi per inviare loro i messaggi. - No comment provided by engineer. - You could not be verified; please try again. Non è stato possibile verificarti, riprova. No comment provided by engineer. + + You decide who can connect. + Sei tu a decidere chi può connettersi. + No comment provided by engineer. + You have already requested connection via this address! Hai già richiesto la connessione tramite questo indirizzo! @@ -6271,11 +8866,6 @@ Repeat connection request? Ripetere la richiesta di connessione? No comment provided by engineer. - - You have no chats - Non hai chat - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. Devi inserire la password ogni volta che si avvia l'app: non viene memorizzata sul dispositivo. @@ -6296,11 +8886,26 @@ Ripetere la richiesta di connessione? Sei entrato/a in questo gruppo. Connessione al membro del gruppo invitante. No comment provided by engineer. + + You may migrate the exported database. + Puoi migrare il database esportato. + No comment provided by engineer. + + + You may save the exported archive. + Puoi salvare l'archivio esportato. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. Devi usare la versione più recente del tuo database della chat SOLO su un dispositivo, altrimenti potresti non ricevere più i messaggi da alcuni contatti. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + Devi consentire le chiamate al tuo contatto per poterlo chiamare. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. Devi consentire al tuo contatto di inviare messaggi vocali per poterli inviare anche tu. @@ -6316,6 +8921,11 @@ Ripetere la richiesta di connessione? Hai inviato un invito al gruppo No comment provided by engineer. + + You should receive notifications. + Dovresti ricevere le notifiche. + token info + You will be connected to group when the group host's device is online, please wait or check later! Verrai connesso/a al gruppo quando il dispositivo dell'host del gruppo sarà in linea, attendi o controlla più tardi! @@ -6351,6 +8961,11 @@ Ripetere la richiesta di connessione? Continuerai a ricevere chiamate e notifiche da profili silenziati quando sono attivi. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + Non riceverai più messaggi da questa chat. La cronologia della chat verrà conservata. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Non riceverai più messaggi da questo gruppo. La cronologia della chat verrà conservata. @@ -6371,31 +8986,16 @@ Ripetere la richiesta di connessione? Stai usando un profilo in incognito per questo gruppo: per impedire la condivisione del tuo profilo principale non è consentito invitare contatti No comment provided by engineer. - - Your %@ servers - I tuoi server %@ - No comment provided by engineer. - Your ICE servers I tuoi server ICE No comment provided by engineer. - - Your SMP servers - I tuoi server SMP - No comment provided by engineer. - Your SimpleX address Il tuo indirizzo SimpleX No comment provided by engineer. - - Your XFTP servers - I tuoi server XFTP - No comment provided by engineer. - Your calls Le tue chiamate @@ -6411,16 +9011,19 @@ Ripetere la richiesta di connessione? Il tuo database della chat non è crittografato: imposta la password per crittografarlo. No comment provided by engineer. + + Your chat preferences + Le tue preferenze della chat + alert title + Your chat profiles I tuoi profili di chat No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Il tuo contatto deve essere in linea per completare la connessione. -Puoi annullare questa connessione e rimuovere il contatto (e riprovare più tardi con un link nuovo). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + La tua connessione è stata spostata a %@, ma si è verificato un errore imprevisto durante il reindirizzamento al profilo. No comment provided by engineer. @@ -6438,6 +9041,11 @@ Puoi annullare questa connessione e rimuovere il contatto (e riprovare più tard I tuoi contatti resteranno connessi. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Le credenziali potrebbero essere inviate in chiaro. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Il tuo attuale database della chat verrà ELIMINATO e SOSTITUITO con quello importato. @@ -6468,33 +9076,36 @@ Puoi annullare questa connessione e rimuovere il contatto (e riprovare più tard Verrà condiviso il tuo profilo **%@**. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Il tuo profilo è memorizzato sul tuo dispositivo e condiviso solo con i tuoi contatti. -I server di SimpleX non possono vedere il tuo profilo. + + Your profile is stored on your device and only shared with your contacts. + Il profilo è condiviso solo con i tuoi contatti. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Il tuo profilo, i contatti e i messaggi recapitati sono memorizzati sul tuo dispositivo. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Il tuo profilo è memorizzato sul tuo dispositivo e condiviso solo con i tuoi contatti. I server di SimpleX non possono vedere il tuo profilo. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Il tuo profilo è stato cambiato. Se lo salvi, il profilo aggiornato verrà inviato a tutti i tuoi contatti. + alert message + Your random profile Il tuo profilo casuale No comment provided by engineer. - - Your server - Il tuo server - No comment provided by engineer. - Your server address L'indirizzo del tuo server No comment provided by engineer. + + Your servers + I tuoi server + No comment provided by engineer. + Your settings Le tue impostazioni @@ -6535,11 +9146,21 @@ I server di SimpleX non possono vedere il tuo profilo. chiamata accettata call status + + accepted invitation + invito accettato + chat list item title + admin amministratore member role + + admins + amministratori + feature role + agreeing encryption for %@… concordando la crittografia per %@… @@ -6550,6 +9171,11 @@ I server di SimpleX non possono vedere il tuo profilo. concordando la crittografia… chat item text + + all members + tutti i membri + feature role + always sempre @@ -6560,6 +9186,16 @@ I server di SimpleX non possono vedere il tuo profilo. e altri %lld eventi No comment provided by engineer. + + archived report + segnalazione archiviata + No comment provided by engineer. + + + attempts + tentativi + No comment provided by engineer. + audio call (not e2e encrypted) chiamata audio (non crittografata e2e) @@ -6593,13 +9229,19 @@ I server di SimpleX non possono vedere il tuo profilo. blocked by admin bloccato dall'amministratore - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold grassetto No comment provided by engineer. + + call + chiama + No comment provided by engineer. + call error errore di chiamata @@ -6632,7 +9274,7 @@ I server di SimpleX non possono vedere il tuo profilo. changed your role to %@ - cambiato il tuo ruolo in %@ + ha cambiato il tuo ruolo in %@ rcv group event chat item @@ -6703,7 +9345,7 @@ I server di SimpleX non possono vedere il tuo profilo. connecting… in connessione… - chat list item title + No comment provided by engineer. connection established @@ -6750,10 +9392,16 @@ I server di SimpleX non possono vedere il tuo profilo. giorni time unit + + decryption errors + errori di decifrazione + No comment provided by engineer. + default (%@) predefinito (%@) - pref value + delete after time +pref value default (no) @@ -6800,6 +9448,11 @@ I server di SimpleX non possono vedere il tuo profilo. messaggio duplicato integrity error chat item + + duplicates + doppi + No comment provided by engineer. + e2e encrypted crittografato e2e @@ -6875,9 +9528,14 @@ I server di SimpleX non possono vedere il tuo profilo. errore No comment provided by engineer. - - event happened - evento accaduto + + expired + scaduto + No comment provided by engineer. + + + forwarded + inoltrato No comment provided by engineer. @@ -6905,6 +9563,11 @@ I server di SimpleX non possono vedere il tuo profilo. Il portachiavi di iOS verrà usato per archiviare in modo sicuro la password dopo il riavvio dell'app o la modifica della password; consentirà di ricevere notifiche push. No comment provided by engineer. + + inactive + inattivo + No comment provided by engineer. + incognito via contact address link incognito via link indirizzo del contatto @@ -6945,6 +9608,11 @@ I server di SimpleX non possono vedere il tuo profilo. invito al gruppo %@ group name + + invite + invita + No comment provided by engineer. + invited ha invitato @@ -6992,7 +9660,7 @@ I server di SimpleX non possono vedere il tuo profilo. member %1$@ changed to %2$@ - membro %1$@ cambiato in %2$@ + il membro %1$@ è diventato %2$@ profile update event chat item @@ -7000,6 +9668,11 @@ I server di SimpleX non possono vedere il tuo profilo. si è connesso/a rcv group event chat item + + message + messaggio + No comment provided by engineer. + message received messaggio ricevuto @@ -7025,6 +9698,11 @@ I server di SimpleX non possono vedere il tuo profilo. moderato da %@ marked deleted chat item preview text + + moderator + moderatore + member role + months mesi @@ -7033,7 +9711,7 @@ I server di SimpleX non possono vedere il tuo profilo. never mai - No comment provided by engineer. + delete after time new message @@ -7064,8 +9742,8 @@ I server di SimpleX non possono vedere il tuo profilo. off off enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7082,18 +9760,44 @@ I server di SimpleX non possono vedere il tuo profilo. on group pref value + + other + altro + No comment provided by engineer. + + + other errors + altri errori + No comment provided by engineer. + owner proprietario member role + + owners + proprietari + feature role + peer-to-peer peer-to-peer No comment provided by engineer. + + pending + in attesa + No comment provided by engineer. + + + pending approval + in attesa di approvazione + No comment provided by engineer. + quantum resistant e2e encryption + crittografia e2e resistente alla quantistica chat item text @@ -7106,6 +9810,11 @@ I server di SimpleX non possono vedere il tuo profilo. conferma ricevuta… No comment provided by engineer. + + rejected + rifiutato + No comment provided by engineer. + rejected call chiamata rifiutata @@ -7136,6 +9845,26 @@ I server di SimpleX non possono vedere il tuo profilo. ti ha rimosso/a rcv group event chat item + + requested to connect + richiesto di connettersi + chat list item title + + + saved + salvato + No comment provided by engineer. + + + saved from %@ + salvato da %@ + No comment provided by engineer. + + + search + cerca + No comment provided by engineer. + sec sec @@ -7161,6 +9890,15 @@ I server di SimpleX non possono vedere il tuo profilo. invia messaggio diretto No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + info coda server: %1$@ + +ultimo msg ricevuto: %2$@ + queue info + set new contact address impostato nuovo indirizzo di contatto @@ -7168,11 +9906,12 @@ I server di SimpleX non possono vedere il tuo profilo. set new profile picture - impostata nuova immagine del profilo + ha impostato una nuova immagine del profilo profile update event chat item standard end-to-end encryption + crittografia end-to-end standard chat item text @@ -7200,11 +9939,21 @@ I server di SimpleX non possono vedere il tuo profilo. sconosciuto connection info + + unknown servers + relay sconosciuti + No comment provided by engineer. + unknown status stato sconosciuto No comment provided by engineer. + + unprotected + non protetto + No comment provided by engineer. + updated group profile ha aggiornato il profilo del gruppo @@ -7245,6 +9994,11 @@ I server di SimpleX non possono vedere il tuo profilo. via relay No comment provided by engineer. + + video + video + No comment provided by engineer. + video call (not e2e encrypted) videochiamata (non crittografata e2e) @@ -7270,11 +10024,21 @@ I server di SimpleX non possono vedere il tuo profilo. settimane time unit + + when IP hidden + quando l'IP è nascosto + No comment provided by engineer. + yes pref value + + you + tu + No comment provided by engineer. + you are invited to group sei stato/a invitato/a al gruppo @@ -7349,7 +10113,7 @@ I server di SimpleX non possono vedere il tuo profilo.
- +
@@ -7386,7 +10150,7 @@ I server di SimpleX non possono vedere il tuo profilo.
- +
@@ -7406,4 +10170,250 @@ I server di SimpleX non possono vedere il tuo profilo.
+ +
+ +
+ + + %d new events + %d nuovi eventi + notification body + + + From %d chat(s) + Da %d chat + notification body + + + From: %@ + Da: %@ + notification body + + + New events + Nuovi eventi + notification + + + New messages + Nuovi messaggi + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright © 2024 SimpleX Chat. Tutti i diritti riservati. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + L'app è bloccata! + No comment provided by engineer. + + + Cancel + Annulla + No comment provided by engineer. + + + Cannot access keychain to save database password + Impossibile accedere al portachiavi per salvare la password del database + No comment provided by engineer. + + + Cannot forward message + Impossibile inoltrare il messaggio + No comment provided by engineer. + + + Comment + Commento + No comment provided by engineer. + + + Currently maximum supported file size is %@. + Attualmente la dimensione massima supportata è di %@. + No comment provided by engineer. + + + Database downgrade required + Downgrade del database necessario + No comment provided by engineer. + + + Database encrypted! + Database crittografato! + No comment provided by engineer. + + + Database error + Errore del database + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + La password del database è diversa da quella salvata nel portachiavi. + No comment provided by engineer. + + + Database passphrase is required to open chat. + La password del database è necessaria per aprire la chat. + No comment provided by engineer. + + + Database upgrade required + Aggiornamento del database necessario + No comment provided by engineer. + + + Error preparing file + Errore nella preparazione del file + No comment provided by engineer. + + + Error preparing message + Errore nella preparazione del messaggio + No comment provided by engineer. + + + Error: %@ + Errore: %@ + No comment provided by engineer. + + + File error + Errore del file + No comment provided by engineer. + + + Incompatible database version + Versione del database incompatibile + No comment provided by engineer. + + + Invalid migration confirmation + Conferma di migrazione non valida + No comment provided by engineer. + + + Keychain error + Errore del portachiavi + No comment provided by engineer. + + + Large file! + File grande! + No comment provided by engineer. + + + No active profile + Nessun profilo attivo + No comment provided by engineer. + + + Ok + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + Apri l'app per eseguire il downgrade del database. + No comment provided by engineer. + + + Open the app to upgrade the database. + Apri l'app per aggiornare il database. + No comment provided by engineer. + + + Passphrase + Password + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Crea un profilo nell'app SimpleX + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Le preferenze della chat selezionata vietano questo messaggio. + No comment provided by engineer. + + + Sending a message takes longer than expected. + L'invio di un messaggio richiede più tempo del previsto. + No comment provided by engineer. + + + Sending message… + Invio messaggio… + No comment provided by engineer. + + + Share + Condividi + No comment provided by engineer. + + + Slow network? + Rete lenta? + No comment provided by engineer. + + + Unknown database error: %@ + Errore del database sconosciuto: %@ + No comment provided by engineer. + + + Unsupported format + Formato non supportato + No comment provided by engineer. + + + Wait + Attendi + No comment provided by engineer. + + + Wrong database passphrase + Password del database sbagliata + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + Puoi consentire la condivisione in Privacy e sicurezza / impostazioni di SimpleX Lock. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/it.xcloc/contents.json b/apps/ios/SimpleX Localizations/it.xcloc/contents.json index 2ad653d36f..a42f254bd9 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/it.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "it", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff index e10c39bbc3..27134216a7 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (コピー可能) @@ -74,7 +47,7 @@ %@ (current) - %@ (現在) + %@ (現在) No comment provided by engineer. @@ -109,6 +82,7 @@ %@ downloaded + %@ ダウンロード済 No comment provided by engineer. @@ -126,6 +100,11 @@ %@ は検証されています No comment provided by engineer. + + %@ server + %@ サーバー + No comment provided by engineer. + %@ servers %@ サーバー @@ -133,6 +112,7 @@ %@ uploaded + %@ アップロード済 No comment provided by engineer. @@ -140,8 +120,14 @@ %@ が接続を希望しています! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members + %@や%@など%lld人のメンバー No comment provided by engineer. @@ -159,11 +145,36 @@ %d 日 time interval + + %d file(s) are still being downloaded. + %d 個のファイルをダウンロードしています。 + forward confirmation reason + + + %d file(s) failed to download. + %d 個のファイルがダウンロードに失敗しました。 + forward confirmation reason + + + %d file(s) were deleted. + %d 個のファイルが削除されました。 + forward confirmation reason + + + %d file(s) were not downloaded. + %d 個のファイルがダウンロードされていません。 + forward confirmation reason + %d hours %d 時 time interval + + %d messages not forwarded + %d 個のメッセージが未転送 + alert title + %d min %d 分 @@ -179,6 +190,10 @@ %d 秒 time interval + + %d seconds(s) + delete after time + %d skipped message(s) %d 件のスキップされたメッセージ @@ -191,7 +206,7 @@ %lld - %lld + No comment provided by engineer. @@ -211,6 +226,7 @@ %lld group events + %lld件のグループイベント No comment provided by engineer. @@ -220,10 +236,12 @@ %lld messages blocked + %lld件のメッセージをブロック No comment provided by engineer. %lld messages blocked by admin + %lldのメッセージが管理者によりブロック済 No comment provided by engineer. @@ -246,11 +264,6 @@ %lldつの新しいインターフェース言語 No comment provided by engineer. - - %lld second(s) - %lld 秒 - No comment provided by engineer. - %lld seconds %lld 秒 @@ -301,49 +314,39 @@ %u 件のメッセージがスキップされました。 No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) + (新規) No comment provided by engineer. (this device v%@) + (このデバイス v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **新しい連絡先を追加**: 連絡先のワンタイム QR コードまたはリンクを作成します。 + + **Create 1-time link**: to create and share a new invitation link. + **コンタクトの追加**: 新しい招待リンクを作成するか、受け取ったリンクから接続します。 No comment provided by engineer. **Create group**: to create a new group. + **グループ作成**: 新しいグループを作成する。 No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **よりプライベート**: 20 分ごとに新しいメッセージを確認します。 デバイス トークンは SimpleX Chat サーバーと共有されますが、連絡先やメッセージの数は共有されません。 No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **最もプライベート**: SimpleX Chat 通知サーバーを使用せず、バックグラウンドで定期的にメッセージをチェックします (アプリの使用頻度によって異なります)。 No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **注意**: 2つの端末で同じデータベースを使用すると、セキュリティ保護として、あなたが接続しているメッセージの復号化が解除されます。 No comment provided by engineer. @@ -351,11 +354,16 @@ **注意**: パスフレーズを紛失すると、パスフレーズを復元または変更できなくなります。 No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **推奨**: デバイス トークンと通知は SimpleX Chat 通知サーバーに送信されますが、メッセージの内容、サイズ、送信者は送信されません。 No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + **QRスキャン / リンクの貼り付け**: 受け取ったリンクで接続する。 + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **警告**: 即時の プッシュ通知には、キーチェーンに保存されたパスフレーズが必要です。 @@ -363,16 +371,17 @@ **Warning**: the archive will be removed. + **警告**: アーカイブデータは削除されます。 No comment provided by engineer. **e2e encrypted** audio call - **e2e 暗号化**音声通話 + **エンドツーエンド暗号化済み**の音声通話 No comment provided by engineer. **e2e encrypted** video call - **e2e暗号化**ビデオ通話 + **エンドツーエンド暗号化済み**の テレビ電話 通話 No comment provided by engineer. @@ -380,15 +389,13 @@ \*太字* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). - faster and more stable. + - [ディレクトリサービス](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) に接続 (ベータ)! +- 配信証明を送信する (最大 20 人まで)。 +- より速く、より安定。 No comment provided by engineer. @@ -404,6 +411,9 @@ - optionally notify deleted contacts. - profile names with spaces. - and more! + - 任意で削除された連絡先へ通知します。 +- プロフィール名に空白を含めることができます。 +- and more! No comment provided by engineer. @@ -415,13 +425,9 @@ - 編集履歴。 No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec + 0 秒 time to disappear @@ -432,7 +438,8 @@ 1 day 1日 - time interval + delete after time +time interval 1 hour @@ -447,12 +454,28 @@ 1 month 1ヶ月 - time interval + delete after time +time interval 1 week 1週間 - time interval + delete after time +time interval + + + 1 year + delete after time + + + 1-time link + 使い捨てリンク + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + 使い捨てリンクは、*ひとつの連絡先にのみ* 使用できます - 対面または任意のチャットで共有してください。 + No comment provided by engineer. 5 minutes @@ -469,11 +492,6 @@ 30秒 No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -523,31 +541,29 @@ アドレス変更を中止しますか? No comment provided by engineer. - - About SimpleX - SimpleXについて - No comment provided by engineer. - About SimpleX Chat SimpleX Chat について No comment provided by engineer. - - About SimpleX address - SimpleXアドレスについて + + About operators No comment provided by engineer. - - Accent color - アクセントカラー + + Accent No comment provided by engineer. Accept 承諾 accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + No comment provided by engineer. Accept connection request? @@ -562,20 +578,40 @@ Accept incognito シークレットモードで承諾 - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + No comment provided by engineer. + + + Acknowledged + No comment provided by engineer. + + + Acknowledgement errors + No comment provided by engineer. + + + Active + token status text + + + Active connections + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. プロフィールにアドレスを追加し、連絡先があなたのアドレスを他の人と共有できるようにします。プロフィールの更新は連絡先に送信されます。 No comment provided by engineer. - - Add contact + + Add friends No comment provided by engineer. - - Add preset servers - 既存サーバを追加 + + Add list No comment provided by engineer. @@ -583,14 +619,18 @@ プロフィールを追加 No comment provided by engineer. + + Add server + サーバを追加 + No comment provided by engineer. + Add servers by scanning QR codes. QRコードでサーバを追加する。 No comment provided by engineer. - - Add server… - サーバを追加… + + Add team members No comment provided by engineer. @@ -598,11 +638,41 @@ 別の端末に追加 No comment provided by engineer. + + Add to list + No comment provided by engineer. + Add welcome message ウェルカムメッセージを追加 No comment provided by engineer. + + Add your team members to the conversations. + No comment provided by engineer. + + + Added media & file servers + 追加されたメディア & ファイルサーバー + No comment provided by engineer. + + + Added message servers + 追加されたメッセージサーバー + No comment provided by engineer. + + + Additional accent + No comment provided by engineer. + + + Additional accent 2 + No comment provided by engineer. + + + Additional secondary + No comment provided by engineer. + Address アドレス @@ -613,6 +683,14 @@ アドレス変更は中止されます。古い受信アドレスが使用されます。 No comment provided by engineer. + + Address or 1-time link? + No comment provided by engineer. + + + Address settings + No comment provided by engineer. + Admins can block a member for all. No comment provided by engineer. @@ -627,6 +705,15 @@ ネットワーク詳細設定 No comment provided by engineer. + + Advanced settings + 詳細設定 + No comment provided by engineer. + + + All + No comment provided by engineer. + All app data is deleted. すべてのアプリデータが削除されます。 @@ -637,16 +724,28 @@ 全チャットとメッセージが削除されます(※元に戻せません※)! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + alert message + All data is erased when it is entered. 入力するとすべてのデータが消去されます。 No comment provided by engineer. + + All data is kept private on your device. + No comment provided by engineer. + All group members will remain connected. グループ全員の接続が継続します。 No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! No comment provided by engineer. @@ -660,6 +759,19 @@ All new messages from %@ will be hidden! No comment provided by engineer. + + All profiles + すべてのプロフィール + profile dropdown + + + All reports will be archived for you. + No comment provided by engineer. + + + All servers + No comment provided by engineer. + All your contacts will remain connected. あなたの連絡先が繋がったまま継続します。 @@ -672,6 +784,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + すべての連絡先、会話、ファイルは安全に暗号化され、設定されたXFTPリレーに分割でアップロードされます。 No comment provided by engineer. @@ -684,14 +797,22 @@ 連絡先が通話を許可している場合のみ通話を許可する。 No comment provided by engineer. + + Allow calls? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. 連絡先が許可している場合のみ消えるメッセージを許可する。 No comment provided by engineer. + + Allow downgrade + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) - 送信相手も永久メッセージ削除を許可する時のみに許可する。 + 送信相手も永久メッセージ削除を許可する時のみに許可する。(24時間) No comment provided by engineer. @@ -714,9 +835,23 @@ 消えるメッセージの送信を許可する。 No comment provided by engineer. + + Allow sharing + 共有を許可 + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) - 送信済みメッセージの永久削除を許可する。 + 送信済みメッセージの永久削除を許可する。(24時間) + No comment provided by engineer. + + + Allow to report messsages to moderators. + No comment provided by engineer. + + + Allow to send SimpleX links. + SimpleXリンクの送信を許可。 No comment provided by engineer. @@ -751,7 +886,7 @@ Allow your contacts to irreversibly delete sent messages. (24 hours) - 送信相手が永久メッセージ削除するのを許可する。 + 送信相手が永久メッセージ削除するのを許可する。(24時間) No comment provided by engineer. @@ -771,10 +906,17 @@ Already connecting! + 既に接続中です! No comment provided by engineer. Already joining the group! + すでにグループに参加しています! + No comment provided by engineer. + + + Always use private routing. + プライベートルーティングを常に使用する。 No comment provided by engineer. @@ -787,11 +929,20 @@ 指定された名前の空のチャット プロファイルが作成され、アプリが通常どおり開きます。 No comment provided by engineer. + + Another reason + report reason + Answer call 通話に応答 No comment provided by engineer. + + Anybody can host servers. + プロトコル技術とコードはオープンソースで、どなたでもご自分のサーバを運用できます。 + No comment provided by engineer. + App build: %@ アプリのビルド: %@ @@ -799,6 +950,7 @@ App data migration + アプリデータの移行 No comment provided by engineer. @@ -806,6 +958,10 @@ アプリは新しいローカルファイル(ビデオを除く)を暗号化します。 No comment provided by engineer. + + App group: + No comment provided by engineer. + App icon アプリのアイコン @@ -821,6 +977,10 @@ アプリのパスコードは自己破壊パスコードに置き換えられます。 No comment provided by engineer. + + App session + No comment provided by engineer. + App version アプリのバージョン @@ -833,15 +993,54 @@ Appearance - 見た目 + アピアランス No comment provided by engineer. Apply + 適用 + No comment provided by engineer. + + + Apply to + に適用する + No comment provided by engineer. + + + Archive + No comment provided by engineer. + + + Archive %lld reports? + No comment provided by engineer. + + + Archive all reports? No comment provided by engineer. Archive and upload + アーカイブとアップロード + No comment provided by engineer. + + + Archive contacts to chat later. + No comment provided by engineer. + + + Archive report + No comment provided by engineer. + + + Archive report? + No comment provided by engineer. + + + Archive reports + swipe action + + + Archived contacts No comment provided by engineer. @@ -908,11 +1107,19 @@ 画像を自動的に受信 No comment provided by engineer. + + Auto-accept settings + alert title + Back 戻る No comment provided by engineer. + + Background + No comment provided by engineer. + Bad desktop address No comment provided by engineer. @@ -927,15 +1134,51 @@ メッセージのハッシュ値問題 No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups No comment provided by engineer. + + Better groups performance + No comment provided by engineer. + + + Better message dates. + No comment provided by engineer. + Better messages より良いメッセージ No comment provided by engineer. + + Better networking + No comment provided by engineer. + + + Better notifications + No comment provided by engineer. + + + Better privacy and security + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + + + Black + No comment provided by engineer. + Block No comment provided by engineer. @@ -964,6 +1207,14 @@ Blocked by admin No comment provided by engineer. + + Blur for better privacy. + No comment provided by engineer. + + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. 自分も相手もメッセージへのリアクションを追加できます。 @@ -971,7 +1222,7 @@ Both you and your contact can irreversibly delete sent messages. (24 hours) - あなたと連絡相手が送信済みメッセージを永久削除できます。 + あなたと連絡相手が送信済みメッセージを永久削除できます。(24時間) No comment provided by engineer. @@ -994,11 +1245,29 @@ ブルガリア語、フィンランド語、タイ語、ウクライナ語 - ユーザーと [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)に感謝します! No comment provided by engineer. + + Business address + No comment provided by engineer. + + + Business chats + No comment provided by engineer. + + + Businesses + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). チャット プロファイル経由 (デフォルト) または [接続経由](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + No comment provided by engineer. + Call already ended! 通話は既に終了してます! @@ -1009,10 +1278,22 @@ 通話 No comment provided by engineer. + + Calls prohibited! + No comment provided by engineer. + Camera not available No comment provided by engineer. + + Can't call contact + No comment provided by engineer. + + + Can't call member + No comment provided by engineer. + Can't invite contact! 連絡先を招待できません! @@ -1023,10 +1304,15 @@ 連絡先を招待できません! No comment provided by engineer. + + Can't message member + No comment provided by engineer. + Cancel 中止 - No comment provided by engineer. + alert action +alert button Cancel migration @@ -1037,9 +1323,21 @@ データベースのパスワードを保存するためのキーチェーンにアクセスできません No comment provided by engineer. + + Cannot forward message + No comment provided by engineer. + Cannot receive file ファイル受信ができません + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + snd error text + + + Cellular No comment provided by engineer. @@ -1047,6 +1345,14 @@ 変更 No comment provided by engineer. + + Change automatic message deletion? + alert title + + + Change chat profiles + authentication reason + Change database passphrase? データベースのパスフレーズを更新しますか? @@ -1091,11 +1397,22 @@ Change self-destruct passcode 自己破壊パスコードを変更する authentication reason - set passcode view +set passcode view - - Chat archive - チャットのアーカイブ + + Chat + No comment provided by engineer. + + + Chat already exists + No comment provided by engineer. + + + Chat already exists! + No comment provided by engineer. + + + Chat colors No comment provided by engineer. @@ -1113,6 +1430,10 @@ チャットのデータベースが削除されました No comment provided by engineer. + + Chat database exported + No comment provided by engineer. + Chat database imported チャットのデータベースが読み込まれました @@ -1132,6 +1453,10 @@ Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat. No comment provided by engineer. + + Chat list + No comment provided by engineer. + Chat migrated! No comment provided by engineer. @@ -1141,15 +1466,45 @@ チャット設定 No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + ユーザープロフィール + No comment provided by engineer. + + + Chat theme + チャットテーマ + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + No comment provided by engineer. + Chats チャット No comment provided by engineer. + + Check messages every 20 min. + No comment provided by engineer. + + + Check messages when allowed. + No comment provided by engineer. + Check server address and try again. サーバのアドレスを確認してから再度試してください。 - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1170,10 +1525,24 @@ ライブラリから選択 No comment provided by engineer. + + Chunks deleted + チャンクが削除されました + No comment provided by engineer. + + + Chunks downloaded + チャンクがダウンロードされました + No comment provided by engineer. + + + Chunks uploaded + No comment provided by engineer. + Clear 消す - No comment provided by engineer. + swipe action Clear conversation @@ -1185,8 +1554,17 @@ ダイアログのクリアしますか? No comment provided by engineer. + + Clear group? + No comment provided by engineer. + + + Clear or delete group? + No comment provided by engineer. + Clear private notes? + プライベートノートを消しますか? No comment provided by engineer. @@ -1194,11 +1572,19 @@ 検証を消す No comment provided by engineer. - - Colors - + + Color chats with the new themes. No comment provided by engineer. + + Color mode + 色設定 + No comment provided by engineer. + + + Community guidelines violation + report reason + Compare file ファイルを比較 @@ -1209,11 +1595,48 @@ 連絡先とセキュリティコードを確認する。 No comment provided by engineer. + + Completed + 完了 + No comment provided by engineer. + + + Conditions accepted on: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + No comment provided by engineer. + + + Conditions of use + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + No comment provided by engineer. + Configure ICE servers ICEサーバを設定 No comment provided by engineer. + + Configure server operators + No comment provided by engineer. + Confirm 確認 @@ -1224,11 +1647,19 @@ パスコードを確認 No comment provided by engineer. + + Confirm contact deletion? + No comment provided by engineer. + Confirm database upgrades データベースのアップグレードを確認 No comment provided by engineer. + + Confirm files from unknown servers. + No comment provided by engineer. + Confirm network settings No comment provided by engineer. @@ -1251,6 +1682,10 @@ Confirm upload No comment provided by engineer. + + Confirmed + token status text + Connect 接続 @@ -1267,6 +1702,12 @@ Connect to desktop + デスクトップに接続 + No comment provided by engineer. + + + Connect to your friends faster. + 友達ともっと速くつながりましょう。 No comment provided by engineer. @@ -1301,12 +1742,29 @@ This is your own one-time link! Connect with %@ No comment provided by engineer. + + Connected + 接続中 + No comment provided by engineer. + Connected desktop + デスクトップに接続済 + No comment provided by engineer. + + + Connected servers + 接続中のサーバ No comment provided by engineer. Connected to desktop + デスクトップに接続済 + No comment provided by engineer. + + + Connecting + 接続待ち No comment provided by engineer. @@ -1319,8 +1777,14 @@ This is your own one-time link! サーバーに接続中… (エラー: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + 連絡先に接続中です。しばらくお待ちいただくか、後で確認してください! + No comment provided by engineer. + Connecting to desktop + デスクトップに接続中 No comment provided by engineer. @@ -1328,6 +1792,15 @@ This is your own one-time link! 接続 No comment provided by engineer. + + Connection and servers status. + 接続とサーバーのステータス。 + No comment provided by engineer. + + + Connection blocked + No comment provided by engineer. + Connection error 接続エラー @@ -1338,13 +1811,35 @@ This is your own one-time link! 接続エラー (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + No comment provided by engineer. + + + Connection not ready. + No comment provided by engineer. + + + Connection notifications + No comment provided by engineer. + Connection request sent! 接続リクエストを送信しました! No comment provided by engineer. + + Connection requires encryption renegotiation. + No comment provided by engineer. + + + Connection security + No comment provided by engineer. + Connection terminated + 接続停止 No comment provided by engineer. @@ -1352,6 +1847,14 @@ This is your own one-time link! 接続タイムアウト No comment provided by engineer. + + Connection with desktop stopped + No comment provided by engineer. + + + Connections + No comment provided by engineer. + Contact allows 連絡先の許可 @@ -1362,6 +1865,10 @@ This is your own one-time link! 連絡先に既に存在します No comment provided by engineer. + + Contact deleted! + No comment provided by engineer. + Contact hidden: 連絡先が非表示: @@ -1372,9 +1879,8 @@ This is your own one-time link! 連絡先は接続中 notification - - Contact is not connected yet! - 連絡先がまだ繋がってません! + + Contact is deleted. No comment provided by engineer. @@ -1387,6 +1893,10 @@ This is your own one-time link! 連絡先の設定 No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + No comment provided by engineer. + Contacts 連絡先 @@ -1397,21 +1907,37 @@ This is your own one-time link! 連絡先はメッセージを削除対象とすることができます。あなたには閲覧可能です。 No comment provided by engineer. + + Content violates conditions of use + blocking reason + Continue 続ける No comment provided by engineer. + + Conversation deleted! + No comment provided by engineer. + Copy コピー - chat item action + No comment provided by engineer. + + + Copy error + No comment provided by engineer. Core version: v%@ コアのバージョン: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? No comment provided by engineer. @@ -1421,6 +1947,10 @@ This is your own one-time link! 作成 No comment provided by engineer. + + Create 1-time link + No comment provided by engineer. + Create SimpleX address SimpleXアドレスの作成 @@ -1430,11 +1960,6 @@ This is your own one-time link! Create a group using a random profile. No comment provided by engineer. - - Create an address to let people connect with you. - 人とつながるためのアドレスを作成する。 - No comment provided by engineer. - Create file ファイルを作成 @@ -1454,6 +1979,10 @@ This is your own one-time link! リンクを生成する No comment provided by engineer. + + Create list + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 [デスクトップアプリ](https://simplex.chat/downloads/)で新しいプロファイルを作成します。 💻 @@ -1461,6 +1990,7 @@ This is your own one-time link! Create profile + プロフィールを作成する No comment provided by engineer. @@ -1478,6 +2008,10 @@ This is your own one-time link! プロフィールを作成する No comment provided by engineer. + + Created + No comment provided by engineer. + Created at No comment provided by engineer. @@ -1486,11 +2020,6 @@ This is your own one-time link! Created at: %@ copied message info - - Created on %@ - %@ によって作成されました - No comment provided by engineer. - Creating archive link No comment provided by engineer. @@ -1504,11 +2033,19 @@ This is your own one-time link! 現在のパスコード No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + No comment provided by engineer. + Current passphrase… 現在の暗証フレーズ… No comment provided by engineer. + + Current profile + No comment provided by engineer. + Currently maximum supported file size is %@. 現在サポートされている最大ファイルサイズは %@. @@ -1519,11 +2056,25 @@ This is your own one-time link! カスタム時間 No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + + + Customize theme + カスタムテーマ + No comment provided by engineer. + Dark ダークモード No comment provided by engineer. + + Dark mode colors + ダークモードカラー + No comment provided by engineer. + Database ID データベースID @@ -1622,6 +2173,11 @@ This is your own one-time link! データベースはアプリ再起動時に移行されます No comment provided by engineer. + + Debug delivery + 配信のデバッグ + No comment provided by engineer. + Decentralized 分散型 @@ -1635,17 +2191,17 @@ This is your own one-time link! Delete 削除 - chat item action + alert action +swipe action + + + Delete %lld messages of members? + No comment provided by engineer. Delete %lld messages? No comment provided by engineer. - - Delete Contact - 連絡先を削除 - No comment provided by engineer. - Delete address アドレスを削除 @@ -1670,14 +2226,12 @@ This is your own one-time link! Delete and notify contact No comment provided by engineer. - - Delete archive - アーカイブを削除 + + Delete chat No comment provided by engineer. - - Delete chat archive? - チャットのアーカイブを削除しますか? + + Delete chat messages from your device. No comment provided by engineer. @@ -1690,6 +2244,10 @@ This is your own one-time link! チャットのプロフィールを削除しますか? No comment provided by engineer. + + Delete chat? + No comment provided by engineer. + Delete connection 接続を削除する @@ -1700,9 +2258,8 @@ This is your own one-time link! 連絡先を削除 No comment provided by engineer. - - Delete contact? -This cannot be undone! + + Delete contact? No comment provided by engineer. @@ -1764,6 +2321,10 @@ This cannot be undone! リンクを削除しますか? No comment provided by engineer. + + Delete list? + alert title + Delete member message? メンバーのメッセージを削除しますか? @@ -1777,7 +2338,7 @@ This cannot be undone! Delete messages メッセージを削除 - No comment provided by engineer. + alert button Delete messages after @@ -1794,9 +2355,8 @@ This cannot be undone! 古いデータベースを削除しますか? No comment provided by engineer. - - Delete pending connection - 確認待ちの接続を削除 + + Delete or moderate up to 200 messages. No comment provided by engineer. @@ -1814,11 +2374,27 @@ This cannot be undone! 待ち行列を削除 server test step + + Delete report + No comment provided by engineer. + + + Delete up to 20 messages at once. + No comment provided by engineer. + Delete user profile? ユーザープロフィールを削除しますか? No comment provided by engineer. + + Delete without notification + No comment provided by engineer. + + + Deleted + No comment provided by engineer. + Deleted at 削除完了 @@ -1829,6 +2405,14 @@ This cannot be undone! 削除完了: %@ copied message info + + Deletion errors + No comment provided by engineer. + + + Delivered even when Apple drops them. + No comment provided by engineer. + Delivery 配信 @@ -1859,6 +2443,27 @@ This cannot be undone! Desktop devices + デスクトップ機器 + No comment provided by engineer. + + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + + + Destination server error: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + + + Detailed statistics + No comment provided by engineer. + + + Details No comment provided by engineer. @@ -1866,6 +2471,11 @@ This cannot be undone! 開発 No comment provided by engineer. + + Developer options + 開発者向けの設定 + No comment provided by engineer. + Developer tools 開発ツール @@ -1896,8 +2506,12 @@ This cannot be undone! ダイレクトメッセージ chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + No comment provided by engineer. + + + Direct messages between members are prohibited. このグループではメンバー間のダイレクトメッセージが使用禁止です。 No comment provided by engineer. @@ -1911,11 +2525,23 @@ This cannot be undone! SimpleXロックを無効にする authentication reason + + Disable automatic message deletion? + alert title + + + Disable delete messages + alert button + Disable for all すべて無効 No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message 消えるメッセージ @@ -1931,8 +2557,8 @@ This cannot be undone! このチャットでは消えるメッセージが使用禁止です。 No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. このグループでは消えるメッセージが使用禁止です。 No comment provided by engineer. @@ -1964,11 +2590,19 @@ This cannot be undone! Discover via local network No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. 緊急通報にSimpleXを使用しないでください。 No comment provided by engineer. + + Do NOT use private routing. + No comment provided by engineer. + Do it later 後で行う @@ -1978,9 +2612,17 @@ This cannot be undone! Do not send history to new members. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + + + Documents: + No comment provided by engineer. + Don't create address - アドレスを作成しないでください + アドレスを作成しない No comment provided by engineer. @@ -1988,16 +2630,33 @@ This cannot be undone! 有効にしない No comment provided by engineer. + + Don't miss important messages. + No comment provided by engineer. + Don't show again 次から表示しない No comment provided by engineer. + + Done + No comment provided by engineer. + Downgrade and open chat ダウングレードしてチャットを開く No comment provided by engineer. + + Download + alert button +chat item action + + + Download errors + No comment provided by engineer. + Download failed No comment provided by engineer. @@ -2007,6 +2666,18 @@ This cannot be undone! ファイルをダウンロード server test step + + Download files + alert action + + + Downloaded + No comment provided by engineer. + + + Downloaded files + No comment provided by engineer. + Downloading archive No comment provided by engineer. @@ -2025,6 +2696,10 @@ This cannot be undone! 間隔 No comment provided by engineer. + + E2E encrypted notifications. + No comment provided by engineer. + Edit 編集する @@ -2045,6 +2720,10 @@ This cannot be undone! 有効にする(設定の優先を維持) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + No comment provided by engineer. + Enable SimpleX Lock SimpleXロックを有効にする @@ -2058,7 +2737,7 @@ This cannot be undone! Enable automatic message deletion? 自動メッセージ削除を有効にしますか? - No comment provided by engineer. + alert title Enable camera access @@ -2103,6 +2782,14 @@ This cannot be undone! 自己破壊パスコードを有効にする set passcode view + + Enabled + No comment provided by engineer. + + + Enabled for + No comment provided by engineer. + Encrypt 暗号化する @@ -2170,6 +2857,10 @@ This cannot be undone! Encryption re-negotiation failed. No comment provided by engineer. + + Encryption renegotiation in progress. + No comment provided by engineer. + Enter Passcode パスコードを入力 @@ -2231,30 +2922,33 @@ This cannot be undone! アドレス変更中止エラー No comment provided by engineer. + + Error accepting conditions + alert title + Error accepting contact request 連絡先リクエストの承諾にエラー発生 No comment provided by engineer. - - Error accessing database file - データベースファイルへのアクセスエラー - No comment provided by engineer. - Error adding member(s) メンバー追加にエラー発生 No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + alert title Error changing address アドレス変更にエラー発生 No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role 役割変更にエラー発生 @@ -2265,6 +2959,18 @@ This cannot be undone! 設定変更にエラー発生 No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + + + Error checking token status + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address アドレス作成にエラー発生 @@ -2280,6 +2986,10 @@ This cannot be undone! グループリンク生成にエラー発生 No comment provided by engineer. + + Error creating list + alert title + Error creating member contact メンバー連絡先の作成中にエラーが発生 @@ -2294,6 +3004,10 @@ This cannot be undone! プロフィール作成にエラー発生! No comment provided by engineer. + + Error creating report + No comment provided by engineer. + Error decrypting file ファイルの復号エラー @@ -2314,11 +3028,6 @@ This cannot be undone! 接続の削除エラー No comment provided by engineer. - - Error deleting contact - 連絡先の削除にエラー発生 - No comment provided by engineer. - Error deleting database データベースの削除にエラー発生 @@ -2362,6 +3071,10 @@ This cannot be undone! チャットデータベースのエキスポートにエラー発生 No comment provided by engineer. + + Error exporting theme: %@ + No comment provided by engineer. + Error importing chat database チャットデータベースのインポートにエラー発生 @@ -2372,9 +3085,12 @@ This cannot be undone! グループ参加にエラー発生 No comment provided by engineer. - - Error loading %@ servers - %@ サーバーのロード中にエラーが発生 + + Error loading servers + alert title + + + Error migrating settings No comment provided by engineer. @@ -2384,16 +3100,31 @@ This cannot be undone! Error receiving file ファイル受信にエラー発生 + alert title + + + Error reconnecting server No comment provided by engineer. + + Error reconnecting servers + No comment provided by engineer. + + + Error registering for notifications + alert title + Error removing member メンバー除名にエラー発生 No comment provided by engineer. - - Error saving %@ servers - %@ サーバの保存エラー + + Error reordering lists + alert title + + + Error resetting statistics No comment provided by engineer. @@ -2401,6 +3132,10 @@ This cannot be undone! ICEサーバ保存にエラー発生 No comment provided by engineer. + + Error saving chat list + alert title + Error saving group profile グループのプロフィール保存にエラー発生 @@ -2416,6 +3151,10 @@ This cannot be undone! キーチェーンにパスフレーズを保存にエラー発生 No comment provided by engineer. + + Error saving servers + alert title + Error saving settings when migrating @@ -2458,16 +3197,24 @@ This cannot be undone! チャット停止にエラー発生 No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! プロフィール切り替えにエラー発生! - No comment provided by engineer. + alertTitle Error synchronizing connection 接続の同期エラー No comment provided by engineer. + + Error testing server connection + No comment provided by engineer. + Error updating group link グループのリンクのアップデートにエラー発生 @@ -2478,6 +3225,10 @@ This cannot be undone! メッセージの更新にエラー発生 No comment provided by engineer. + + Error updating server + alert title + Error updating settings 設定の更新にエラー発生 @@ -2504,7 +3255,9 @@ This cannot be undone! Error: %@ エラー : %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2516,6 +3269,14 @@ This cannot be undone! エラー: データベースが存在しません No comment provided by engineer. + + Errors + No comment provided by engineer. + + + Errors in servers configuration. + servers error + Even when disabled in the conversation. 会話中に無効になっている場合でも。 @@ -2530,6 +3291,10 @@ This cannot be undone! Expand chat item action + + Expired + token status text + Export database データベースをエキスポート @@ -2540,6 +3305,10 @@ This cannot be undone! エクスポートエラー: No comment provided by engineer. + + Export theme + No comment provided by engineer. + Exported database archive. データベースのアーカイブをエクスポートします。 @@ -2564,15 +3333,57 @@ This cannot be undone! 送信者がオンラインになるまでの待ち時間がなく、速い! No comment provided by engineer. + + Faster deletion of groups. + No comment provided by engineer. + Faster joining and more reliable messages. No comment provided by engineer. + + Faster sending messages. + No comment provided by engineer. + Favorite お気に入り + swipe action + + + Favorites No comment provided by engineer. + + File error + file error alert title + + + File errors: +%@ + alert message + + + File is blocked by server operator: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + file error text + + + File server error: %@ + file error text + + + File status + No comment provided by engineer. + + + File status: %@ + copied message info + File will be deleted from servers. ファイルはサーバーから削除されます。 @@ -2593,6 +3404,10 @@ This cannot be undone! ファイル: %@ No comment provided by engineer. + + Files + No comment provided by engineer. + Files & media ファイルとメディア @@ -2603,11 +3418,15 @@ This cannot be undone! ファイルとメディア chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. このグループでは、ファイルとメディアは禁止されています。 No comment provided by engineer. + + Files and media not allowed + No comment provided by engineer. + Files and media prohibited! ファイルとメディアは禁止されています! @@ -2666,11 +3485,93 @@ This cannot be undone! グループメンバーによる修正はサポートされていません No comment provided by engineer. + + For all moderators + No comment provided by engineer. + + + For chat profile %@: + servers error + For console コンソール No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + No comment provided by engineer. + + + For me + No comment provided by engineer. + + + For private routing + No comment provided by engineer. + + + For social media + No comment provided by engineer. + + + Forward + chat item action + + + Forward %d message(s)? + alert title + + + Forward and save messages + No comment provided by engineer. + + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + + + Forwarded + No comment provided by engineer. + + + Forwarded from + No comment provided by engineer. + + + Forwarding %lld messages + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + snd error text + Found desktop No comment provided by engineer. @@ -2690,11 +3591,6 @@ This cannot be undone! フルネーム (任意): No comment provided by engineer. - - Full name: - フルネーム: - No comment provided by engineer. - Fully decentralized – visible only to members. No comment provided by engineer. @@ -2714,6 +3610,18 @@ This cannot be undone! GIFとステッカー No comment provided by engineer. + + Get notified when mentioned. + No comment provided by engineer. + + + Good afternoon! + message preview + + + Good morning! + message preview + Group グループ @@ -2767,36 +3675,6 @@ This cannot be undone! グループのリンク No comment provided by engineer. - - Group members can add message reactions. - グループメンバーはメッセージへのリアクションを追加できます。 - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - グループのメンバーがメッセージを完全削除することができます。 - No comment provided by engineer. - - - Group members can send direct messages. - グループのメンバーがダイレクトメッセージを送信できます。 - No comment provided by engineer. - - - Group members can send disappearing messages. - グループのメンバーが消えるメッセージを送信できます。 - No comment provided by engineer. - - - Group members can send files and media. - グループメンバーはファイルやメディアを送信できます。 - No comment provided by engineer. - - - Group members can send voice messages. - グループのメンバーが音声メッセージを送信できます。 - No comment provided by engineer. - Group message: グループメッセージ: @@ -2837,11 +3715,19 @@ This cannot be undone! あなたにとってグループが削除されます (※元に戻せません※)! No comment provided by engineer. + + Groups + No comment provided by engineer. + Help ヘルプ No comment provided by engineer. + + Help admins moderating their groups. + No comment provided by engineer. + Hidden プライベート @@ -2891,10 +3777,17 @@ This cannot be undone! SimpleX の仕組み No comment provided by engineer. + + How it affects privacy + No comment provided by engineer. + + + How it helps privacy + No comment provided by engineer. + How it works - 技術の説明 - No comment provided by engineer. + alert button How to @@ -2920,6 +3813,10 @@ This cannot be undone! ICEサーバ (1行に1サーバ) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. 直接会えない場合は、ビデオ通話で QR コードを表示するか、リンクを共有してください。 @@ -2960,8 +3857,8 @@ This cannot be undone! 即座に No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam スパムや悪質送信を防止 No comment provided by engineer. @@ -2984,10 +3881,19 @@ This cannot be undone! Import failed No comment provided by engineer. + + Import theme + No comment provided by engineer. + Importing archive No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery No comment provided by engineer. @@ -3011,6 +3917,18 @@ This cannot be undone! 返信先 No comment provided by engineer. + + In-call sounds + No comment provided by engineer. + + + Inappropriate content + report reason + + + Inappropriate profile + report reason + Incognito シークレットモード @@ -3079,6 +3997,11 @@ This cannot be undone! インストール [ターミナル用SimpleX Chat](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + 即時 + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3086,16 +4009,35 @@ This cannot be undone! No comment provided by engineer. - - Instantly - すぐに - No comment provided by engineer. - Interface インターフェース No comment provided by engineer. + + Interface colors + No comment provided by engineer. + + + Invalid + token status text + + + Invalid (bad token) + token status text + + + Invalid (expired) + token status text + + + Invalid (unregistered) + token status text + + + Invalid (wrong topic) + token status text + Invalid QR code No comment provided by engineer. @@ -3128,7 +4070,7 @@ This cannot be undone! Invalid server address! 無効なサーバアドレス! - No comment provided by engineer. + alert title Invalid status @@ -3150,6 +4092,10 @@ This cannot be undone! メンバーを招待する No comment provided by engineer. + + Invite to chat + No comment provided by engineer. + Invite to group グループに招待する @@ -3165,8 +4111,8 @@ This cannot be undone! このチャットではメッセージの完全削除が使用禁止です。 No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. このグループではメッセージの完全削除が使用禁止です。 No comment provided by engineer. @@ -3191,6 +4137,10 @@ This cannot be undone! 3. 接続に問題があった。 No comment provided by engineer. + + It protects your IP address and connections. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). このリンクからすでに接続されているようです。そうでない場合は、エラー(%@)が発生しました。 @@ -3209,7 +4159,7 @@ This cannot be undone! Join 参加 - No comment provided by engineer. + swipe action Join group @@ -3245,6 +4195,10 @@ This is your link for group %@! Keep + alert action + + + Keep conversation No comment provided by engineer. @@ -3253,7 +4207,7 @@ This is your link for group %@! Keep unused invitation? - No comment provided by engineer. + alert title Keep your connections @@ -3288,6 +4242,14 @@ This is your link for group %@! Leave 脱退 + swipe action + + + Leave chat + No comment provided by engineer. + + + Leave chat? No comment provided by engineer. @@ -3327,6 +4289,18 @@ This is your link for group %@! Linked desktops No comment provided by engineer. + + List + swipe action + + + List name and emoji should be different for all lists. + No comment provided by engineer. + + + List name... + No comment provided by engineer. + Live message! ライブメッセージ! @@ -3337,11 +4311,6 @@ This is your link for group %@! ライブメッセージ No comment provided by engineer. - - Local - 自分のみ - No comment provided by engineer. - Local name ローカルネーム @@ -3362,11 +4331,6 @@ This is your link for group %@! ロックモード No comment provided by engineer. - - Make a private connection - プライベートな接続をする - No comment provided by engineer. - Make one message disappear メッセージを1つ消す @@ -3377,21 +4341,11 @@ This is your link for group %@! プロフィールを非表示にできます! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - %@ サーバー アドレスが正しい形式で、行が区切られており、重複していないことを確認してください (%@)。 - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. WebRTC ICEサーバのアドレスを正しく1行ずつに分けて、重複しないように、形式もご確認ください。 No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - 多くの人が次のような質問をしました: *SimpleX にユーザー識別子がない場合、どうやってメッセージを配信できるのですか?* - No comment provided by engineer. - Mark deleted for everyone 全員に対して削除済みマークを付ける @@ -3417,11 +4371,31 @@ This is your link for group %@! 最大 30 秒で即時受信します。 No comment provided by engineer. + + Media & file servers + No comment provided by engineer. + + + Medium + blur media + Member メンバー No comment provided by engineer. + + Member inactive + item status text + + + Member reports + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. メンバーの役割が "%@" に変更されます。 グループメンバー全員に通知されます。 @@ -3432,11 +4406,61 @@ This is your link for group %@! メンバーの役割が "%@" に変更されます。 メンバーは新たな招待を受け取ります。 No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! メンバーをグループから除名する (※元に戻せません※)! No comment provided by engineer. + + Members can add message reactions. + グループメンバーはメッセージへのリアクションを追加できます。 + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + グループのメンバーがメッセージを完全削除することができます。(24時間) + No comment provided by engineer. + + + Members can report messsages to moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + No comment provided by engineer. + + + Members can send direct messages. + グループのメンバーがダイレクトメッセージを送信できます。 + No comment provided by engineer. + + + Members can send disappearing messages. + グループのメンバーが消えるメッセージを送信できます。 + No comment provided by engineer. + + + Members can send files and media. + グループメンバーはファイルやメディアを送信できます。 + No comment provided by engineer. + + + Members can send voice messages. + グループのメンバーが音声メッセージを送信できます。 + No comment provided by engineer. + + + Mention members 👋 + No comment provided by engineer. + + + Menus + No comment provided by engineer. + Message delivery error メッセージ送信エラー @@ -3446,11 +4470,27 @@ This is your link for group %@! Message delivery receipts! No comment provided by engineer. + + Message delivery warning + item status text + Message draft メッセージの下書き No comment provided by engineer. + + Message forwarded + item status text + + + Message may be delivered later if member becomes active. + item status description + + + Message queue info + No comment provided by engineer. + Message reactions メッセージへのリアクション @@ -3461,11 +4501,35 @@ This is your link for group %@! このチャットではメッセージへのリアクションは禁止されています。 No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. このグループではメッセージへのリアクションは禁止されています。 No comment provided by engineer. + + Message reception + No comment provided by engineer. + + + Message servers + No comment provided by engineer. + + + Message shape + No comment provided by engineer. + + + Message source remains private. + No comment provided by engineer. + + + Message status + No comment provided by engineer. + + + Message status: %@ + copied message info + Message text メッセージ内容 @@ -3489,12 +4553,30 @@ This is your link for group %@! Messages from %@ will be shown! No comment provided by engineer. + + Messages in this chat will never be deleted. + alert message + + + Messages received + No comment provided by engineer. + + + Messages sent + No comment provided by engineer. + + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + メッセージ、ファイル、通話は、前方秘匿性、否認可能性および侵入復元性を備えた**エンドツーエンドの暗号化**によって保護されます。 No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + メッセージ、ファイル、通話は、前方秘匿性、否認可能性および侵入復元性を備えた**耐量子E2E暗号化**によって保護されます。 No comment provided by engineer. @@ -3503,6 +4585,7 @@ This is your link for group %@! Migrate from another device + 別の端末から移行 No comment provided by engineer. @@ -3545,9 +4628,9 @@ This is your link for group %@! 移行が完了しました No comment provided by engineer. - - Migrations: %@ - 移行: %@ + + Migrations: + 移行: No comment provided by engineer. @@ -3565,21 +4648,28 @@ This is your link for group %@! モデレーターによって介入済み: %@ copied message info + + More + swipe action + More improvements are coming soon! まだまだ改善してまいります! No comment provided by engineer. + + More reliable network connection. + No comment provided by engineer. + + + More reliable notifications + No comment provided by engineer. + Most likely this connection is deleted. おそらく、この接続は削除されています。 item status description - - Most likely this contact has deleted the connection with you. - 恐らくこの連絡先があなたとの接続を削除しました。 - No comment provided by engineer. - Multiple chat profiles 複数チャットのプロフィール @@ -3588,7 +4678,11 @@ This is your link for group %@! Mute ミュート - No comment provided by engineer. + notification label action + + + Mute all + notification label action Muted when inactive! @@ -3598,13 +4692,33 @@ This is your link for group %@! Name 名前 - No comment provided by engineer. + swipe action Network & servers ネットワークとサーバ No comment provided by engineer. + + Network connection + No comment provided by engineer. + + + Network decentralization + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + snd error text + + + Network management + No comment provided by engineer. + + + Network operator + No comment provided by engineer. + Network settings ネットワーク設定 @@ -3615,15 +4729,31 @@ This is your link for group %@! ネットワーク状況 No comment provided by engineer. + + New + token status text + New Passcode 新しいパスコード No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat No comment provided by engineer. + + New chat experience 🎉 + No comment provided by engineer. + New contact request 新しい繋がりのリクエスト @@ -3634,11 +4764,6 @@ This is your link for group %@! 新しい連絡先: notification - - New database archive - 新しいデータベースのアーカイブ - No comment provided by engineer. - New desktop app! 新しいデスクトップアプリ! @@ -3649,11 +4774,19 @@ This is your link for group %@! 新たな表示名 No comment provided by engineer. + + New events + notification + New in %@ %@ の新機能 No comment provided by engineer. + + New media options + No comment provided by engineer. + New member role 新しいメンバーの役割 @@ -3669,6 +4802,10 @@ This is your link for group %@! 新しいパスフレーズ… No comment provided by engineer. + + New server + No comment provided by engineer. + No いいえ @@ -3679,6 +4816,18 @@ This is your link for group %@! アプリのパスワードはありません Authentication unavailable + + No chats + No comment provided by engineer. + + + No chats found + No comment provided by engineer. + + + No chats in list %@ + No comment provided by engineer. + No contacts selected 連絡先が選択されてません @@ -3699,6 +4848,10 @@ This is your link for group %@! デバイストークンがありません! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + item status description + No filtered chats フィルタされたチャットはありません @@ -3714,20 +4867,94 @@ This is your link for group %@! 履歴はありません No comment provided by engineer. + + No info, try to reload + No comment provided by engineer. + + + No media & file servers. + servers error + + + No message + No comment provided by engineer. + + + No message servers. + servers error + + + No network connection + No comment provided by engineer. + + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message 音声メッセージを録音する権限がありません No comment provided by engineer. + + No push server + 自分のみ + No comment provided by engineer. + No received or sent files 送受信済みのファイルがありません No comment provided by engineer. + + No servers for private message routing. + servers error + + + No servers to receive files. + servers error + + + No servers to receive messages. + servers error + + + No servers to send files. + servers error + + + No token! + alert title + + + No unread chats + No comment provided by engineer. + + + No user identifiers. + 世界初のユーザーIDのないプラットフォーム|設計も元からプライベート。 + No comment provided by engineer. + Not compatible! No comment provided by engineer. + + Notes + No comment provided by engineer. + + + Nothing selected + No comment provided by engineer. + + + Nothing to forward! + alert title + Notifications 通知 @@ -3738,6 +4965,18 @@ This is your link for group %@! 通知が無効になっています! No comment provided by engineer. + + Notifications error + alert title + + + Notifications privacy + No comment provided by engineer. + + + Notifications status + alert title + Now admins can: - delete members' messages. @@ -3754,36 +4993,35 @@ This is your link for group %@! Off オフ - No comment provided by engineer. + blur media Ok OK - No comment provided by engineer. + alert button Old database 古いデータベース No comment provided by engineer. - - Old database archive - 過去のデータベースアーカイブ - No comment provided by engineer. - One-time invitation link 使い捨ての招待リンク No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - 接続にオニオンのホストが必要となります。VPN を有効にする必要があります。 + + Onion hosts will be **required** for connection. +Requires compatible VPN. + 接続にオニオンのホストが必要となります。 +VPN を有効にする必要があります。 No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - オニオンのホストが利用可能時に使われます。VPN を有効にする必要があります。 + + Onion hosts will be used when available. +Requires compatible VPN. + オニオンのホストが利用可能時に使われます。 +VPN を有効にする必要があります。 No comment provided by engineer. @@ -3791,11 +5029,19 @@ This is your link for group %@! オニオンのホストが使われません。 No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. **2 レイヤーのエンドツーエンド暗号化**を使用して送信されたユーザー プロファイル、連絡先、グループ、メッセージを保存できるのはクライアント デバイスのみです。 No comment provided by engineer. + + Only delete conversation + No comment provided by engineer. + Only group owners can change group preferences. グループ設定を変えられるのはグループのオーナーだけです。 @@ -3811,6 +5057,14 @@ This is your link for group %@! 音声メッセージを利用可能に設定できるのはグループのオーナーだけです。 No comment provided by engineer. + + Only sender and moderators see it + No comment provided by engineer. + + + Only you and moderators see it + No comment provided by engineer. + Only you can add message reactions. メッセージへのリアクションを追加できるのは、あなただけです。 @@ -3818,7 +5072,7 @@ This is your link for group %@! Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours) - メッセージの完全削除はあなたにしかできません (あなたの連絡先は削除対象とすることができます)。 + メッセージの完全削除はあなたにしかできません (あなたの連絡先は削除対象とすることができます)。(24時間) No comment provided by engineer. @@ -3843,7 +5097,7 @@ This is your link for group %@! Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours) - メッセージを完全削除できるのはあなたの連絡相手だけです (あなたは削除対象とすることができます)。 + メッセージを完全削除できるのはあなたの連絡相手だけです (あなたは削除対象とすることができます)。(24時間) No comment provided by engineer. @@ -3864,13 +5118,17 @@ This is your link for group %@! Open 開く - No comment provided by engineer. + alert action Open Settings 設定を開く No comment provided by engineer. + + Open changes + No comment provided by engineer. + Open chat チャットを開く @@ -3881,28 +5139,38 @@ This is your link for group %@! チャットのコンソールを開く authentication reason + + Open conditions + No comment provided by engineer. + Open group No comment provided by engineer. + + Open link? + alert title + Open migration to another device authentication reason - - Open user profiles - ユーザープロフィールを開く - authentication reason - - - Open-source protocol and code – anybody can run the servers. - プロトコル技術とコードはオープンソースで、どなたでもご自分のサーバを運用できます。 - No comment provided by engineer. - Opening app… No comment provided by engineer. + + Operator + No comment provided by engineer. + + + Operator server + alert title + + + Or import archive file + No comment provided by engineer. + Or paste archive link No comment provided by engineer. @@ -3919,6 +5187,23 @@ This is your link for group %@! Or show this code No comment provided by engineer. + + Or to share privately + No comment provided by engineer. + + + Organize chats into lists + No comment provided by engineer. + + + Other + No comment provided by engineer. + + + Other file errors: +%@ + alert message + PING count PING回数 @@ -3954,6 +5239,10 @@ This is your link for group %@! パスコードを設定しました! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show パスワードを表示する @@ -3980,13 +5269,12 @@ This is your link for group %@! Paste the link you received No comment provided by engineer. - - People can connect to you only via the links you share. - あなたと繋がることができるのは、あなたからリンクを頂いた方のみです。 + + Pending No comment provided by engineer. - - Periodically + + Periodic 定期的に No comment provided by engineer. @@ -3999,11 +5287,24 @@ This is your link for group %@! Picture-in-picture calls No comment provided by engineer. + + Play from the chat list. + No comment provided by engineer. + + + Please ask your contact to enable calls. + No comment provided by engineer. + Please ask your contact to enable sending voice messages. 音声メッセージを有効にするように連絡相手に要求してください。 No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. リンクが正しいかどうかご確認ください。または、連絡相手にもう一度リンクをお求めください。 @@ -4068,59 +5369,107 @@ Error: %@ パスフレーズを失くさないように保管してください。失くすと変更できなくなります。 No comment provided by engineer. + + Please try to disable and re-enable notfications. + token info + + + Please wait for token activation to complete. + token info + + + Please wait for token to be registered. + token info + Polish interface ポーランド語UI No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect サーバアドレスの証明証IDが正しくないかもしれません server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. 添付を含めて、下書きを保存する。 No comment provided by engineer. - - Preset server - プレセットサーバ - No comment provided by engineer. - Preset server address プレセットサーバのアドレス No comment provided by engineer. + + Preset servers + No comment provided by engineer. + Preview プレビュー No comment provided by engineer. + + Previously connected servers + No comment provided by engineer. + Privacy & security プライバシーとセキュリティ No comment provided by engineer. + + Privacy for your customers. + No comment provided by engineer. + + + Privacy policy and conditions of use. + No comment provided by engineer. + Privacy redefined プライバシーの基準を新境地に No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + No comment provided by engineer. + Private filenames プライベートなファイル名 No comment provided by engineer. + + Private media file names. + No comment provided by engineer. + + + Private message routing + No comment provided by engineer. + + + Private message routing 🚀 + No comment provided by engineer. + Private notes + プライベートノート name of notes to self + + Private routing + No comment provided by engineer. + + + Private routing error + No comment provided by engineer. + Profile and server connections プロフィールとサーバ接続 @@ -4131,12 +5480,8 @@ Error: %@ プロフィール画像 No comment provided by engineer. - - Profile name - No comment provided by engineer. - - - Profile name: + + Profile images No comment provided by engineer. @@ -4144,10 +5489,14 @@ Error: %@ プロフィールのパスワード No comment provided by engineer. + + Profile theme + No comment provided by engineer. + Profile update will be sent to your contacts. 連絡先にプロフィール更新のお知らせが届きます。 - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4169,6 +5518,14 @@ Error: %@ メッセージへのリアクションは禁止されています。 No comment provided by engineer. + + Prohibit reporting messages to moderators. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + No comment provided by engineer. + Prohibit sending direct messages to members. メンバー間のダイレクトメッセージを使用禁止にする。 @@ -4189,11 +5546,20 @@ Error: %@ 音声メッセージを使用禁止にする。 No comment provided by engineer. + + Protect IP address + No comment provided by engineer. + Protect app screen アプリ画面を守る No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + No comment provided by engineer. + Protect your chat profiles with a password! チャットのプロフィールをパスワードで保護します! @@ -4209,6 +5575,18 @@ Error: %@ KB あたりのプロトコル タイムアウト No comment provided by engineer. + + Proxied + No comment provided by engineer. + + + Proxied servers + No comment provided by engineer. + + + Proxy requires password + No comment provided by engineer. + Push notifications プッシュ通知 @@ -4227,6 +5605,10 @@ Error: %@ アプリを評価 No comment provided by engineer. + + Reachable chat toolbar + No comment provided by engineer. + React… 反応する… @@ -4235,41 +5617,40 @@ Error: %@ Read 読む - No comment provided by engineer. + swipe action Read more 続きを読む No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - 詳しくは[ユーザーガイド](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)をご覧ください。 - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + 詳しくは[ユーザーガイド](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)をご覧ください。 + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). 詳しくは[ユーザーガイド](https://simplex.chat/docs/guide/readme.html#connect-to-friends)をご覧ください。 No comment provided by engineer. - - Read more in our GitHub repository. - GitHubリポジトリで詳細をご確認ください。 - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). - 詳しくは[GitHubリポジトリ](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)をご覧ください。 + 詳しくは[GitHubリポジトリ](https://github.com/simplex-chat/simplex-chat#readme)をご覧ください。 No comment provided by engineer. Receipts are disabled No comment provided by engineer. + + Receive errors + No comment provided by engineer. + Received at 受信 @@ -4290,6 +5671,18 @@ Error: %@ 受信したメッセージ message info title + + Received messages + No comment provided by engineer. + + + Received reply + No comment provided by engineer. + + + Received total + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. 開発中の機能です!相手のクライアントが4.2でなければ機能しません。アドレス変更が完了すると、会話にメッセージが出ます。連絡相手 (またはグループのメンバー) からメッセージを受信できないかをご確認ください。 @@ -4309,16 +5702,40 @@ Error: %@ Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + No comment provided by engineer. + Recipients see updates as you type them. 受信者には、入力時に更新内容が表示されます。 No comment provided by engineer. + + Reconnect + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. 接続されているすべてのサーバーを再接続して、メッセージを強制的に配信します。 追加のトラフィックを使用します。 No comment provided by engineer. + + Reconnect all servers + No comment provided by engineer. + + + Reconnect all servers? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + No comment provided by engineer. + + + Reconnect server? + No comment provided by engineer. + Reconnect servers? サーバーに再接続しますか? @@ -4339,10 +5756,23 @@ Error: %@ 電池使用量低減 No comment provided by engineer. + + Register + No comment provided by engineer. + + + Register notification token? + token info + + + Registered + token status text + Reject 拒否 - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4369,6 +5799,14 @@ Error: %@ 削除 No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + + + Remove image + No comment provided by engineer. + Remove member メンバーを除名する @@ -4424,6 +5862,46 @@ Error: %@ 返信 chat item action + + Report + chat item action + + + Report content: only group moderators will see it. + report reason + + + Report member profile: only group moderators will see it. + report reason + + + Report other: only group moderators will see it. + report reason + + + Report reason? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + report reason + + + Report violation: only group moderators will see it. + report reason + + + Report: %@ + report in notification + + + Reporting messages to moderators is prohibited. + No comment provided by engineer. + + + Reports + No comment provided by engineer. + Required 必須 @@ -4434,16 +5912,36 @@ Error: %@ 戻す No comment provided by engineer. + + Reset all hints + No comment provided by engineer. + + + Reset all statistics + No comment provided by engineer. + + + Reset all statistics? + No comment provided by engineer. + Reset colors 既定の色に戻す No comment provided by engineer. + + Reset to app theme + No comment provided by engineer. + Reset to defaults 既定に戻す No comment provided by engineer. + + Reset to user theme + No comment provided by engineer. + Restart the app to create a new chat profile 新しいチャットプロファイルを作成するためにアプリを再起動する @@ -4483,9 +5981,8 @@ Error: %@ 開示する chat item action - - Revert - 元に戻す + + Review conditions No comment provided by engineer. @@ -4513,9 +6010,16 @@ Error: %@ チャット起動 No comment provided by engineer. - - SMP servers - SMPサーバ + + SMP server + No comment provided by engineer. + + + SOCKS proxy + No comment provided by engineer. + + + Safely receive files No comment provided by engineer. @@ -4525,43 +6029,42 @@ Error: %@ Save 保存 - chat item action + alert button +chat item action Save (and notify contacts) 保存(連絡先に通知) - No comment provided by engineer. + alert button Save and notify contact 保存して、連絡先にに知らせる - No comment provided by engineer. + alert button Save and notify group members 保存して、グループのメンバーにに知らせる No comment provided by engineer. + + Save and reconnect + No comment provided by engineer. + Save and update group profile グループプロファイルの保存と更新 No comment provided by engineer. - - Save archive - アーカイブを保存 - No comment provided by engineer. - - - Save auto-accept settings - 自動受け入れ設定を保存する - No comment provided by engineer. - Save group profile グループプロフィールの保存 No comment provided by engineer. + + Save list + No comment provided by engineer. + Save passphrase and open chat パスフレーズをを保存して、チャットを開始 @@ -4575,7 +6078,7 @@ Error: %@ Save preferences? この設定でよろしいですか? - No comment provided by engineer. + alert title Save profile password @@ -4590,27 +6093,46 @@ Error: %@ Save servers? サーバを保存しますか? - No comment provided by engineer. - - - Save settings? - 設定を保存しますか? - No comment provided by engineer. + alert title Save welcome message? ウェルカムメッセージを保存しますか? No comment provided by engineer. + + Save your profile? + alert title + + + Saved + No comment provided by engineer. + Saved WebRTC ICE servers will be removed 保存されたWebRTC ICEサーバは削除されます No comment provided by engineer. + + Saved from + No comment provided by engineer. + Saved message message info title + + Saving %lld messages + No comment provided by engineer. + + + Scale + No comment provided by engineer. + + + Scan / Paste link + No comment provided by engineer. + Scan QR code QRコードを読み込む @@ -4648,11 +6170,19 @@ Error: %@ Search or paste SimpleX link No comment provided by engineer. + + Secondary + No comment provided by engineer. + Secure queue 待ち行列セキュリティ確認 server test step + + Secured + No comment provided by engineer. + Security assessment セキュリティ評価 @@ -4666,6 +6196,18 @@ Error: %@ Select 選択 + chat item action + + + Select chat profile + No comment provided by engineer. + + + Selected %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. No comment provided by engineer. @@ -4702,11 +6244,6 @@ Error: %@ Send delivery receipts to No comment provided by engineer. - - Send direct message - ダイレクトメッセージを送信 - No comment provided by engineer. - Send direct message to connect ダイレクトメッセージを送信して接続する @@ -4717,6 +6254,10 @@ Error: %@ 消えるメッセージを送信 No comment provided by engineer. + + Send errors + No comment provided by engineer. + Send link previews リンクのプレビューを送信 @@ -4727,14 +6268,25 @@ Error: %@ ライブメッセージを送信 No comment provided by engineer. + + Send message to enable calls. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + No comment provided by engineer. + Send notifications 通知を送信する No comment provided by engineer. - - Send notifications: - 通知を送信する: + + Send private reports No comment provided by engineer. @@ -4758,7 +6310,7 @@ Error: %@ Sender cancelled file transfer. 送信者がファイル転送をキャンセルしました。 - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4809,6 +6361,10 @@ Error: %@ 送信日時: %@ copied message info + + Sent directly + No comment provided by engineer. + Sent file event 送信済みファイルイベント @@ -4819,11 +6375,59 @@ Error: %@ 送信 message info title + + Sent messages + No comment provided by engineer. + Sent messages will be deleted after set time. 一定時間が経ったら送信されたメッセージが削除されます。 No comment provided by engineer. + + Sent reply + No comment provided by engineer. + + + Sent total + No comment provided by engineer. + + + Sent via proxy + No comment provided by engineer. + + + Server + No comment provided by engineer. + + + Server added to operator %@. + alert message + + + Server address + No comment provided by engineer. + + + Server address is incompatible with network settings. + srv error text. + + + Server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Server operator changed. + alert title + + + Server operators + No comment provided by engineer. + + + Server protocol changed. + alert title + Server requires authorization to create queues, check password キューを作成するにはサーバーの認証が必要です。パスワードを確認してください @@ -4839,11 +6443,31 @@ Error: %@ サーバテスト失敗! No comment provided by engineer. + + Server type + No comment provided by engineer. + + + Server version is incompatible with network settings. + srv error text + + + Server version is incompatible with your app: %@. + No comment provided by engineer. + Servers サーバ No comment provided by engineer. + + Servers info + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + No comment provided by engineer. + Session code No comment provided by engineer. @@ -4853,11 +6477,19 @@ Error: %@ 1日に設定 No comment provided by engineer. + + Set chat name… + No comment provided by engineer. + Set contact name… 連絡先の名前を設定… No comment provided by engineer. + + Set default theme + No comment provided by engineer. + Set group preferences グループの設定を行う @@ -4868,6 +6500,10 @@ Error: %@ システム認証の代わりに設定します。 No comment provided by engineer. + + Set message expiration in chats. + No comment provided by engineer. + Set passcode パスコードを設定する @@ -4897,24 +6533,49 @@ Error: %@ 設定 No comment provided by engineer. + + Settings were changed. + alert message + + + Shape profile images + No comment provided by engineer. + Share 共有する - chat item action + alert action +chat item action Share 1-time link 使い捨てのリンクを共有 No comment provided by engineer. + + Share 1-time link with a friend + No comment provided by engineer. + + + Share SimpleX address on social media. + No comment provided by engineer. + Share address アドレスを共有する No comment provided by engineer. + + Share address publicly + No comment provided by engineer. + Share address with contacts? アドレスを連絡先と共有しますか? + alert title + + + Share from other apps. No comment provided by engineer. @@ -4922,15 +6583,27 @@ Error: %@ リンクを送る No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link No comment provided by engineer. + + Share to SimpleX + No comment provided by engineer. + Share with contacts 連絡先と共有する No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code No comment provided by engineer. @@ -4950,21 +6623,41 @@ Error: %@ 最新のメッセージを表示 No comment provided by engineer. + + Show message status + No comment provided by engineer. + + + Show percentage + No comment provided by engineer. + Show preview プレビューを表示 No comment provided by engineer. + + Show → on messages sent via private routing. + No comment provided by engineer. + Show: 表示する: No comment provided by engineer. + + SimpleX + No comment provided by engineer. + SimpleX Address SimpleXアドレス No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. SimpleX Chat のセキュリティは Trail of Bits によって監査されました。 @@ -4995,6 +6688,18 @@ Error: %@ SimpleXアドレス No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + No comment provided by engineer. + + + SimpleX address or 1-time link? + No comment provided by engineer. + + + SimpleX channel link + simplex link type + SimpleX contact address SimpleX連絡先アドレス @@ -5013,6 +6718,14 @@ Error: %@ SimpleX links SimpleXリンク + chat feature + + + SimpleX links are prohibited. + No comment provided by engineer. + + + SimpleX links not allowed No comment provided by engineer. @@ -5020,11 +6733,19 @@ Error: %@ SimpleX使い捨て招待リンク simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode シークレットモードの簡素化 No comment provided by engineer. + + Size + No comment provided by engineer. + Skip スキップ @@ -5040,16 +6761,46 @@ Error: %@ 小グループ(最大20名) No comment provided by engineer. + + Soft + blur media + + + Some app settings were not migrated. + No comment provided by engineer. + + + Some file(s) were not exported: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. インポート中に致命的でないエラーが発生しました - 詳細はチャットコンソールを参照してください。 No comment provided by engineer. + + Some non-fatal errors occurred during import: + No comment provided by engineer. + + + Some servers failed the test: +%@ + alert message + Somebody 誰か notification title + + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + No comment provided by engineer. + Start chat チャットを開始する @@ -5064,6 +6815,14 @@ Error: %@ 移行の開始 No comment provided by engineer. + + Starting from %@. + No comment provided by engineer. + + + Statistics + No comment provided by engineer. + Stop 停止 @@ -5078,11 +6837,6 @@ Error: %@ Stop chat No comment provided by engineer. - - Stop chat to enable database actions - チャットを停止してデータベースアクションを有効にします - No comment provided by engineer. - Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. データベースのエクスポート、読み込み、削除するにはチャットを閉じてからです。チャットを閉じると送受信ができなくなります。 @@ -5111,27 +6865,55 @@ Error: %@ Stop sharing 共有を停止 - No comment provided by engineer. + alert action Stop sharing address? アドレスの共有を停止しますか? - No comment provided by engineer. + alert title Stopping chat No comment provided by engineer. + + Storage + No comment provided by engineer. + + + Strong + blur media + Submit 送信 No comment provided by engineer. + + Subscribed + No comment provided by engineer. + + + Subscription errors + No comment provided by engineer. + + + Subscriptions ignored + No comment provided by engineer. + Support SimpleX Chat Simplex Chatを支援 No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System システム @@ -5142,11 +6924,19 @@ Error: %@ システム認証 No comment provided by engineer. + + TCP connection + No comment provided by engineer. + TCP connection timeout TCP接続タイムアウト No comment provided by engineer. + + TCP port for messaging + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5162,11 +6952,19 @@ Error: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture 写真を撮影 No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + No comment provided by engineer. + Tap button ボタンをタップ @@ -5199,16 +6997,19 @@ Error: %@ Tap to scan No comment provided by engineer. - - Tap to start a new chat - タップして新しいチャットを始める - No comment provided by engineer. + + Temporary file error + file error alert title Test failed at step %@. テストはステップ %@ で失敗しました。 server test failure + + Test notifications + No comment provided by engineer. + Test server テストサーバ @@ -5222,7 +7023,7 @@ Error: %@ Tests failed! テストは失敗しました! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5239,11 +7040,6 @@ Error: %@ ユーザーに感謝します – Weblate 経由で貢献してください! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - 世界初のユーザーIDのないプラットフォーム|設計も元からプライベート。 - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5256,6 +7052,14 @@ It can happen because of some bug or when the connection is compromised.アプリは、メッセージや連絡先のリクエストを受信したときに通知することができます - 設定を開いて有効にしてください。 No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + No comment provided by engineer. + The attempt to change database passphrase was not completed. データベースのパスフレーズ変更が完了してません。 @@ -5265,6 +7069,10 @@ It can happen because of some bug or when the connection is compromised.The code you scanned is not a SimpleX link QR code. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + No comment provided by engineer. + The connection you accepted will be cancelled! 承認済の接続がキャンセルされます! @@ -5285,6 +7093,11 @@ It can happen because of some bug or when the connection is compromised.暗号化は機能しており、新しい暗号化への同意は必要ありません。接続エラーが発生する可能性があります! No comment provided by engineer. + + The future of messaging + 次世代のプライバシー・メッセンジャー + No comment provided by engineer. + The hash of the previous message is different. 以前のメッセージとハッシュ値が異なります。 @@ -5300,9 +7113,12 @@ It can happen because of some bug or when the connection is compromised.メッセージは、すべてのメンバーに対してモデレートされたものとして表示されます。 No comment provided by engineer. - - The next generation of private messaging - 次世代のプライバシー・メッセンジャー + + The messages will be deleted for all members. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. No comment provided by engineer. @@ -5310,9 +7126,12 @@ It can happen because of some bug or when the connection is compromised.古いデータベースは移行時に削除されなかったので、削除することができます。 No comment provided by engineer. - - The profile is only shared with your contacts. - プロフィールは連絡先にしか共有されません。 + + The same conditions will apply to operator **%@**. + No comment provided by engineer. + + + The second preset operator in the app! No comment provided by engineer. @@ -5330,13 +7149,24 @@ It can happen because of some bug or when the connection is compromised.現在のチャットプロフィールの新しい接続のサーバ **%@**。 No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. No comment provided by engineer. - - Theme - テーマ + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + + + Themes + No comment provided by engineer. + + + These conditions will also apply for: **%@**. No comment provided by engineer. @@ -5359,6 +7189,10 @@ It can happen because of some bug or when the connection is compromised.選択中の以前の送受信したメッセージが削除されます (※元に戻せません※)。数分かかります。 No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. あなたのプロフィール、連絡先、メッセージ、ファイルが完全削除されます (※元に戻せません※)。 @@ -5397,11 +7231,27 @@ It can happen because of some bug or when the connection is compromised.This is your own one-time link! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + No comment provided by engineer. + + + This message was deleted or not received yet. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. この設定は現在のチャットプロフィール **%@** のメッセージに適用されます。 No comment provided by engineer. + + Title + No comment provided by engineer. + To ask any questions and to receive updates: 質問や最新情報を受け取るには: @@ -5421,9 +7271,8 @@ It can happen because of some bug or when the connection is compromised.新規に接続する場合 No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - プライバシーを保護するために、SimpleX には、他のすべてのプラットフォームで使用されるユーザー ID の代わりに、連絡先ごとに個別のメッセージ キューの識別子があります。 + + To protect against your link being replaced, you can compare contact security codes. No comment provided by engineer. @@ -5431,6 +7280,10 @@ It can happen because of some bug or when the connection is compromised.時間帯を漏らさないために、画像と音声ファイルはUTCを使います。 No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5438,6 +7291,23 @@ You will be prompted to complete authentication before this feature is enabled.< オンにするには、認証ステップが行われます。 No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + プライバシーを保護するために、SimpleX には、他のすべてのプラットフォームで使用されるユーザー ID の代わりに、連絡先ごとに個別のメッセージ キューの識別子があります。 + No comment provided by engineer. + + + To receive + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. 音声メッセージを録音する場合は、マイクの使用を許可してください。 @@ -5448,25 +7318,53 @@ You will be prompted to complete authentication before this feature is enabled.< 非表示のプロフィールを表示するには、**チャット プロフィール** ページの検索フィールドに完全なパスワードを入力します。 No comment provided by engineer. + + To send + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. インスタント プッシュ通知をサポートするには、チャット データベースを移行する必要があります。 No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. エンドツーエンド暗号化を確認するには、ご自分の端末と連絡先の端末のコードを比べます (スキャンします)。 No comment provided by engineer. + + Toggle chat list: + No comment provided by engineer. + Toggle incognito when connecting. No comment provided by engineer. + + Token status: %@. + token status + + + Toolbar opacity + No comment provided by engineer. + + + Total + No comment provided by engineer. + Transport isolation トランスポート隔離 No comment provided by engineer. + + Transport sessions + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). この連絡先からのメッセージの受信に使用されるサーバーに接続しようとしています (エラー: %@)。 @@ -5516,10 +7414,9 @@ You will be prompted to complete authentication before this feature is enabled.< Unblock member? No comment provided by engineer. - - Unexpected error: %@ - 予期しないエラー: %@ - item status description + + Undelivered messages + No comment provided by engineer. Unexpected migration state @@ -5529,7 +7426,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unfav. お気に入りを取り消す。 - No comment provided by engineer. + swipe action Unhide @@ -5566,6 +7463,10 @@ You will be prompted to complete authentication before this feature is enabled.< 不明なエラー No comment provided by engineer. + + Unknown servers! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. iOS 通話インターフェイスを使用しない場合は、中断を避けるために「おやすみモード」を有効にしてください。 @@ -5599,11 +7500,15 @@ To connect, please ask your contact to create another connection link and check Unmute ミュート解除 - No comment provided by engineer. + notification label action Unread 未読 + swipe action + + + Unsupported connection link No comment provided by engineer. @@ -5615,11 +7520,6 @@ To connect, please ask your contact to create another connection link and check 更新 No comment provided by engineer. - - Update .onion hosts setting? - .onionのホスト設定を更新しますか? - No comment provided by engineer. - Update database passphrase データベースのパスフレーズを更新 @@ -5630,9 +7530,12 @@ To connect, please ask your contact to create another connection link and check ネットワーク設定を更新しますか? No comment provided by engineer. - - Update transport isolation mode? - トランスポート隔離モードを更新しますか? + + Update settings? + No comment provided by engineer. + + + Updated conditions No comment provided by engineer. @@ -5640,16 +7543,15 @@ To connect, please ask your contact to create another connection link and check 設定を更新すると、全サーバにクライントの再接続が行われます。 No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - 設定を更新すると、全サーバにクライントの再接続が行われます。 - No comment provided by engineer. - Upgrade and open chat アップグレードしてチャットを開く No comment provided by engineer. + + Upload errors + No comment provided by engineer. + Upload failed No comment provided by engineer. @@ -5659,20 +7561,44 @@ To connect, please ask your contact to create another connection link and check ファイルをアップロードする server test step + + Uploaded + No comment provided by engineer. + + + Uploaded files + No comment provided by engineer. + Uploading archive No comment provided by engineer. + + Use %@ + No comment provided by engineer. + Use .onion hosts .onionホストを使う No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? SimpleX チャット サーバーを使用しますか? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat チャット @@ -5683,6 +7609,14 @@ To connect, please ask your contact to create another connection link and check 現在のプロファイルを使用する No comment provided by engineer. + + Use for files + No comment provided by engineer. + + + Use for messages + No comment provided by engineer. + Use for new connections 新しい接続に使う @@ -5706,23 +7640,45 @@ To connect, please ask your contact to create another connection link and check Use only local notifications? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + No comment provided by engineer. + + + Use private routing with unknown servers. + No comment provided by engineer. + Use server サーバを使う No comment provided by engineer. + + Use servers + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. No comment provided by engineer. - - User profile - ユーザープロフィール + + Use the app with one hand. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - .onionホストを使用するには、互換性のあるVPNプロバイダーが必要です。 + + Use web port + No comment provided by engineer. + + + User selection + No comment provided by engineer. + + + Username No comment provided by engineer. @@ -5789,11 +7745,19 @@ To connect, please ask your contact to create another connection link and check 1GBまでのビデオとファイル No comment provided by engineer. + + View conditions + No comment provided by engineer. + View security code セキュリティコードを確認 No comment provided by engineer. + + View updated conditions + No comment provided by engineer. + Visible history chat feature @@ -5808,11 +7772,15 @@ To connect, please ask your contact to create another connection link and check このチャットでは音声メッセージが使用禁止です。 No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. このグループでは音声メッセージが使用禁止です。 No comment provided by engineer. + + Voice messages not allowed + No comment provided by engineer. + Voice messages prohibited! 音声メッセージは使用禁止です! @@ -5842,6 +7810,14 @@ To connect, please ask your contact to create another connection link and check ビデオ待機中 No comment provided by engineer. + + Wallpaper accent + No comment provided by engineer. + + + Wallpaper background + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures No comment provided by engineer. @@ -5880,9 +7856,12 @@ To connect, please ask your contact to create another connection link and check 利用可能時に No comment provided by engineer. - - When people request to connect, you can accept or reject it. - 接続が要求されたら、それを受け入れるか拒否するかを選択できます。 + + When connecting audio and video calls. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. No comment provided by engineer. @@ -5890,6 +7869,18 @@ To connect, please ask your contact to create another connection link and check 連絡相手にシークレットモードのプロフィールを共有すると、その連絡相手に招待されたグループでも同じプロフィールが使われます。 No comment provided by engineer. + + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + No comment provided by engineer. + + + Wired ethernet + No comment provided by engineer. + With encrypted files and media. No comment provided by engineer. @@ -5903,24 +7894,34 @@ To connect, please ask your contact to create another connection link and check With reduced battery usage. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + alert message + Wrong database passphrase データベースのパスフレーズが違います No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + file error text + Wrong passphrase! パスフレーズが違います! No comment provided by engineer. - - XFTP servers - XFTPサーバ - No comment provided by engineer. - - - You - あなた + + XFTP server No comment provided by engineer. @@ -5947,6 +7948,10 @@ To connect, please ask your contact to create another connection link and check すでに %@ に接続されています。 No comment provided by engineer. + + You are already connected with %@. + No comment provided by engineer. + You are already connecting to %@. No comment provided by engineer. @@ -5986,11 +7991,23 @@ Repeat join request? グループ招待が届きました No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. デバイスやアプリの認証を行わずに、ロック画面から通話を受けることができます。 No comment provided by engineer. + + You can change it in Appearance settings. + No comment provided by engineer. + + + You can configure servers via settings. + No comment provided by engineer. + You can create it later 後からでも作成できます @@ -6017,13 +8034,22 @@ Repeat join request? You can make it visible to your SimpleX contacts via Settings. + 設定でSimpleXの連絡先に表示させることができます。 No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ %@ にメッセージを送信できるようになりました notification body + + You can send messages to %@ from Archived contacts. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + No comment provided by engineer. + You can set lock screen notification preview via settings. 設定からロック画面の通知プレビューを設定できます。 @@ -6039,16 +8065,15 @@ Repeat join request? このアドレスを連絡先と共有して、**%@** に接続できるようにすることができます。 No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - アドレスをリンクやQRコードとして共有することで、誰でも接続することができます。 - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app アプリの設定/データベースから、またはアプリを再起動することでチャットを開始できます No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. 設定からSimpleXのロックをオンにすることができます。 @@ -6061,23 +8086,23 @@ Repeat join request? You can view invitation link again in connection details. - No comment provided by engineer. + alert message You can't send messages! メッセージを送信できませんでした! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - あなたはメッセージの受信に使用するサーバーを制御し、連絡先はあなたがメッセージの送信に使用するサーバーを使用することができます。 - No comment provided by engineer. - You could not be verified; please try again. 確認できませんでした。 もう一度お試しください。 No comment provided by engineer. + + You decide who can connect. + あなたと繋がることができるのは、あなたからリンクを頂いた方のみです。 + No comment provided by engineer. + You have already requested connection via this address! No comment provided by engineer. @@ -6087,11 +8112,6 @@ Repeat join request? Repeat connection request? No comment provided by engineer. - - You have no chats - あなたはチャットがありません - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. アプリ起動時にパスフレーズを入力しなければなりません。端末に保存されてません。 @@ -6112,11 +8132,23 @@ Repeat connection request? グループに参加しました。招待をくれたメンバーに接続してます。 No comment provided by engineer. + + You may migrate the exported database. + No comment provided by engineer. + + + You may save the exported archive. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. あなたの最新データベースを1つの端末にしか使わなければ、一部の連絡先からメッセージが届きかねます。 No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. 音声メッセージを送るには、連絡相手からの音声メッセージを許可しなければなりません。 @@ -6132,6 +8164,10 @@ Repeat connection request? グループの招待を送りました No comment provided by engineer. + + You should receive notifications. + token info + You will be connected to group when the group host's device is online, please wait or check later! グループのホスト端末がオンラインになったら、接続されます。後でチェックするか、しばらくお待ちください! @@ -6165,6 +8201,10 @@ Repeat connection request? ミュートされたプロフィールがアクティブな場合でも、そのプロフィールからの通話や通知は引き続き受信します。 No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. このグループからのメッセージが届かなくなります。チャットの履歴が残ります。 @@ -6185,31 +8225,16 @@ Repeat connection request? シークレットモードのプロフィールでこのグループに参加しています。メインのプロフィールを守るために、招待することができません No comment provided by engineer. - - Your %@ servers - あなたの %@ サーバー - No comment provided by engineer. - Your ICE servers あなたのICEサーバ No comment provided by engineer. - - Your SMP servers - あなたのSMPサーバ - No comment provided by engineer. - Your SimpleX address あなたのSimpleXアドレス No comment provided by engineer. - - Your XFTP servers - あなたのXFTPサーバ - No comment provided by engineer. - Your calls あなたの通話 @@ -6225,16 +8250,17 @@ Repeat connection request? チャット データベースは暗号化されていません - 暗号化するにはパスフレーズを設定してください。 No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles あなたのチャットプロフィール No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - 接続を完了するには、連絡相手がオンラインになる必要があります。 -この接続をキャンセルして、連絡先を削除をすることもできます (後でやり直すこともできます)。 + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. No comment provided by engineer. @@ -6252,6 +8278,10 @@ You can cancel this connection and remove the contact (and try later with a new 連絡先は接続されたままになります。 No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. 現在のチャット データベースは削除され、インポートされたデータベースに置き換えられます。 @@ -6281,33 +8311,34 @@ You can cancel this connection and remove the contact (and try later with a new あなたのプロファイル **%@** が共有されます。 No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - プロフィールはデバイスに保存され、連絡先とのみ共有されます。 -SimpleX サーバーはあなたのプロファイルを参照できません。 + + Your profile is stored on your device and only shared with your contacts. + プロフィールは連絡先にしか共有されません。 No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - あなたのプロフィール、連絡先、送信したメッセージがご自分の端末に保存されます。 + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + プロフィールはデバイスに保存され、連絡先とのみ共有されます。 SimpleX サーバーはあなたのプロファイルを参照できません。 No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your random profile あなたのランダム・プロフィール No comment provided by engineer. - - Your server - あなたのサーバ - No comment provided by engineer. - Your server address あなたのサーバアドレス No comment provided by engineer. + + Your servers + No comment provided by engineer. + Your settings あなたの設定 @@ -6348,11 +8379,19 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 受けた通話 call status + + accepted invitation + chat list item title + admin 管理者 member role + + admins + feature role + agreeing encryption for %@… %@の暗号化に同意しています… @@ -6363,6 +8402,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 暗号化に同意しています… chat item text + + all members + feature role + always 常に @@ -6372,6 +8415,14 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 and %lld other events No comment provided by engineer. + + archived report + No comment provided by engineer. + + + attempts + No comment provided by engineer. + audio call (not e2e encrypted) 音声通話 (エンドツーエンド暗号化なし) @@ -6401,13 +8452,18 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 blocked by admin - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold 太文字 No comment provided by engineer. + + call + No comment provided by engineer. + call error 通話エラー @@ -6510,7 +8566,7 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 connecting… 接続待ち… - chat list item title + No comment provided by engineer. connection established @@ -6556,10 +8612,15 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 time unit + + decryption errors + No comment provided by engineer. + default (%@) デフォルト (%@) - pref value + delete after time +pref value default (no) @@ -6605,6 +8666,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 重複メッセージ integrity error chat item + + duplicates + No comment provided by engineer. + e2e encrypted エンドツーエンド暗号化済み @@ -6680,9 +8745,12 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 エラー No comment provided by engineer. - - event happened - イベント発生 + + expired + No comment provided by engineer. + + + forwarded No comment provided by engineer. @@ -6710,6 +8778,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 iOS キーチェーンは、アプリを再起動するかパスフレーズを変更した後にパスフレーズを安全に保存するために使用され、プッシュ通知を受信できるようになります。 No comment provided by engineer. + + inactive + No comment provided by engineer. + incognito via contact address link 連絡先リンク経由でシークレットモード @@ -6750,6 +8822,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 グループ %@ への招待 group name + + invite + No comment provided by engineer. + invited 招待済み @@ -6804,6 +8880,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 接続中 rcv group event chat item + + message + No comment provided by engineer. + message received メッセージを受信 @@ -6829,6 +8909,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 %@ によってモデレートされた marked deleted chat item preview text + + moderator + member role + months @@ -6837,7 +8921,7 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 never 一度も - No comment provided by engineer. + delete after time new message @@ -6868,8 +8952,8 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 off オフ enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -6886,16 +8970,36 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 オン group pref value + + other + No comment provided by engineer. + + + other errors + No comment provided by engineer. + owner オーナー member role + + owners + feature role + peer-to-peer P2P No comment provided by engineer. + + pending + No comment provided by engineer. + + + pending approval + No comment provided by engineer. + quantum resistant e2e encryption chat item text @@ -6910,6 +9014,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 確認を受け取りました… No comment provided by engineer. + + rejected + No comment provided by engineer. + rejected call 拒否した通話 @@ -6938,6 +9046,22 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 あなたを除名しました rcv group event chat item + + requested to connect + chat list item title + + + saved + No comment provided by engineer. + + + saved from %@ + No comment provided by engineer. + + + search + No comment provided by engineer. + sec @@ -6962,6 +9086,12 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 send direct message No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + queue info + set new contact address profile update event chat item @@ -6998,10 +9128,18 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 不明 connection info + + unknown servers + No comment provided by engineer. + unknown status No comment provided by engineer. + + unprotected + No comment provided by engineer. + updated group profile グループプロフィールを更新しました @@ -7040,6 +9178,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 リレー経由 No comment provided by engineer. + + video + No comment provided by engineer. + video call (not e2e encrypted) ビデオ通話 (非エンドツーエンド暗号化) @@ -7065,11 +9207,19 @@ SimpleX サーバーはあなたのプロファイルを参照できません。 time unit + + when IP hidden + No comment provided by engineer. + yes はい pref value + + you + No comment provided by engineer. + you are invited to group グループ招待が届きました @@ -7142,7 +9292,7 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
- +
@@ -7178,7 +9328,7 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
- +
@@ -7198,4 +9348,205 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
+ +
+ +
+ + + %d new events + notification body + + + From %d chat(s) + notification body + + + From: %@ + notification body + + + New events + notification + + + New messages + notification + + +
+ +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + App is locked! + No comment provided by engineer. + + + Cancel + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Cannot forward message + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database encrypted! + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database passphrase is required to open chat. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Passphrase + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + No comment provided by engineer. + + + Sending a message takes longer than expected. + No comment provided by engineer. + + + Sending message… + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Slow network? + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wait + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/contents.json b/apps/ios/SimpleX Localizations/ja.xcloc/contents.json index 7d3c224e68..ce6052fc44 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/ja.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "ja", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/ko.xcloc/Localized Contents/ko.xliff b/apps/ios/SimpleX Localizations/ko.xcloc/Localized Contents/ko.xliff index cc7b5522e6..019f63cbc0 100644 --- a/apps/ios/SimpleX Localizations/ko.xcloc/Localized Contents/ko.xliff +++ b/apps/ios/SimpleX Localizations/ko.xcloc/Localized Contents/ko.xliff @@ -5,9 +5,11 @@ - + + + No comment provided by engineer. @@ -22,220 +24,264 @@ No comment provided by engineer. - + ( + ( No comment provided by engineer. - + (can be copied) + (복사 가능) No comment provided by engineer. - + !1 colored! + !1 색상 적용됨! No comment provided by engineer. - + #secret# + #비밀# No comment provided by engineer. - + %@ + %@ No comment provided by engineer. - + %@ %@ + %@ %@ No comment provided by engineer. - + %@ / %@ + %@ / %@ No comment provided by engineer. - + %@ is connected! + %@이(가) 연결되었습니다! notification title - + %@ is not verified + %@은(는) 인증되지 않았습니다 No comment provided by engineer. - + %@ is verified + %@ 은(는) 인증되었습니다 No comment provided by engineer. - + %@ wants to connect! + %@ 연결을 원함! notification title - + %d days + %d 일 message ttl - + %d hours + %d 시간 message ttl - + %d min + %d 분 message ttl - + %d months + %d 개월 message ttl - + %d sec + %d 초 message ttl - + %d skipped message(s) + 건너뛰기 메시지 %d개 integrity error chat item - + %lld + %lld No comment provided by engineer. - + %lld %@ + %lld %@ No comment provided by engineer. - + %lld contact(s) selected + %lld명의 연락처 선택됨 No comment provided by engineer. - + %lld file(s) with total size of %@ + 총 크기가 %@인 파일 %lld 개 No comment provided by engineer. - + %lld members + %lld명의 멤버 No comment provided by engineer. - + %lld second(s) + %lld 초 No comment provided by engineer. - + %lldd + %lldd No comment provided by engineer. - + %lldh + %lldh No comment provided by engineer. - + %lldk + %lldk No comment provided by engineer. - + %lldm + %lldm No comment provided by engineer. - + %lldmth + %lldmth No comment provided by engineer. %llds + No comment provided by engineer. %lldw No comment provided by engineer. - + ( + ( No comment provided by engineer. - + ) - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. + ) No comment provided by engineer. **Create link / QR code** for your contact to use. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. + **비공개**: 20분마다 새로운 메시지를 확인합니다. 푸시 서버에는 장치 토큰만 공유됩니다. 연락처 수나 메세지 메타데이터가 표시되지 않습니다. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. + **비공개**: SimpleX 채팅 푸시 서버를 사용하지 마세요. 앱은 사용 빈도에 따라 시스템이 허용하는 백그라운드에서 메세지를 확인합니다. No comment provided by engineer. **Paste received link** or open it in the browser and tap **Open in mobile app**. No comment provided by engineer. - + **Please note**: you will NOT be able to recover or change passphrase if you lose it. + **참고**: 비밀번호를 분실하면 복구하거나 변경할 수 없습니다. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. + **권장**: 디바이스 토큰과 종단 간 암호화 알림이 SimpleX 채팅 푸시 서버로 전송되지만 메세지 내용, 크기 또는 발신자가 표시되지 않습니다. No comment provided by engineer. **Scan QR code**: to connect to your contact in person or via video call. No comment provided by engineer. - + **Warning**: Instant push notifications require passphrase saved in Keychain. + **경고**: 즉각적인 푸시 알림은 암호문을 키체인에 저장해야 합니다. No comment provided by engineer. - + **e2e encrypted** audio call + **e2e** 오디오 통화 No comment provided by engineer. - + **e2e encrypted** video call + **e2e 암호화** 영상 통화 No comment provided by engineer. \*bold* + No comment provided by engineer. - + , + , No comment provided by engineer. - + . + . No comment provided by engineer. - + 1 day + 1일 message ttl - + 1 hour + 1시간 message ttl - + 1 month + 1개월 message ttl - + 1 week + 1주 message ttl 2 weeks message ttl - + 6 + 6 No comment provided by engineer. - + : + : No comment provided by engineer. - + A new contact + 새로운 연결 notification title @@ -246,29 +292,34 @@ A random profile will be sent to your contact No comment provided by engineer. - + A separate TCP connection will be used **for each chat profile you have in the app**. + 앱에 있는 각 채팅 프로필**마다 별도의 TCP 연결이 사용됩니다. No comment provided by engineer. - + A separate TCP connection will be used **for each contact and group member**. **Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail. + 각 연락처 및 그룹 구성원**마다 별도의 TCP 연결이 사용됩니다. +**참고**: 연결이 많으면 배터리와 트래픽 소비가 상당히 증가하고 일부 연결이 실패할 수 있습니다. No comment provided by engineer. About SimpleX No comment provided by engineer. - + About SimpleX Chat + SimpleX Chat에 대하여 No comment provided by engineer. Accent color No comment provided by engineer. - + Accept + 승인 accept contact request via notification accept incoming call via notification @@ -276,12 +327,14 @@ Accept contact No comment provided by engineer. - + Accept contact request from %@? + %@의 연락 요청을 수락하시겠습니까? notification body - + Accept incognito + 인정하지 않음 No comment provided by engineer. @@ -292,337 +345,411 @@ Add preset servers No comment provided by engineer. - + Add profile + 프로필 추가하기 No comment provided by engineer. - + Add servers by scanning QR codes. + QR 코드를 스캔하여 서버를 추가합니다. No comment provided by engineer. - - Add server… + + Add server + 서버 추가하기 No comment provided by engineer. - + Add to another device + 다른 장치에 추가하기 No comment provided by engineer. - + Add welcome message + 환영 메세지 추가하기 No comment provided by engineer. - + Admins can create the links to join groups. + 관리자는 그룹에 가입할 수 있는 링크를 만들 수 있습니다. No comment provided by engineer. - + Advanced network settings + 고급 네트워크 설정 No comment provided by engineer. - + All chats and messages will be deleted - this cannot be undone! + 모든 채팅과 메세지가 삭제됩니다. - 수정 불가능! No comment provided by engineer. - + All group members will remain connected. + 모든 그룹 구성원은 연결 상태를 유지합니다. No comment provided by engineer. - + All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. + 모든 메세지가 삭제됩니다 - 수정할 수 없습니다! 메세지는 오직 당신만을 위해 삭제될 것입니다. No comment provided by engineer. All your contacts will remain connected No comment provided by engineer. - + Allow + 승인 No comment provided by engineer. - + Allow disappearing messages only if your contact allows it to you. + 연락처가 메세지를 허용하는 경우에만 메세지 삭제를 허용합니다. No comment provided by engineer. Allow irreversible message deletion only if your contact allows it to you. No comment provided by engineer. - + Allow sending direct messages to members. + 회원에게 직접 메시지를 보낼 수 있습니다. No comment provided by engineer. - + Allow sending disappearing messages. + 사라지는 메시지를 보내는 것을 허용합니다. No comment provided by engineer. Allow to irreversibly delete sent messages. No comment provided by engineer. - + Allow to send voice messages. + 음성 메세지를 보낼 수 있습니다. No comment provided by engineer. - + Allow voice messages only if your contact allows them. + 연락처가 음성 메세지를 허용하는 경우에만 음성 메세지를 허용합니다. No comment provided by engineer. - + Allow voice messages? + 음성 메세지를 허용 하겠습니까? No comment provided by engineer. Allow your contacts to irreversibly delete sent messages. No comment provided by engineer. - + Allow your contacts to send disappearing messages. + 연락처가 사라지는 메시지를 보낼 수 있도록 허용합니다. No comment provided by engineer. - + Allow your contacts to send voice messages. + 연락처가 음성 메시지를 보낼 수 있도록 허용합니다. No comment provided by engineer. - + Already connected? + 이미 연결되었나요? No comment provided by engineer. - + Always use relay + 항상 릴레이 사용 No comment provided by engineer. - + Answer call + 응답 전화 No comment provided by engineer. - + App build: %@ + 앱 빌드: %@ No comment provided by engineer. - + App icon + 앱 아이콘 No comment provided by engineer. - + App version + 앱 버전 No comment provided by engineer. - + App version: v%@ + 앱 버전: v%@ No comment provided by engineer. - + Appearance + 출석 No comment provided by engineer. - + Attach + 첨부 No comment provided by engineer. - + Audio & video calls + 음성 & 영상 통화 No comment provided by engineer. - + Audio and video calls + 음성 및 영상 통화 No comment provided by engineer. - + Authentication failed + 인증 실패 No comment provided by engineer. - + Authentication is required before the call is connected, but you may miss calls. + 통화가 연결되기 전에 인증이 필요하지만, 통화를 놓칠 수 있습니다. No comment provided by engineer. - + Authentication unavailable + 인증 사용 불가 No comment provided by engineer. - + Auto-accept contact requests + 연락처 요청 자동 수락 No comment provided by engineer. - + Auto-accept images + 이미지 자동 수락 No comment provided by engineer. Automatically No comment provided by engineer. - + Back + 뒤로가기 No comment provided by engineer. Both you and your contact can irreversibly delete sent messages. No comment provided by engineer. - + Both you and your contact can send disappearing messages. + 당신과 당신의 연락처 모두 사라지는 메시지를 보낼 수 있습니다. No comment provided by engineer. - + Both you and your contact can send voice messages. + 당신과 당신의 연락처 모두 음성 메시지를 보낼 수 있습니다. No comment provided by engineer. - + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). + 채팅 프로필(기본값) 또는 [연결](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. - + Call already ended! + 통화가 이미 종료되었습니다! No comment provided by engineer. - + Calls + 통화 No comment provided by engineer. Can't delete user profile! No comment provided by engineer. - + Can't invite contact! + 주소를 초대할 수 없습니다. No comment provided by engineer. - + Can't invite contacts! + 연락처를 초대할 수 없습니다! No comment provided by engineer. - + Cancel + 취소 No comment provided by engineer. - + Cannot access keychain to save database password + 데이터베이스 암호를 저장하는 키체인에 접근 할 수 없습니다 No comment provided by engineer. - + Cannot receive file + 파일을 받을 수 없습니다 No comment provided by engineer. - + Change + 변경 No comment provided by engineer. - + Change database passphrase? + 데이터베이스 암호 변경? No comment provided by engineer. - + Change member role? + 멤버 역할을 변경하시겠습니까? No comment provided by engineer. - + Change receiving address + 수신 주소 변경 No comment provided by engineer. Change receiving address? - 修改接收地址? + 수신 주소를 변경하시겠습니까? No comment provided by engineer. - + Change role + 역할 변경 No comment provided by engineer. - + Chat archive + 채팅 기록 보관함 No comment provided by engineer. - + Chat console + 채팅 콘솔 No comment provided by engineer. - + Chat database + 채팅 데이터베이스 No comment provided by engineer. - + Chat database deleted + 채팅 데이터베이스 삭제 No comment provided by engineer. - + Chat database imported + 채팅 데이터베이스를 가져옴 No comment provided by engineer. - + Chat is running + 채팅이 실행 중입니다 No comment provided by engineer. - + Chat is stopped + 채팅이 중단되었습니다 No comment provided by engineer. - + Chat preferences + 채팅 환경설정 No comment provided by engineer. - + Chats + 채팅 No comment provided by engineer. - + Check server address and try again. + 서버 주소를 확인한 후 다시 시도합니다. No comment provided by engineer. - + Chinese and Spanish interface + 중국어 및 스페인어 환경 No comment provided by engineer. - + Choose file + 파일 선택 No comment provided by engineer. - + Choose from library + 라이브러리에서 선택 No comment provided by engineer. - + Clear + 정리 No comment provided by engineer. - + Clear conversation + 대화 삭제 No comment provided by engineer. - + Clear conversation? + 대화 삭제? No comment provided by engineer. - + Clear verification + 인증 삭제 No comment provided by engineer. Colors No comment provided by engineer. - + Compare security codes with your contacts. + 보안 코드를 연락처와 비교합니다. No comment provided by engineer. - + Configure ICE servers + ICE 서버 구성 No comment provided by engineer. - + Confirm + 확인 No comment provided by engineer. - + Confirm new passphrase… + 새 암호 확인… No comment provided by engineer. - + Confirm password + 비밀번호 확인 No comment provided by engineer. - + Connect + 연결 server test step @@ -633,8 +760,9 @@ Connect via group link? No comment provided by engineer. - + Connect via link + 링크를 통해 연결 No comment provided by engineer. @@ -645,174 +773,210 @@ Connect via one-time link? No comment provided by engineer. - + Connecting to server… + 서버에 연결중… No comment provided by engineer. - + Connecting to server… (error: %@) + 서버에 연결중...(오류: %@) No comment provided by engineer. - + Connection + 연결 No comment provided by engineer. - + Connection error + 연결 오류 No comment provided by engineer. - + Connection error (AUTH) + 연결 에러 (인증) No comment provided by engineer. Connection request No comment provided by engineer. - + Connection request sent! + 연결 요청이 전송되었습니다! No comment provided by engineer. - + Connection timeout + 연결 시간초과 No comment provided by engineer. - + Contact allows + 연락 가능 No comment provided by engineer. - + Contact already exists + 연결이 이미 존재 No comment provided by engineer. Contact and all messages will be deleted - this cannot be undone! No comment provided by engineer. - + Contact hidden: + 숨겨진 연락처: notification - + Contact is connected + 연락처가 연결되었습니다 notification Contact is not connected yet! No comment provided by engineer. - + Contact name + 연락처 이름 No comment provided by engineer. - + Contact preferences + 연락처 선호도 No comment provided by engineer. Contact requests No comment provided by engineer. - + Contacts can mark messages for deletion; you will be able to view them. + 연락처는 메세지를 삭제하도록 표시할 수 있으며, 이를 확인할 수 있습니다. No comment provided by engineer. - + Copy + 복사 chat item action Core built at: %@ No comment provided by engineer. - + Core version: v%@ + 코어 버전: v%@ No comment provided by engineer. - + Create + 생성 No comment provided by engineer. Create address No comment provided by engineer. - + Create group link + 그룹 링크 생성 No comment provided by engineer. - + Create link + 링크 생성 No comment provided by engineer. Create one-time invitation link No comment provided by engineer. - + Create queue + 큐 생성 server test step - + Create secret group + 비밀 그룹 생성 No comment provided by engineer. - + Create your profile + 프로필 생성 No comment provided by engineer. Created on %@ No comment provided by engineer. - + Current passphrase… + 현재 암호… No comment provided by engineer. - + Currently maximum supported file size is %@. + 현재 지원되는 최대 파일 크기는 %@입니다. No comment provided by engineer. - + Dark + 다크 No comment provided by engineer. - + Database ID + 데이터베이스 아이디 No comment provided by engineer. - + Database encrypted! + 데이터베이스 암호화됨! No comment provided by engineer. - + Database encryption passphrase will be updated and stored in the keychain. + 데이터베이스 암호화 키가 키체인에 저장됩니다. + No comment provided by engineer. - + Database encryption passphrase will be updated. + 데이터베이스 암호화 키가 업데이트됩니다. + No comment provided by engineer. - + Database error + 데이터베이스 오류 No comment provided by engineer. - + Database is encrypted using a random passphrase, you can change it. + 데이터베이스는 임의의 암호를 사용하여 암호화되므로 변경할 수 있습니다. No comment provided by engineer. - + Database is encrypted using a random passphrase. Please change it before exporting. + 데이터베이스는 임의의 암호를 사용하여 암호화됩니다. 내보내기 전에 변경하십시오. No comment provided by engineer. - + Database passphrase + 데이터베이스 암호화 키 No comment provided by engineer. - + Database passphrase & export + 데이터베이스 암호화 키 & 내보내기 No comment provided by engineer. @@ -1001,8 +1165,8 @@ Direct messages chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited. No comment provided by engineer. @@ -1017,8 +1181,8 @@ Disappearing messages are prohibited in this chat. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. No comment provided by engineer. @@ -1409,16 +1573,16 @@ Group members can irreversibly delete sent messages. No comment provided by engineer. - - Group members can send direct messages. + + Members can send direct messages. No comment provided by engineer. - - Group members can send disappearing messages. + + Members can send disappearing messages. No comment provided by engineer. - - Group members can send voice messages. + + Members can send voice messages. No comment provided by engineer. @@ -1525,8 +1689,8 @@ Image will be received when your contact is online, please wait or check later! No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam No comment provided by engineer. @@ -1630,8 +1794,8 @@ Irreversible message deletion is prohibited in this chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. No comment provided by engineer. @@ -1949,8 +2113,8 @@ We will be adding server redundancy to prevent lost messages. Onion hosts will not be used. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only client devices store user profiles, contacts, groups, and messages. No comment provided by engineer. @@ -2001,8 +2165,9 @@ We will be adding server redundancy to prevent lost messages. Open user profiles authentication reason - - Open-source protocol and code – anybody can run the servers. + + Anybody can host servers. + 누구나 서버를 호스팅할 수 있습니다. No comment provided by engineer. @@ -2037,8 +2202,8 @@ We will be adding server redundancy to prevent lost messages. Paste the link you received into the box below to connect with your contact. No comment provided by engineer. - - People can connect to you only via the links you share. + + You decide who can connect. No comment provided by engineer. @@ -2397,24 +2562,29 @@ We will be adding server redundancy to prevent lost messages. Send live message No comment provided by engineer. - + Send notifications + 알림 전송 No comment provided by engineer. - + Send notifications: + 알림 전송: No comment provided by engineer. - + Send questions and ideas + 질문이나 아이디어 보내기 No comment provided by engineer. - + Send them from gallery or custom keyboards. + 갤러리 또는 사용자 정의 키보드에서 그들을 보내십시오. No comment provided by engineer. - + Sender cancelled file transfer. + 상대방이 파일 전송을 취소했습니다. No comment provided by engineer. @@ -2653,8 +2823,8 @@ We will be adding server redundancy to prevent lost messages. Thanks to the users – contribute via Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. + + No user identifiers. No comment provided by engineer. @@ -2689,16 +2859,16 @@ We will be adding server redundancy to prevent lost messages. The message will be marked as moderated for all members. No comment provided by engineer. - - The next generation of private messaging + + The future of messaging No comment provided by engineer. The old database was not removed during the migration, it can be deleted. No comment provided by engineer. - - The profile is only shared with your contacts. + + Your profile is stored on your device and only shared with your contacts. No comment provided by engineer. @@ -2757,8 +2927,8 @@ We will be adding server redundancy to prevent lost messages. To make a new connection No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. No comment provided by engineer. @@ -2951,8 +3121,8 @@ To connect, please ask your contact to create another connection link and check Voice messages are prohibited in this chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. No comment provided by engineer. @@ -3076,10 +3246,6 @@ SimpleX Lock must be enabled. You can't send messages! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - You could not be verified; please try again. No comment provided by engineer. @@ -3755,6 +3921,1206 @@ SimpleX servers cannot see your profile. \~strike~ No comment provided by engineer. + + Change passcode + 패스코드 변경 + authentication reason + + + Cellular + 셀룰러 + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + 이 서버 또는 도착 서버가 비밀 라우팅을 지원하지 않을 때 직통 메시지 보내기. + No comment provided by engineer. + + + Send up to 100 last messages to new members. + 새로운 멤버에게 최대 100개의 마지막 메시지 보내기. + No comment provided by engineer. + + + ## History + ## 기록 + + + ## In reply to + ## 에 대한 답변 + + + %@ downloaded + %@ 다운로드됨 + + + # %@ + # %@ + + + %@ and %@ + %@ 그리고 %@ + + + %1$@ at %2$@: + %2$@의 %1$@: + + + %@ connected + %@ 연결됨 + + + %@ (current): + %@ (현재): + + + %@ (current) + %@ (현재) + + + %@ and %@ connected + %@ 및 %@이(가) 연결되었습니다 + + + %@ server + %@서버 + + + %@ servers + %@서버들 + + + %@, %@ and %lld members + %@, %@ 과 %lld 멤버들 + + + %d file(s) are still being downloaded. + %d 개의 파일 다운로드중. + + + %d file(s) were deleted. + %d개의 파일이 삭제됨. + + + %d file(s) were not downloaded. + %d개의 파일이 다운로드 되지 않음. + + + %d weeks + %d 주 + + + %lld seconds + %lld 초 + + + **Create 1-time link**: to create and share a new invitation link. + **1회 링크 생성** : 새 초대 링크를 생성하고 공유합니다. + + + 1-time link + 일회성 링크 + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + 일회용 링크는 *한 번의 연락처로만* 사용할 수 있으며, 대면 또는 메신저를 통해 공유할 수 있습니다. + + + A few more things + 몇 가지 더 + + + Accept conditions + 조건 수락 + + + Accepted conditions + 수락된 조건 + + + Active connections + 연결 활성화 + + + %@ uploaded + %@업로드됨 + + + Accept connection request? + 연결 요청을 수락하시겠습니까? + + + %lld minutes + %lld 분 + + + **Warning**: the archive will be removed. + **경고**: 보관물이 제거됩니다. + + + 5 minutes + 5 분 + + + Abort changing address + 주소 변경 중단 + + + Acknowledgement errors + 확인 오류 + + + Abort + 중단 + + + %u messages failed to decrypt. + %u개의 메세지를 번역하는데 실패함. + + + Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. + 연락처가 다른 사람과 공유할 수 있도록 프로필에 주소를 추가합니다. 프로필 업데이트가 연락처로 전송됩니다. + + + %lld messages blocked by admin + 관리자에 의해 차단된 %lld개의 메세지 + + + **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **참고**: 두 장치에서 동일한 데이터베이스를 사용하면 보안 보호를 위해 연결에서 메세지를 해독할 수 있습니다. + + + **Create group**: to create a new group. + **그룹 생성** : 새로운 그룹을 생성합니다. + + + %d file(s) failed to download. + %d개의 파일을 다운로드하는데 실패함. + + + %d messages not forwarded + %d개의 메세지가 전달되지 않음 + + + **Scan / Paste link**: to connect via a link you received. + **스캔/붙여넣기 링크**: 받은 링크를 통해 연결합니다. + + + About operators + 연산자 정보 + + + Address change will be aborted. Old receiving address will be used. + 주소 변경이 중단됩니다. 이전 수신 주소가 사용됩니다. + + + %@: + %@: + + + %lld messages blocked + %lld개의 메세지가 차단됨 + + + %lld messages marked deleted + 삭제된 메세지 %lld 개 + + + - more stable message delivery. +- a bit better groups. +- and more! + - 보다 안정적인 메세지 전달. +- 조금 더 나은 그룹. +- 그리고 더! + + + 0s + 0초 + + + 1 minute + 1분 + + + Abort changing address? + 주소 변경을 중단하시겠습니까? + + + 30 seconds + 30초 + + + - voice messages up to 5 minutes. +- custom time to disappear. +- editing history. + - 음성 메세지 최대 5분. +- 사라지는 맞춤형 시간. +- 편집 기록. + + + Add friends + 친구 추가 + + + Add team members + 팀원 추가하기 + + + Add your team members to the conversations. + 대화에 팀원을 추가하세요. + + + %u messages skipped. + 메세지 %u개를 건너뜀. + + + %@, %@ and %lld other members connected + %@, %@ 그리고 %lld 다른 멤버들이 연결됨 + + + %lld messages moderated by %@ + %@ 에 의해 중재된 %lld 개의 메세지 + + + %lld new interface languages + %lld개의 새로운 인터페이스 언어 + + + %1$@, %2$@ + %1$@, %2$@ + + + - optionally notify deleted contacts. +- profile names with spaces. +- and more! + - 선택적으로 삭제된 연락처를 통지합니다. +- 공백이 있는 프로필 이름. +- 그리고 더! + + + <p>Hi!</p> +<p><a href="%@">Connect to me via SimpleX Chat</a></p> + <p>안녕하세요!/p> +<p><a href="%@">SimpleX 채팅을 통해 저에게 연결하세요 </a></p> + + + A new random profile will be shared. + 새로운 랜덤 프로필이 공유될 것입니다. + + + Acknowledged + 인정된 + + + Additional accent 2 + 추가 악센트2 + + + Added media & file servers + 미디어 및 파일 서버 추가 + + + Added message servers + 추가된 메세지 서버 + + + Additional accent + 추가 악센트 + + + Additional secondary + 추가적 보조 + + + Address + 주소 + + + Address or 1-time link? + 주소 또는 일회성 링크? + + + Address settings + 주소 세팅 + + + Admins can block a member for all. + 관리자는 모두를 위해 회원을 차단할 수 있습니다. + + + %lld group events + %lld개의 그룹 이벤트 + + + All app data is deleted. + 모든 앱 데이터가 삭제됩니다. + + + All data is erased when it is entered. + 입력하면 모든 데이터가 삭제됩니다. + + + 0 sec + 0 초 + + + (this device v%@) + (이 장치 v%@) + + + (new) + (새로운) + + + Advanced settings + 고급 설정 + + + All data is kept private on your device. + 모든 데이터는 기기에서 비공개로 유지됩니다. + + + All profiles + 전체 프로필 + + + All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + 모든 연락처, 대화 및 파일은 안전하게 암호화되어 구성된 XFTP 릴레이에 청크로 업로드됩니다. + + + Allow calls only if your contact allows them. + 허용된 연락처만 통화가 가능합니다. + + + Allow calls? + 통화 허용? + + + Allow downgrade + 강등 허용 + + + Allow irreversible message deletion only if your contact allows it to you. (24 hours) + 연락처가 허용하는 경우에만 수정 불가능한 메세지 삭제를 허용합니다. (24시간) + + + Allow to send files and media. + 파일과 미디어를 전송할 수 있습니다. + + + Archiving database + 보관된 데이터베이스 + + + Better calls + 더 나은 통화 + + + Block + 차단 + + + Conditions will be accepted for enabled operators after 30 days. + 30일 후에 활성화된 운영자에 대한 조건이 수락될 것입니다. + + + Conditions will be accepted on: %@. + 조건은 다음과 같습니다: %@. + + + Connect via one-time link + 일회성 링크를 통해 연결 + + + Connected desktop + 데스크톱과 연결됨 + + + Connected servers + 연결된 서버 + + + Connection security + 연결 보안 + + + Connection terminated + 종료된 연결 + + + Connection with desktop stopped + 데스크톱과의 연결이 중지됨 + + + Current conditions text couldn't be loaded, you can review conditions via this link: + 현재 조건 텍스트를 로드할 수 없습니다. 이 링크를 통해 조건을 검토할 수 있습니다: + + + Bad desktop address + 잘못된 데스크톱 주소 + + + Camera not available + 카메라가 사용 불가능합니다 + + + Custom time + 사용자 지정 시간 + + + Allow to irreversibly delete sent messages. (24 hours) + 보낸 메시지를 되돌릴 수 없도록 삭제합니다. (24시간) + + + Allow message reactions. + 메세지 응답 허용. + + + Allow your contacts adding message reactions. + 연락처가 메세지 응답을 추가하도록 허용합니다. + + + Already connecting! + 이미 연결 중입니다! + + + Already joining the group! + 그룹에 참가하는 중입니다! + + + Archive and upload + 기록 및 업로드 + + + Chat colors + 채팅 색깔 + + + Chat list + 채팅 목록 + + + Completed + 완료됨 + + + Copy error + 복사 오류 + + + Create SimpleX address + SimpleX 주소 생성 + + + Creating link… + 생성 링크… + + + Blocked by admin + 관리자에 의해 차단됨 + + + Connect to desktop + 데스크톱에 연결 + + + Created at + 에 생성됨 + + + Created at: %@ + 생성 위치: %@ + + + Change self-destruct passcode + 자기-파괴 비밀번호 변경 + + + Create file + 파일 생성 + + + Allow your contacts to irreversibly delete sent messages. (24 hours) + 연락처가 보낸 메세지를 되돌릴 수 없도록 삭제할 수 있도록 허용합니다. (24시간) + + + App data migration + 앱 데이터 이동 + + + Apply to + 적용 대상 + + + Block for all + 모두를 위한 차단 + + + Both you and your contact can add message reactions. + 당신과 당신의 연락처 모두 메세지 반응을 추가할 수 있습니다. + + + Calls prohibited! + 통화 금지! + + + Change self-destruct mode + 자기-파괴 모드 변경 + + + Contacts + 연락처 + + + Create group + 그룹 생성 + + + Both you and your contact can make calls. + 당신과 당신의 연락처 모두 전화를 걸 수 있습니다. + + + App passcode + 앱 비밀번호 + + + All your contacts will remain connected. Profile update will be sent to your contacts. + 당신의 모든 연락은 연결되어 있습니다. 프로필 업데이트가 모든 연락으로 전송됩니다. + + + App encrypts new local files (except videos). + 앱은 새로운 로컬 파일을 암호화합니다 (동영상 제외). + + + Chat preferences were changed. + 채팅 환경설정이 변경되었습니다. + + + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 + [데스크톱 앱]에 새로운 프로필 생성(https://simplex.chat/downloads/).💻 + + + Contact is deleted. + 연락처가 삭제되었습니다. + + + Continue + 계속 + + + Current Passcode + 현재 비밀번호 + + + An empty chat profile with the provided name is created, and the app opens as usual. + 제공된 이름으로 빈 채팅 프로필이 생성되고 앱이 정상적으로 열립니다. + + + Allow your contacts to call you. + 연락처가 전화할 수 있도록 허용합니다. + + + Allow sharing + 공유 허용 + + + Always use private routing. + 항상 개인 경로를 사용합니다. + + + Better user experience + 더 나은 사용자 경험 + + + Change lock mode + 잠금 모드 변경 + + + Allow message reactions only if your contact allows them. + 연락처가 메세지 응답을 허용하는 경우에만 메세지 응답을 허용합니다. + + + Better security ✅ + 더 나은 안전✅ + + + Both you and your contact can irreversibly delete sent messages. (24 hours) + 당신과 당신의 연락처 모두 보낸 메세지를 되돌릴 수 없습니다. (24시간) + + + Confirm contact deletion? + 연락처 삭제를 확인하시겠습니까? + + + Can't call contact + 연락처에 전화할 수 없습니다 + + + Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + 불가리아어, 핀란드어, 태국어, 우크라이나어 - 사용자 여러분과 [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)에 감사드립니다! + + + Capacity exceeded - recipient did not receive previously sent messages. + 용량 초과 - 수신자가 이전에 보낸 메세지를 받지 못했습니다. + + + Chat + 채팅 + + + Connect to yourself? +This is your own one-time link! + 자신에게 연결할까요? +이것은 당신만의 일회성 링크입니다! + + + Choose _Migrate from another device_ on the new device and scan QR code. + 새 기기에서 _다른 기기에서 이동_을 선택하고 QR 코드를 스캔합니다. + + + Connecting to desktop + 데스크톱에 연결중 + + + Connect with %@ + %@와 연결 + + + Archived contacts + 보관된 연락처 + + + Better message dates. + 더 나은 메세지 날짜. + + + Better networking + 더 나은 네트워킹 + + + Check messages when allowed. + 허용될 때 메시지를 확인합니다. + + + Compare file + 파일 비교 + + + Conditions will be automatically accepted for enabled operators on: %@. + 다음 조건은 활성화된 운영자에 대해 자동으로 수락됩니다: %@. + + + Confirm upload + 업로드 확인 + + + Connect incognito + 비밀 연결 + + + Connect to your friends faster. + 친구들과 더 빨리 연결하세요. + + + Connect to yourself? + 자신과 연결할까요? + + + Created + 생성됨 + + + Creating archive link + 기록 링크 생성하기 + + + Auto-accept + 자동 수락 + + + All new messages from %@ will be hidden! + %@로부터의 모든 새 메세지가 숨겨집니다! + + + Auto-accept settings + 자동-수락 설정 + + + Archive contacts to chat later. + 나중에 채팅할 연락처를 보관합니다. + + + Background + 배경 + + + Bad message hash + 잘못된 메세지 hash + + + Better groups + 더 나은 그룹 + + + Better messages + 더 나은 메세지 + + + Chunks downloaded + 다운로드된 청크 + + + Chunks deleted + 삭제된 청크 + + + Chunks uploaded + 업로드 된 청크 + + + Corner + 코너 + + + Correct name to %@? + %@의 정확한 이름은? + + + Create a group using a random profile. + 랜덤 프로필을 사용하여 그룹을 만듭니다. + + + Authentication cancelled + 인증 취소 + + + Confirm Passcode + 비밀번호 확인 + + + Confirm database upgrades + 데이터베이스 업그레이드 확인 + + + Blur media + 가려진 미디어 + + + Block group members + 그룹 구성원 차단 + + + Connected + 연결됨 + + + All messages will be deleted - this cannot be undone! + 모든 메세지가 삭제됩니다 - 수정할 수 없습니다! + + + All your contacts will remain connected. + 당신의 모든 연락은 계속 연결되어 있습니다. + + + Allow to send SimpleX links. + SinpleX 링크 전송 허용. + + + Bad message ID + 잘못된 메세지 ID + + + Black + 블랙 + + + Block member + 차단 구성원 + + + Connected to desktop + 데스크톱과 연결됨 + + + App passcode is replaced with self-destruct passcode. + 앱 비밀번호는 자체-파괴 비밀번호로 대체됩니다. + + + Apply + 적용 + + + Better notifications + 더 나은 공지 + + + Block member? + 차단 멤버? + + + Blur for better privacy. + 더 나은 개인정보를 위해 흐림. + + + Business address + 사업체 주소 + + + Business chats + 비즈니스 채팅 + + + Can't call member + 회원에게 전화할 수 없습니다 + + + Can't message member + 멤버에게 메세지를 보낼 수 없습니다 + + + Cancel migration + 이동 취소 + + + Change chat profiles + 채팅 프로필 변경 + + + Chat already exists + 채팅이 이미 존재합니다 + + + Chat already exists! + 채팅이 이미 존재합니다! + + + Chat database exported + 채팅 데이터베이스 내보내기 + + + Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat. + 채팅이 중지되었습니다. 이미 다른 장치에서 이 데이터베이스를 사용하고 있다면 채팅을 시작하기 전에 다시 전송해야 합니다. + + + Chat migrated! + 채팅 이동! + + + Chat theme + 채팅 테마 + + + Chat will be deleted for all members - this cannot be undone! + 채팅은 모든 회원에게 삭제됩니다 - 이는 되돌릴 수 없습니다! + + + Chat profile + 채팅 프로필 + + + Chat will be deleted for you - this cannot be undone! + 채팅은 삭제됩니다 - 되돌릴 수 없습니다! + + + Check messages every 20 min. + 20분마다 메시지를 확인합니다. + + + Color chats with the new themes. + 새로운 테마로 채팅을 색칠하세요. + + + Color mode + 색깔 모드 + + + Clear private notes? + 개인 메모를 지우시겠습니까? + + + Conditions accepted on: %@. + 조건이 수락됨: %@. + + + Conditions are accepted for the operator(s): **%@**. + 운영자의 조건이 허용됩니다: **%@**. + + + Conditions of use + 이용 조건 + + + Conditions will be accepted for operator(s): **%@**. + 운영자 조건이 수락됩니다: **%@**. + + + Conditions will be accepted for the operator(s): **%@**. + 운영자 조건이 수락됩니다.: **%@**. + + + Confirm that you remember database passphrase to migrate it. + 이동하는데에 필요한 데이터베이스 비밀번호를 기억하는지 확인합니다. + + + Connect to yourself? +This is your own SimpleX address! + 자신과 연결할까요? +이것은 당신의 SimpleX 주소입니다! + + + Connect via contact address + 연락처 주소로 연결 + + + Connecting + 연결중 + + + Connecting to contact, please wait or check later! + 연락처에 연결 중이니 기다려 주시거나 나중에 확인해 주세요! + + + Connection and servers status. + 연결 및 서버 상태. + + + Connection notifications + 연결 공지 + + + Connections + 연결 + + + Contact will be deleted - this cannot be undone! + 연락처가 삭제됩니다 - 취소할 수 없습니다! + + + Conversation deleted! + 대화가 삭제되었습니다! + + + Create 1-time link + 일회성 링크 생성 + + + Create profile + 프로필 생성 + + + Audio/video calls + 음성/영상 통화 + + + Audio/video calls are prohibited. + 음성/영상 통화는 금지되어 있습니다. + + + App session + 앱 세션 + + + Block member for all? + 모두를 위한 차단 멤버? + + + Cannot forward message + 메세지를 전달할 수 없습니다 + + + Confirm network settings + 네트워크 설정 확인 + + + Connect automatically + 자동으로 연결 + + + Confirm files from unknown servers. + 알 수 없는 서버에서 파일을 확인합니다. + + + Conditions are already accepted for these operator(s): **%@**. + 이 운영자들에 대한 조건은 이미 받아들여지고 있습니다: **%@**. + + + Contact deleted! + 연락처 삭제! + + + Current profile + 현재 프로필 + + + Customizable message shape. + 사용자 지정 가능한 메세지 형태. + + + %d seconds(s) + %d 초 + + + 1 year + 1년 + + + Add list + 리스트 추가 + + + Add to list + 리스트에 추가 + + + All + 모두 + + + Allow to report messsages to moderators. + 메시지를 신고하는것을 허용합니다. + + + Another reason + 다른 이유 + + + App group: + 앱 그룹: + + + Archive + 아카이브 + + + Archive report + 신고 아카이브 + + + Archive report? + 신고를 아카이브할까요? + + + Archive reports + 신고 아카이브 + + + Ask + 묻기 + + + Clear group? + 그룹을 비울까요? + + + Clear or delete group? + 그룹을 비우거나 삭제할까요? + + + Community guidelines violation + 커뮤니티 지침 위반 + + + Connection blocked + 연결 차단됨 + + + Connection is blocked by server operator: +%@ + 서버 관리자에 의해 연결이 차단되었습니다: +%@ + + + Connection not ready. + 연결 준비되지 않음. + + + Connection requires encryption renegotiation. + 연결에는 암호화 재협상이 필요합니다. + + + Content violates conditions of use + 내용은 사용 규정을 위반합니다 + + + Create list + 리스트 추가 + + + Database ID: %d + 데이터베이스 아이디: %d + + + Database IDs and Transport isolation option. + 데이터베이스 ID 및 전송 격리 옵션. + + + Database downgrade + 데이터베이스 다운그레이드 + + + Better groups performance + 더 나은 그룹 성능 + + + Confirmed + 확인함 + + + Active + 활성화됨 + + + Archive all reports? + 모든 신고를 아카이브할까요? + + + Businesses + 비즈니스 + + + Better privacy and security + 더 나은 프라이버시 및 보안 + + + Change automatic message deletion? + 자동 메시지 삭제를 변경할까요? + + + All chats will be removed from the list %@, and the list deleted. + 모든 채팅은 %@ 리스트에서 제거되고 리스트는 삭제됩니다. + + + All reports will be archived for you. + 모든 보고서는 사용자를 위해 보관됩니다. + + + Accent + 강조 + + + Archive %lld reports? + %lld 신고를 아카이브할까요? + + + - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! +- delivery receipts (up to 20 members). +- faster and more stable. + - [경로 서비스](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA) 에 연결중! +- 전달 확인 (최대 20 명의 멤버). +- 더 빠르고 안정적입니다. + + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + 모든 메시지와 파일은 **종간단 암호화 (E2EE)**되며, 개인 메시지는 양자 보안이 적용됩니다. + + + Customize theme + 테마 사용자 지정 + + + Dark mode colors + 다크 모드 색상들 +
@@ -3778,8 +5144,9 @@ SimpleX servers cannot see your profile. SimpleX needs microphone access for audio and video calls, and to record voice messages. Privacy - Microphone Usage Description
- + SimpleX needs access to Photo Library for saving captured and received media + SimpleX는 캡처 및 수신 된 미디어를 저장하기 위해 사진 라이브러리에 접근이 필요합니다 Privacy - Photo Library Additions Usage Description @@ -3793,8 +5160,9 @@ SimpleX servers cannot see your profile. SimpleX NSE Bundle display name - + SimpleX NSE + SimpleX NSE Bundle name diff --git a/apps/ios/SimpleX Localizations/lt.xcloc/Localized Contents/lt.xliff b/apps/ios/SimpleX Localizations/lt.xcloc/Localized Contents/lt.xliff index feb1e177f1..0f795170c6 100644 --- a/apps/ios/SimpleX Localizations/lt.xcloc/Localized Contents/lt.xliff +++ b/apps/ios/SimpleX Localizations/lt.xcloc/Localized Contents/lt.xliff @@ -162,20 +162,16 @@ ) No comment provided by engineer. - - **Add new contact**: to create your one-time QR Code or link for your contact. - No comment provided by engineer. - **Create link / QR code** for your contact to use. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. No comment provided by engineer. @@ -187,8 +183,8 @@ **Turėkite omenyje**: jeigu prarasite slaptafrazę, NEBEGALĖSITE jos atkurti ar pakeisti. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. No comment provided by engineer. @@ -329,9 +325,9 @@ Pridėti serverius skenuojant QR kodus. No comment provided by engineer. - - Add server… - Pridėti serverį… + + Add server + Pridėti serverį No comment provided by engineer. @@ -1033,8 +1029,8 @@ Direct messages chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited. No comment provided by engineer. @@ -1049,8 +1045,8 @@ Disappearing messages are prohibited in this chat. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. No comment provided by engineer. @@ -1417,16 +1413,16 @@ Group members can irreversibly delete sent messages. No comment provided by engineer. - - Group members can send direct messages. + + Members can send direct messages. No comment provided by engineer. - - Group members can send disappearing messages. + + Members can send disappearing messages. No comment provided by engineer. - - Group members can send voice messages. + + Members can send voice messages. No comment provided by engineer. @@ -1513,8 +1509,8 @@ Image will be received when your contact is online, please wait or check later! No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam No comment provided by engineer. @@ -1614,8 +1610,8 @@ Irreversible message deletion is prohibited in this chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. No comment provided by engineer. @@ -1919,8 +1915,8 @@ We will be adding server redundancy to prevent lost messages. Onion hosts will not be used. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only client devices store user profiles, contacts, groups, and messages. No comment provided by engineer. @@ -1971,8 +1967,8 @@ We will be adding server redundancy to prevent lost messages. Open user profiles authentication reason - - Open-source protocol and code – anybody can run the servers. + + Anybody can host servers. No comment provided by engineer. @@ -2003,8 +1999,8 @@ We will be adding server redundancy to prevent lost messages. Paste the link you received into the box below to connect with your contact. No comment provided by engineer. - - People can connect to you only via the links you share. + + You decide who can connect. No comment provided by engineer. @@ -2591,8 +2587,8 @@ We will be adding server redundancy to prevent lost messages. Thanks to the users – contribute via Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. + + No user identifiers. No comment provided by engineer. @@ -2627,16 +2623,16 @@ We will be adding server redundancy to prevent lost messages. The message will be marked as moderated for all members. No comment provided by engineer. - - The next generation of private messaging + + The future of messaging No comment provided by engineer. The old database was not removed during the migration, it can be deleted. No comment provided by engineer. - - The profile is only shared with your contacts. + + Your profile is stored on your device and only shared with your contacts. No comment provided by engineer. @@ -2687,8 +2683,8 @@ We will be adding server redundancy to prevent lost messages. To make a new connection No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. No comment provided by engineer. @@ -2873,8 +2869,8 @@ To connect, please ask your contact to create another connection link and check Voice messages are prohibited in this chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. No comment provided by engineer. @@ -2993,10 +2989,6 @@ To connect, please ask your contact to create another connection link and check You can't send messages! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - You could not be verified; please try again. No comment provided by engineer. diff --git a/apps/ios/SimpleX Localizations/ml.xcloc/Localized Contents/ml.xliff b/apps/ios/SimpleX Localizations/ml.xcloc/Localized Contents/ml.xliff deleted file mode 100644 index f4a1a815ea..0000000000 --- a/apps/ios/SimpleX Localizations/ml.xcloc/Localized Contents/ml.xliff +++ /dev/null @@ -1,4624 +0,0 @@ - - - -
- -
- - - - - No comment provided by engineer. - - - - No comment provided by engineer. - - - - No comment provided by engineer. - - - - No comment provided by engineer. - - - ( - No comment provided by engineer. - - - (can be copied) - No comment provided by engineer. - - - !1 colored! - No comment provided by engineer. - - - #secret# - No comment provided by engineer. - - - %@ - No comment provided by engineer. - - - %@ %@ - No comment provided by engineer. - - - %@ (current) - No comment provided by engineer. - - - %@ (current): - copied message info - - - %@ / %@ - No comment provided by engineer. - - - %@ is connected! - notification title - - - %@ is not verified - No comment provided by engineer. - - - %@ is verified - No comment provided by engineer. - - - %@ servers - No comment provided by engineer. - - - %@ wants to connect! - notification title - - - %@: - copied message info - - - %d days - time interval - - - %d hours - time interval - - - %d min - time interval - - - %d months - time interval - - - %d sec - time interval - - - %d skipped message(s) - integrity error chat item - - - %d weeks - time interval - - - %lld - No comment provided by engineer. - - - %lld %@ - No comment provided by engineer. - - - %lld contact(s) selected - No comment provided by engineer. - - - %lld file(s) with total size of %@ - No comment provided by engineer. - - - %lld members - No comment provided by engineer. - - - %lld minutes - No comment provided by engineer. - - - %lld second(s) - No comment provided by engineer. - - - %lld seconds - No comment provided by engineer. - - - %lldd - No comment provided by engineer. - - - %lldh - No comment provided by engineer. - - - %lldk - No comment provided by engineer. - - - %lldm - No comment provided by engineer. - - - %lldmth - No comment provided by engineer. - - - %llds - No comment provided by engineer. - - - %lldw - No comment provided by engineer. - - - %u messages failed to decrypt. - No comment provided by engineer. - - - %u messages skipped. - No comment provided by engineer. - - - ( - No comment provided by engineer. - - - ) - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - No comment provided by engineer. - - - **Create link / QR code** for your contact to use. - No comment provided by engineer. - - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. - No comment provided by engineer. - - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). - No comment provided by engineer. - - - **Paste received link** or open it in the browser and tap **Open in mobile app**. - No comment provided by engineer. - - - **Please note**: you will NOT be able to recover or change passphrase if you lose it. - No comment provided by engineer. - - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. - No comment provided by engineer. - - - **Scan QR code**: to connect to your contact in person or via video call. - No comment provided by engineer. - - - **Warning**: Instant push notifications require passphrase saved in Keychain. - No comment provided by engineer. - - - **e2e encrypted** audio call - No comment provided by engineer. - - - **e2e encrypted** video call - No comment provided by engineer. - - - \*bold* - No comment provided by engineer. - - - , - No comment provided by engineer. - - - - voice messages up to 5 minutes. -- custom time to disappear. -- editing history. - No comment provided by engineer. - - - . - No comment provided by engineer. - - - 0s - No comment provided by engineer. - - - 1 day - time interval - - - 1 hour - time interval - - - 1 minute - No comment provided by engineer. - - - 1 month - time interval - - - 1 week - time interval - - - 1-time link - No comment provided by engineer. - - - 5 minutes - No comment provided by engineer. - - - 6 - No comment provided by engineer. - - - 30 seconds - No comment provided by engineer. - - - : - No comment provided by engineer. - - - <p>Hi!</p> -<p><a href="%@">Connect to me via SimpleX Chat</a></p> - email text - - - A new contact - notification title - - - A random profile will be sent to the contact that you received this link from - No comment provided by engineer. - - - A random profile will be sent to your contact - No comment provided by engineer. - - - A separate TCP connection will be used **for each chat profile you have in the app**. - No comment provided by engineer. - - - A separate TCP connection will be used **for each contact and group member**. -**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail. - No comment provided by engineer. - - - About SimpleX - No comment provided by engineer. - - - About SimpleX Chat - No comment provided by engineer. - - - About SimpleX address - No comment provided by engineer. - - - Accent color - No comment provided by engineer. - - - Accept - accept contact request via notification - accept incoming call via notification - - - Accept contact - No comment provided by engineer. - - - Accept contact request from %@? - notification body - - - Accept incognito - No comment provided by engineer. - - - Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. - No comment provided by engineer. - - - Add preset servers - No comment provided by engineer. - - - Add profile - No comment provided by engineer. - - - Add servers by scanning QR codes. - No comment provided by engineer. - - - Add server… - No comment provided by engineer. - - - Add to another device - No comment provided by engineer. - - - Add welcome message - No comment provided by engineer. - - - Address - No comment provided by engineer. - - - Admins can create the links to join groups. - No comment provided by engineer. - - - Advanced network settings - No comment provided by engineer. - - - All app data is deleted. - No comment provided by engineer. - - - All chats and messages will be deleted - this cannot be undone! - No comment provided by engineer. - - - All data is erased when it is entered. - No comment provided by engineer. - - - All group members will remain connected. - No comment provided by engineer. - - - All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. - No comment provided by engineer. - - - All your contacts will remain connected. - No comment provided by engineer. - - - All your contacts will remain connected. Profile update will be sent to your contacts. - No comment provided by engineer. - - - Allow - No comment provided by engineer. - - - Allow calls only if your contact allows them. - No comment provided by engineer. - - - Allow disappearing messages only if your contact allows it to you. - No comment provided by engineer. - - - Allow irreversible message deletion only if your contact allows it to you. - No comment provided by engineer. - - - Allow message reactions only if your contact allows them. - No comment provided by engineer. - - - Allow message reactions. - No comment provided by engineer. - - - Allow sending direct messages to members. - No comment provided by engineer. - - - Allow sending disappearing messages. - No comment provided by engineer. - - - Allow to irreversibly delete sent messages. - No comment provided by engineer. - - - Allow to send voice messages. - No comment provided by engineer. - - - Allow voice messages only if your contact allows them. - No comment provided by engineer. - - - Allow voice messages? - No comment provided by engineer. - - - Allow your contacts adding message reactions. - No comment provided by engineer. - - - Allow your contacts to call you. - No comment provided by engineer. - - - Allow your contacts to irreversibly delete sent messages. - No comment provided by engineer. - - - Allow your contacts to send disappearing messages. - No comment provided by engineer. - - - Allow your contacts to send voice messages. - No comment provided by engineer. - - - Already connected? - No comment provided by engineer. - - - Always use relay - No comment provided by engineer. - - - An empty chat profile with the provided name is created, and the app opens as usual. - No comment provided by engineer. - - - Answer call - No comment provided by engineer. - - - App build: %@ - No comment provided by engineer. - - - App icon - No comment provided by engineer. - - - App passcode - No comment provided by engineer. - - - App passcode is replaced with self-destruct passcode. - No comment provided by engineer. - - - App version - No comment provided by engineer. - - - App version: v%@ - No comment provided by engineer. - - - Appearance - No comment provided by engineer. - - - Attach - No comment provided by engineer. - - - Audio & video calls - No comment provided by engineer. - - - Audio and video calls - No comment provided by engineer. - - - Audio/video calls - chat feature - - - Audio/video calls are prohibited. - No comment provided by engineer. - - - Authentication cancelled - PIN entry - - - Authentication failed - No comment provided by engineer. - - - Authentication is required before the call is connected, but you may miss calls. - No comment provided by engineer. - - - Authentication unavailable - No comment provided by engineer. - - - Auto-accept - No comment provided by engineer. - - - Auto-accept contact requests - No comment provided by engineer. - - - Auto-accept images - No comment provided by engineer. - - - Back - No comment provided by engineer. - - - Bad message ID - No comment provided by engineer. - - - Bad message hash - No comment provided by engineer. - - - Better messages - No comment provided by engineer. - - - Both you and your contact can add message reactions. - No comment provided by engineer. - - - Both you and your contact can irreversibly delete sent messages. - No comment provided by engineer. - - - Both you and your contact can make calls. - No comment provided by engineer. - - - Both you and your contact can send disappearing messages. - No comment provided by engineer. - - - Both you and your contact can send voice messages. - No comment provided by engineer. - - - By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). - No comment provided by engineer. - - - Call already ended! - No comment provided by engineer. - - - Calls - No comment provided by engineer. - - - Can't delete user profile! - No comment provided by engineer. - - - Can't invite contact! - No comment provided by engineer. - - - Can't invite contacts! - No comment provided by engineer. - - - Cancel - No comment provided by engineer. - - - Cannot access keychain to save database password - No comment provided by engineer. - - - Cannot receive file - No comment provided by engineer. - - - Change - No comment provided by engineer. - - - Change database passphrase? - No comment provided by engineer. - - - Change lock mode - authentication reason - - - Change member role? - No comment provided by engineer. - - - Change passcode - authentication reason - - - Change receiving address - No comment provided by engineer. - - - Change receiving address? - No comment provided by engineer. - - - Change role - No comment provided by engineer. - - - Change self-destruct mode - authentication reason - - - Change self-destruct passcode - authentication reason - set passcode view - - - Chat archive - No comment provided by engineer. - - - Chat console - No comment provided by engineer. - - - Chat database - No comment provided by engineer. - - - Chat database deleted - No comment provided by engineer. - - - Chat database imported - No comment provided by engineer. - - - Chat is running - No comment provided by engineer. - - - Chat is stopped - No comment provided by engineer. - - - Chat preferences - No comment provided by engineer. - - - Chats - No comment provided by engineer. - - - Check server address and try again. - No comment provided by engineer. - - - Chinese and Spanish interface - No comment provided by engineer. - - - Choose file - No comment provided by engineer. - - - Choose from library - No comment provided by engineer. - - - Clear - No comment provided by engineer. - - - Clear conversation - No comment provided by engineer. - - - Clear conversation? - No comment provided by engineer. - - - Clear verification - No comment provided by engineer. - - - Colors - No comment provided by engineer. - - - Compare file - server test step - - - Compare security codes with your contacts. - No comment provided by engineer. - - - Configure ICE servers - No comment provided by engineer. - - - Confirm - No comment provided by engineer. - - - Confirm Passcode - No comment provided by engineer. - - - Confirm database upgrades - No comment provided by engineer. - - - Confirm new passphrase… - No comment provided by engineer. - - - Confirm password - No comment provided by engineer. - - - Connect - server test step - - - Connect via contact link? - No comment provided by engineer. - - - Connect via group link? - No comment provided by engineer. - - - Connect via link - No comment provided by engineer. - - - Connect via link / QR code - No comment provided by engineer. - - - Connect via one-time link? - No comment provided by engineer. - - - Connecting to server… - No comment provided by engineer. - - - Connecting to server… (error: %@) - No comment provided by engineer. - - - Connection - No comment provided by engineer. - - - Connection error - No comment provided by engineer. - - - Connection error (AUTH) - No comment provided by engineer. - - - Connection request - No comment provided by engineer. - - - Connection request sent! - No comment provided by engineer. - - - Connection timeout - No comment provided by engineer. - - - Contact allows - No comment provided by engineer. - - - Contact already exists - No comment provided by engineer. - - - Contact and all messages will be deleted - this cannot be undone! - No comment provided by engineer. - - - Contact hidden: - notification - - - Contact is connected - notification - - - Contact is not connected yet! - No comment provided by engineer. - - - Contact name - No comment provided by engineer. - - - Contact preferences - No comment provided by engineer. - - - Contacts can mark messages for deletion; you will be able to view them. - No comment provided by engineer. - - - Continue - No comment provided by engineer. - - - Copy - chat item action - - - Core version: v%@ - No comment provided by engineer. - - - Create - No comment provided by engineer. - - - Create SimpleX address - No comment provided by engineer. - - - Create an address to let people connect with you. - No comment provided by engineer. - - - Create file - server test step - - - Create group link - No comment provided by engineer. - - - Create link - No comment provided by engineer. - - - Create one-time invitation link - No comment provided by engineer. - - - Create queue - server test step - - - Create secret group - No comment provided by engineer. - - - Create your profile - No comment provided by engineer. - - - Created on %@ - No comment provided by engineer. - - - Current Passcode - No comment provided by engineer. - - - Current passphrase… - No comment provided by engineer. - - - Currently maximum supported file size is %@. - No comment provided by engineer. - - - Custom time - No comment provided by engineer. - - - Dark - No comment provided by engineer. - - - Database ID - No comment provided by engineer. - - - Database ID: %d - copied message info - - - Database IDs and Transport isolation option. - No comment provided by engineer. - - - Database downgrade - No comment provided by engineer. - - - Database encrypted! - No comment provided by engineer. - - - Database encryption passphrase will be updated and stored in the keychain. - - No comment provided by engineer. - - - Database encryption passphrase will be updated. - - No comment provided by engineer. - - - Database error - No comment provided by engineer. - - - Database is encrypted using a random passphrase, you can change it. - No comment provided by engineer. - - - Database is encrypted using a random passphrase. Please change it before exporting. - No comment provided by engineer. - - - Database passphrase - No comment provided by engineer. - - - Database passphrase & export - No comment provided by engineer. - - - Database passphrase is different from saved in the keychain. - No comment provided by engineer. - - - Database passphrase is required to open chat. - No comment provided by engineer. - - - Database upgrade - No comment provided by engineer. - - - Database will be encrypted and the passphrase stored in the keychain. - - No comment provided by engineer. - - - Database will be encrypted. - - No comment provided by engineer. - - - Database will be migrated when the app restarts - No comment provided by engineer. - - - Decentralized - No comment provided by engineer. - - - Decryption error - No comment provided by engineer. - - - Delete - chat item action - - - Delete Contact - No comment provided by engineer. - - - Delete address - No comment provided by engineer. - - - Delete address? - No comment provided by engineer. - - - Delete after - No comment provided by engineer. - - - Delete all files - No comment provided by engineer. - - - Delete archive - No comment provided by engineer. - - - Delete chat archive? - No comment provided by engineer. - - - Delete chat profile - No comment provided by engineer. - - - Delete chat profile? - No comment provided by engineer. - - - Delete connection - No comment provided by engineer. - - - Delete contact - No comment provided by engineer. - - - Delete contact? - No comment provided by engineer. - - - Delete database - No comment provided by engineer. - - - Delete file - server test step - - - Delete files and media? - No comment provided by engineer. - - - Delete files for all chat profiles - No comment provided by engineer. - - - Delete for everyone - chat feature - - - Delete for me - No comment provided by engineer. - - - Delete group - No comment provided by engineer. - - - Delete group? - No comment provided by engineer. - - - Delete invitation - No comment provided by engineer. - - - Delete link - No comment provided by engineer. - - - Delete link? - No comment provided by engineer. - - - Delete member message? - No comment provided by engineer. - - - Delete message? - No comment provided by engineer. - - - Delete messages - No comment provided by engineer. - - - Delete messages after - No comment provided by engineer. - - - Delete old database - No comment provided by engineer. - - - Delete old database? - No comment provided by engineer. - - - Delete pending connection - No comment provided by engineer. - - - Delete pending connection? - No comment provided by engineer. - - - Delete profile - No comment provided by engineer. - - - Delete queue - server test step - - - Delete user profile? - No comment provided by engineer. - - - Deleted at - No comment provided by engineer. - - - Deleted at: %@ - copied message info - - - Description - No comment provided by engineer. - - - Develop - No comment provided by engineer. - - - Developer tools - No comment provided by engineer. - - - Device - No comment provided by engineer. - - - Device authentication is disabled. Turning off SimpleX Lock. - No comment provided by engineer. - - - Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication. - No comment provided by engineer. - - - Different names, avatars and transport isolation. - No comment provided by engineer. - - - Direct messages - chat feature - - - Direct messages between members are prohibited in this group. - No comment provided by engineer. - - - Disable SimpleX Lock - authentication reason - - - Disappearing message - No comment provided by engineer. - - - Disappearing messages - chat feature - - - Disappearing messages are prohibited in this chat. - No comment provided by engineer. - - - Disappearing messages are prohibited in this group. - No comment provided by engineer. - - - Disappears at - No comment provided by engineer. - - - Disappears at: %@ - copied message info - - - Disconnect - server test step - - - Display name - No comment provided by engineer. - - - Display name: - No comment provided by engineer. - - - Do NOT use SimpleX for emergency calls. - No comment provided by engineer. - - - Do it later - No comment provided by engineer. - - - Don't create address - No comment provided by engineer. - - - Don't show again - No comment provided by engineer. - - - Downgrade and open chat - No comment provided by engineer. - - - Download file - server test step - - - Duplicate display name! - No comment provided by engineer. - - - Duration - No comment provided by engineer. - - - Edit - chat item action - - - Edit group profile - No comment provided by engineer. - - - Enable - No comment provided by engineer. - - - Enable SimpleX Lock - authentication reason - - - Enable TCP keep-alive - No comment provided by engineer. - - - Enable automatic message deletion? - No comment provided by engineer. - - - Enable instant notifications? - No comment provided by engineer. - - - Enable lock - No comment provided by engineer. - - - Enable notifications - No comment provided by engineer. - - - Enable periodic notifications? - No comment provided by engineer. - - - Enable self-destruct - No comment provided by engineer. - - - Enable self-destruct passcode - set passcode view - - - Encrypt - No comment provided by engineer. - - - Encrypt database? - No comment provided by engineer. - - - Encrypted database - No comment provided by engineer. - - - Encrypted message or another event - notification - - - Encrypted message: database error - notification - - - Encrypted message: database migration error - notification - - - Encrypted message: keychain error - notification - - - Encrypted message: no passphrase - notification - - - Encrypted message: unexpected error - notification - - - Enter Passcode - No comment provided by engineer. - - - Enter correct passphrase. - No comment provided by engineer. - - - Enter passphrase… - No comment provided by engineer. - - - Enter password above to show! - No comment provided by engineer. - - - Enter server manually - No comment provided by engineer. - - - Enter welcome message… - placeholder - - - Enter welcome message… (optional) - placeholder - - - Error - No comment provided by engineer. - - - Error accepting contact request - No comment provided by engineer. - - - Error accessing database file - No comment provided by engineer. - - - Error adding member(s) - No comment provided by engineer. - - - Error changing address - No comment provided by engineer. - - - Error changing role - No comment provided by engineer. - - - Error changing setting - No comment provided by engineer. - - - Error creating address - No comment provided by engineer. - - - Error creating group - No comment provided by engineer. - - - Error creating group link - No comment provided by engineer. - - - Error creating profile! - No comment provided by engineer. - - - Error deleting chat database - No comment provided by engineer. - - - Error deleting chat! - No comment provided by engineer. - - - Error deleting connection - No comment provided by engineer. - - - Error deleting contact - No comment provided by engineer. - - - Error deleting database - No comment provided by engineer. - - - Error deleting old database - No comment provided by engineer. - - - Error deleting token - No comment provided by engineer. - - - Error deleting user profile - No comment provided by engineer. - - - Error enabling notifications - No comment provided by engineer. - - - Error encrypting database - No comment provided by engineer. - - - Error exporting chat database - No comment provided by engineer. - - - Error importing chat database - No comment provided by engineer. - - - Error joining group - No comment provided by engineer. - - - Error loading %@ servers - No comment provided by engineer. - - - Error receiving file - No comment provided by engineer. - - - Error removing member - No comment provided by engineer. - - - Error saving %@ servers - No comment provided by engineer. - - - Error saving ICE servers - No comment provided by engineer. - - - Error saving group profile - No comment provided by engineer. - - - Error saving passcode - No comment provided by engineer. - - - Error saving passphrase to keychain - No comment provided by engineer. - - - Error saving user password - No comment provided by engineer. - - - Error sending email - No comment provided by engineer. - - - Error sending message - No comment provided by engineer. - - - Error starting chat - No comment provided by engineer. - - - Error stopping chat - No comment provided by engineer. - - - Error switching profile! - No comment provided by engineer. - - - Error updating group link - No comment provided by engineer. - - - Error updating message - No comment provided by engineer. - - - Error updating settings - No comment provided by engineer. - - - Error updating user privacy - No comment provided by engineer. - - - Error: - No comment provided by engineer. - - - Error: %@ - No comment provided by engineer. - - - Error: URL is invalid - No comment provided by engineer. - - - Error: no database file - No comment provided by engineer. - - - Exit without saving - No comment provided by engineer. - - - Export database - No comment provided by engineer. - - - Export error: - No comment provided by engineer. - - - Exported database archive. - No comment provided by engineer. - - - Exporting database archive... - No comment provided by engineer. - - - Failed to remove passphrase - No comment provided by engineer. - - - Fast and no wait until the sender is online! - No comment provided by engineer. - - - File will be deleted from servers. - No comment provided by engineer. - - - File will be received when your contact completes uploading it. - No comment provided by engineer. - - - File will be received when your contact is online, please wait or check later! - No comment provided by engineer. - - - File: %@ - No comment provided by engineer. - - - Files & media - No comment provided by engineer. - - - Finally, we have them! 🚀 - No comment provided by engineer. - - - For console - No comment provided by engineer. - - - French interface - No comment provided by engineer. - - - Full link - No comment provided by engineer. - - - Full name (optional) - No comment provided by engineer. - - - Full name: - No comment provided by engineer. - - - Fully re-implemented - work in background! - No comment provided by engineer. - - - Further reduced battery usage - No comment provided by engineer. - - - GIFs and stickers - No comment provided by engineer. - - - Group - No comment provided by engineer. - - - Group display name - No comment provided by engineer. - - - Group full name (optional) - No comment provided by engineer. - - - Group image - No comment provided by engineer. - - - Group invitation - No comment provided by engineer. - - - Group invitation expired - No comment provided by engineer. - - - Group invitation is no longer valid, it was removed by sender. - No comment provided by engineer. - - - Group link - No comment provided by engineer. - - - Group links - No comment provided by engineer. - - - Group members can add message reactions. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. - No comment provided by engineer. - - - Group members can send direct messages. - No comment provided by engineer. - - - Group members can send disappearing messages. - No comment provided by engineer. - - - Group members can send voice messages. - No comment provided by engineer. - - - Group message: - notification - - - Group moderation - No comment provided by engineer. - - - Group preferences - No comment provided by engineer. - - - Group profile - No comment provided by engineer. - - - Group profile is stored on members' devices, not on the servers. - No comment provided by engineer. - - - Group welcome message - No comment provided by engineer. - - - Group will be deleted for all members - this cannot be undone! - No comment provided by engineer. - - - Group will be deleted for you - this cannot be undone! - No comment provided by engineer. - - - Help - No comment provided by engineer. - - - Hidden - No comment provided by engineer. - - - Hidden chat profiles - No comment provided by engineer. - - - Hidden profile password - No comment provided by engineer. - - - Hide - chat item action - - - Hide app screen in the recent apps. - No comment provided by engineer. - - - Hide profile - No comment provided by engineer. - - - Hide: - No comment provided by engineer. - - - History - copied message info - - - How SimpleX works - No comment provided by engineer. - - - How it works - No comment provided by engineer. - - - How to - No comment provided by engineer. - - - How to use it - No comment provided by engineer. - - - How to use your servers - No comment provided by engineer. - - - ICE servers (one per line) - No comment provided by engineer. - - - If you can't meet in person, show QR code in a video call, or share the link. - No comment provided by engineer. - - - If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link. - No comment provided by engineer. - - - If you enter this passcode when opening the app, all app data will be irreversibly removed! - No comment provided by engineer. - - - If you enter your self-destruct passcode while opening the app: - No comment provided by engineer. - - - If you need to use the chat now tap **Do it later** below (you will be offered to migrate the database when you restart the app). - No comment provided by engineer. - - - Ignore - No comment provided by engineer. - - - Image will be received when your contact completes uploading it. - No comment provided by engineer. - - - Image will be received when your contact is online, please wait or check later! - No comment provided by engineer. - - - Immediately - No comment provided by engineer. - - - Immune to spam and abuse - No comment provided by engineer. - - - Import - No comment provided by engineer. - - - Import chat database? - No comment provided by engineer. - - - Import database - No comment provided by engineer. - - - Improved privacy and security - No comment provided by engineer. - - - Improved server configuration - No comment provided by engineer. - - - Incognito - No comment provided by engineer. - - - Incognito mode - No comment provided by engineer. - - - Incognito mode is not supported here - your main profile will be sent to group members - No comment provided by engineer. - - - Incognito mode protects the privacy of your main profile name and image — for each new contact a new random profile is created. - No comment provided by engineer. - - - Incoming audio call - notification - - - Incoming call - notification - - - Incoming video call - notification - - - Incompatible database version - No comment provided by engineer. - - - Incorrect passcode - PIN entry - - - Incorrect security code! - No comment provided by engineer. - - - Info - chat item action - - - Initial role - No comment provided by engineer. - - - Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) - No comment provided by engineer. - - - Instant push notifications will be hidden! - - No comment provided by engineer. - - - Instantly - No comment provided by engineer. - - - Interface - No comment provided by engineer. - - - Invalid connection link - No comment provided by engineer. - - - Invalid server address! - No comment provided by engineer. - - - Invitation expired! - No comment provided by engineer. - - - Invite friends - No comment provided by engineer. - - - Invite members - No comment provided by engineer. - - - Invite to group - No comment provided by engineer. - - - Irreversible message deletion - No comment provided by engineer. - - - Irreversible message deletion is prohibited in this chat. - No comment provided by engineer. - - - Irreversible message deletion is prohibited in this group. - No comment provided by engineer. - - - It allows having many anonymous connections without any shared data between them in a single chat profile. - No comment provided by engineer. - - - It can happen when you or your connection used the old database backup. - No comment provided by engineer. - - - It can happen when: -1. The messages expired in the sending client after 2 days or on the server after 30 days. -2. Message decryption failed, because you or your contact used old database backup. -3. The connection was compromised. - No comment provided by engineer. - - - It seems like you are already connected via this link. If it is not the case, there was an error (%@). - No comment provided by engineer. - - - Italian interface - No comment provided by engineer. - - - Japanese interface - No comment provided by engineer. - - - Join - No comment provided by engineer. - - - Join group - No comment provided by engineer. - - - Join incognito - No comment provided by engineer. - - - Joining group - No comment provided by engineer. - - - KeyChain error - No comment provided by engineer. - - - Keychain error - No comment provided by engineer. - - - LIVE - No comment provided by engineer. - - - Large file! - No comment provided by engineer. - - - Learn more - No comment provided by engineer. - - - Leave - No comment provided by engineer. - - - Leave group - No comment provided by engineer. - - - Leave group? - No comment provided by engineer. - - - Let's talk in SimpleX Chat - email subject - - - Light - No comment provided by engineer. - - - Limitations - No comment provided by engineer. - - - Live message! - No comment provided by engineer. - - - Live messages - No comment provided by engineer. - - - Local name - No comment provided by engineer. - - - Local profile data only - No comment provided by engineer. - - - Lock after - No comment provided by engineer. - - - Lock mode - No comment provided by engineer. - - - Make a private connection - No comment provided by engineer. - - - Make profile private! - No comment provided by engineer. - - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - No comment provided by engineer. - - - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. - No comment provided by engineer. - - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - No comment provided by engineer. - - - Mark deleted for everyone - No comment provided by engineer. - - - Mark read - No comment provided by engineer. - - - Mark verified - No comment provided by engineer. - - - Markdown in messages - No comment provided by engineer. - - - Max 30 seconds, received instantly. - No comment provided by engineer. - - - Member - No comment provided by engineer. - - - Member role will be changed to "%@". All group members will be notified. - No comment provided by engineer. - - - Member role will be changed to "%@". The member will receive a new invitation. - No comment provided by engineer. - - - Member will be removed from group - this cannot be undone! - No comment provided by engineer. - - - Message delivery error - No comment provided by engineer. - - - Message draft - No comment provided by engineer. - - - Message reactions - chat feature - - - Message reactions are prohibited in this chat. - No comment provided by engineer. - - - Message reactions are prohibited in this group. - No comment provided by engineer. - - - Message text - No comment provided by engineer. - - - Messages - No comment provided by engineer. - - - Messages & files - No comment provided by engineer. - - - Migrating database archive... - No comment provided by engineer. - - - Migration error: - No comment provided by engineer. - - - Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat). - No comment provided by engineer. - - - Migration is completed - No comment provided by engineer. - - - Migrations: %@ - No comment provided by engineer. - - - Moderate - chat item action - - - Moderated at - No comment provided by engineer. - - - Moderated at: %@ - copied message info - - - More improvements are coming soon! - No comment provided by engineer. - - - Most likely this contact has deleted the connection with you. - No comment provided by engineer. - - - Multiple chat profiles - No comment provided by engineer. - - - Mute - No comment provided by engineer. - - - Muted when inactive! - No comment provided by engineer. - - - Name - No comment provided by engineer. - - - Network & servers - No comment provided by engineer. - - - Network settings - No comment provided by engineer. - - - Network status - No comment provided by engineer. - - - New Passcode - No comment provided by engineer. - - - New contact request - notification - - - New contact: - notification - - - New database archive - No comment provided by engineer. - - - New display name - No comment provided by engineer. - - - New in %@ - No comment provided by engineer. - - - New member role - No comment provided by engineer. - - - New message - notification - - - New passphrase… - No comment provided by engineer. - - - No - No comment provided by engineer. - - - No app password - Authentication unavailable - - - No contacts selected - No comment provided by engineer. - - - No contacts to add - No comment provided by engineer. - - - No device token! - No comment provided by engineer. - - - Group not found! - No comment provided by engineer. - - - No permission to record voice message - No comment provided by engineer. - - - No received or sent files - No comment provided by engineer. - - - Notifications - No comment provided by engineer. - - - Notifications are disabled! - No comment provided by engineer. - - - Now admins can: -- delete members' messages. -- disable members ("observer" role) - No comment provided by engineer. - - - Off - No comment provided by engineer. - - - Off (Local) - No comment provided by engineer. - - - Ok - No comment provided by engineer. - - - Old database - No comment provided by engineer. - - - Old database archive - No comment provided by engineer. - - - One-time invitation link - No comment provided by engineer. - - - Onion hosts will be required for connection. Requires enabling VPN. - No comment provided by engineer. - - - Onion hosts will be used when available. Requires enabling VPN. - No comment provided by engineer. - - - Onion hosts will not be used. - No comment provided by engineer. - - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. - No comment provided by engineer. - - - Only group owners can change group preferences. - No comment provided by engineer. - - - Only group owners can enable voice messages. - No comment provided by engineer. - - - Only you can add message reactions. - No comment provided by engineer. - - - Only you can irreversibly delete messages (your contact can mark them for deletion). - No comment provided by engineer. - - - Only you can make calls. - No comment provided by engineer. - - - Only you can send disappearing messages. - No comment provided by engineer. - - - Only you can send voice messages. - No comment provided by engineer. - - - Only your contact can add message reactions. - No comment provided by engineer. - - - Only your contact can irreversibly delete messages (you can mark them for deletion). - No comment provided by engineer. - - - Only your contact can make calls. - No comment provided by engineer. - - - Only your contact can send disappearing messages. - No comment provided by engineer. - - - Only your contact can send voice messages. - No comment provided by engineer. - - - Open Settings - No comment provided by engineer. - - - Open chat - No comment provided by engineer. - - - Open chat console - authentication reason - - - Open user profiles - authentication reason - - - Open-source protocol and code – anybody can run the servers. - No comment provided by engineer. - - - Opening database… - No comment provided by engineer. - - - Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red. - No comment provided by engineer. - - - PING count - No comment provided by engineer. - - - PING interval - No comment provided by engineer. - - - Passcode - No comment provided by engineer. - - - Passcode changed! - No comment provided by engineer. - - - Passcode entry - No comment provided by engineer. - - - Passcode not changed! - No comment provided by engineer. - - - Passcode set! - No comment provided by engineer. - - - Password to show - No comment provided by engineer. - - - Paste - No comment provided by engineer. - - - Paste image - No comment provided by engineer. - - - Paste received link - No comment provided by engineer. - - - Paste the link you received into the box below to connect with your contact. - No comment provided by engineer. - - - People can connect to you only via the links you share. - No comment provided by engineer. - - - Periodically - No comment provided by engineer. - - - Permanent decryption error - message decrypt error item - - - Please ask your contact to enable sending voice messages. - No comment provided by engineer. - - - Please check that you used the correct link or ask your contact to send you another one. - No comment provided by engineer. - - - Please check your network connection with %@ and try again. - No comment provided by engineer. - - - Please check yours and your contact preferences. - No comment provided by engineer. - - - Please contact group admin. - No comment provided by engineer. - - - Please enter correct current passphrase. - No comment provided by engineer. - - - Please enter the previous password after restoring database backup. This action can not be undone. - No comment provided by engineer. - - - Please remember or store it securely - there is no way to recover a lost passcode! - No comment provided by engineer. - - - Please report it to the developers. - No comment provided by engineer. - - - Please restart the app and migrate the database to enable push notifications. - No comment provided by engineer. - - - Please store passphrase securely, you will NOT be able to access chat if you lose it. - No comment provided by engineer. - - - Please store passphrase securely, you will NOT be able to change it if you lose it. - No comment provided by engineer. - - - Polish interface - No comment provided by engineer. - - - Possibly, certificate fingerprint in server address is incorrect - server test error - - - Preserve the last message draft, with attachments. - No comment provided by engineer. - - - Preset server - No comment provided by engineer. - - - Preset server address - No comment provided by engineer. - - - Preview - No comment provided by engineer. - - - Privacy & security - No comment provided by engineer. - - - Privacy redefined - No comment provided by engineer. - - - Private filenames - No comment provided by engineer. - - - Profile and server connections - No comment provided by engineer. - - - Profile image - No comment provided by engineer. - - - Profile password - No comment provided by engineer. - - - Profile update will be sent to your contacts. - No comment provided by engineer. - - - Prohibit audio/video calls. - No comment provided by engineer. - - - Prohibit irreversible message deletion. - No comment provided by engineer. - - - Prohibit message reactions. - No comment provided by engineer. - - - Prohibit messages reactions. - No comment provided by engineer. - - - Prohibit sending direct messages to members. - No comment provided by engineer. - - - Prohibit sending disappearing messages. - No comment provided by engineer. - - - Prohibit sending voice messages. - No comment provided by engineer. - - - Protect app screen - No comment provided by engineer. - - - Protect your chat profiles with a password! - No comment provided by engineer. - - - Protocol timeout - No comment provided by engineer. - - - Push notifications - No comment provided by engineer. - - - Rate the app - No comment provided by engineer. - - - React... - chat item menu - - - Read - No comment provided by engineer. - - - Read more - No comment provided by engineer. - - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - - - Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). - No comment provided by engineer. - - - Read more in our GitHub repository. - No comment provided by engineer. - - - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). - No comment provided by engineer. - - - Received at - No comment provided by engineer. - - - Received at: %@ - copied message info - - - Received file event - notification - - - Received message - message info title - - - Receiving file will be stopped. - No comment provided by engineer. - - - Receiving via - No comment provided by engineer. - - - Recipients see updates as you type them. - No comment provided by engineer. - - - Record updated at - No comment provided by engineer. - - - Record updated at: %@ - copied message info - - - Reduced battery usage - No comment provided by engineer. - - - Reject - reject incoming call via notification - - - Reject contact (sender NOT notified) - No comment provided by engineer. - - - Reject contact request - No comment provided by engineer. - - - Relay server is only used if necessary. Another party can observe your IP address. - No comment provided by engineer. - - - Relay server protects your IP address, but it can observe the duration of the call. - No comment provided by engineer. - - - Remove - No comment provided by engineer. - - - Remove member - No comment provided by engineer. - - - Remove member? - No comment provided by engineer. - - - Remove passphrase from keychain? - No comment provided by engineer. - - - Reply - chat item action - - - Required - No comment provided by engineer. - - - Reset - No comment provided by engineer. - - - Reset colors - No comment provided by engineer. - - - Reset to defaults - No comment provided by engineer. - - - Restart the app to create a new chat profile - No comment provided by engineer. - - - Restart the app to use imported chat database - No comment provided by engineer. - - - Restore - No comment provided by engineer. - - - Restore database backup - No comment provided by engineer. - - - Restore database backup? - No comment provided by engineer. - - - Restore database error - No comment provided by engineer. - - - Reveal - chat item action - - - Revert - No comment provided by engineer. - - - Revoke - No comment provided by engineer. - - - Revoke file - cancel file action - - - Revoke file? - No comment provided by engineer. - - - Role - No comment provided by engineer. - - - Run chat - No comment provided by engineer. - - - SMP servers - No comment provided by engineer. - - - Save - chat item action - - - Save (and notify contacts) - No comment provided by engineer. - - - Save and notify contact - No comment provided by engineer. - - - Save and notify group members - No comment provided by engineer. - - - Save and update group profile - No comment provided by engineer. - - - Save archive - No comment provided by engineer. - - - Save auto-accept settings - No comment provided by engineer. - - - Save group profile - No comment provided by engineer. - - - Save passphrase and open chat - No comment provided by engineer. - - - Save passphrase in Keychain - No comment provided by engineer. - - - Save preferences? - No comment provided by engineer. - - - Save profile password - No comment provided by engineer. - - - Save servers - No comment provided by engineer. - - - Save servers? - No comment provided by engineer. - - - Save settings? - No comment provided by engineer. - - - Save welcome message? - No comment provided by engineer. - - - Saved WebRTC ICE servers will be removed - No comment provided by engineer. - - - Scan QR code - No comment provided by engineer. - - - Scan code - No comment provided by engineer. - - - Scan security code from your contact's app. - No comment provided by engineer. - - - Scan server QR code - No comment provided by engineer. - - - Search - No comment provided by engineer. - - - Secure queue - server test step - - - Security assessment - No comment provided by engineer. - - - Security code - No comment provided by engineer. - - - Select - No comment provided by engineer. - - - Self-destruct - No comment provided by engineer. - - - Self-destruct passcode - No comment provided by engineer. - - - Self-destruct passcode changed! - No comment provided by engineer. - - - Self-destruct passcode enabled! - No comment provided by engineer. - - - Send - No comment provided by engineer. - - - Send a live message - it will update for the recipient(s) as you type it - No comment provided by engineer. - - - Send direct message - No comment provided by engineer. - - - Send disappearing message - No comment provided by engineer. - - - Send link previews - No comment provided by engineer. - - - Send live message - No comment provided by engineer. - - - Send notifications - No comment provided by engineer. - - - Send notifications: - No comment provided by engineer. - - - Send questions and ideas - No comment provided by engineer. - - - Send them from gallery or custom keyboards. - No comment provided by engineer. - - - Sender cancelled file transfer. - No comment provided by engineer. - - - Sender may have deleted the connection request. - No comment provided by engineer. - - - Sending file will be stopped. - No comment provided by engineer. - - - Sending via - No comment provided by engineer. - - - Sent at - No comment provided by engineer. - - - Sent at: %@ - copied message info - - - Sent file event - notification - - - Sent message - message info title - - - Sent messages will be deleted after set time. - No comment provided by engineer. - - - Server requires authorization to create queues, check password - server test error - - - Server requires authorization to upload, check password - server test error - - - Server test failed! - No comment provided by engineer. - - - Servers - No comment provided by engineer. - - - Set 1 day - No comment provided by engineer. - - - Set contact name… - No comment provided by engineer. - - - Set group preferences - No comment provided by engineer. - - - Set it instead of system authentication. - No comment provided by engineer. - - - Set passcode - No comment provided by engineer. - - - Set passphrase to export - No comment provided by engineer. - - - Set the message shown to new members! - No comment provided by engineer. - - - Set timeouts for proxy/VPN - No comment provided by engineer. - - - Settings - No comment provided by engineer. - - - Share - chat item action - - - Share 1-time link - No comment provided by engineer. - - - Share address - No comment provided by engineer. - - - Share address with contacts? - No comment provided by engineer. - - - Share link - No comment provided by engineer. - - - Share one-time invitation link - No comment provided by engineer. - - - Share with contacts - No comment provided by engineer. - - - Show calls in phone history - No comment provided by engineer. - - - Show developer options - No comment provided by engineer. - - - Show preview - No comment provided by engineer. - - - Show: - No comment provided by engineer. - - - SimpleX Address - No comment provided by engineer. - - - SimpleX Chat security was audited by Trail of Bits. - No comment provided by engineer. - - - SimpleX Lock - No comment provided by engineer. - - - SimpleX Lock mode - No comment provided by engineer. - - - SimpleX Lock not enabled! - No comment provided by engineer. - - - SimpleX Lock turned on - No comment provided by engineer. - - - SimpleX address - No comment provided by engineer. - - - SimpleX contact address - simplex link type - - - SimpleX encrypted message or connection event - notification - - - SimpleX group link - simplex link type - - - SimpleX links - No comment provided by engineer. - - - SimpleX one-time invitation - simplex link type - - - Skip - No comment provided by engineer. - - - Skipped messages - No comment provided by engineer. - - - Some non-fatal errors occurred during import - you may see Chat console for more details. - No comment provided by engineer. - - - Somebody - notification title - - - Start a new chat - No comment provided by engineer. - - - Start chat - No comment provided by engineer. - - - Start migration - No comment provided by engineer. - - - Stop - No comment provided by engineer. - - - Stop SimpleX - authentication reason - - - Stop chat to enable database actions - No comment provided by engineer. - - - Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. - No comment provided by engineer. - - - Stop chat? - No comment provided by engineer. - - - Stop file - cancel file action - - - Stop receiving file? - No comment provided by engineer. - - - Stop sending file? - No comment provided by engineer. - - - Stop sharing - No comment provided by engineer. - - - Stop sharing address? - No comment provided by engineer. - - - Submit - No comment provided by engineer. - - - Support SimpleX Chat - No comment provided by engineer. - - - System - No comment provided by engineer. - - - System authentication - No comment provided by engineer. - - - TCP connection timeout - No comment provided by engineer. - - - TCP_KEEPCNT - No comment provided by engineer. - - - TCP_KEEPIDLE - No comment provided by engineer. - - - TCP_KEEPINTVL - No comment provided by engineer. - - - Take picture - No comment provided by engineer. - - - Tap button - No comment provided by engineer. - - - Tap to activate profile. - No comment provided by engineer. - - - Tap to join - No comment provided by engineer. - - - Tap to join incognito - No comment provided by engineer. - - - Tap to start a new chat - No comment provided by engineer. - - - Test failed at step %@. - server test failure - - - Test server - No comment provided by engineer. - - - Test servers - No comment provided by engineer. - - - Tests failed! - No comment provided by engineer. - - - Thank you for installing SimpleX Chat! - No comment provided by engineer. - - - Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! - No comment provided by engineer. - - - Thanks to the users – contribute via Weblate! - No comment provided by engineer. - - - The 1st platform without any user identifiers – private by design. - No comment provided by engineer. - - - The ID of the next message is incorrect (less or equal to the previous). -It can happen because of some bug or when the connection is compromised. - No comment provided by engineer. - - - The app can notify you when you receive messages or contact requests - please open settings to enable. - No comment provided by engineer. - - - The attempt to change database passphrase was not completed. - No comment provided by engineer. - - - The connection you accepted will be cancelled! - No comment provided by engineer. - - - The contact you shared this link with will NOT be able to connect! - No comment provided by engineer. - - - The created archive is available via app Settings / Database / Old database archive. - No comment provided by engineer. - - - The group is fully decentralized – it is visible only to the members. - No comment provided by engineer. - - - The hash of the previous message is different. - No comment provided by engineer. - - - The message will be deleted for all members. - No comment provided by engineer. - - - The message will be marked as moderated for all members. - No comment provided by engineer. - - - The next generation of private messaging - No comment provided by engineer. - - - The old database was not removed during the migration, it can be deleted. - No comment provided by engineer. - - - The profile is only shared with your contacts. - No comment provided by engineer. - - - The sender will NOT be notified - No comment provided by engineer. - - - The servers for new connections of your current chat profile **%@**. - No comment provided by engineer. - - - Theme - No comment provided by engineer. - - - There should be at least one user profile. - No comment provided by engineer. - - - There should be at least one visible user profile. - No comment provided by engineer. - - - This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain. - No comment provided by engineer. - - - This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes. - No comment provided by engineer. - - - This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. - No comment provided by engineer. - - - This error is permanent for this connection, please re-connect. - No comment provided by engineer. - - - This feature is experimental! It will only work if the other client has version 4.2 installed. You should see the message in the conversation once the address change is completed – please check that you can still receive messages from this contact (or group member). - No comment provided by engineer. - - - This group no longer exists. - No comment provided by engineer. - - - This setting applies to messages in your current chat profile **%@**. - No comment provided by engineer. - - - To ask any questions and to receive updates: - No comment provided by engineer. - - - To connect, your contact can scan QR code or use the link in the app. - No comment provided by engineer. - - - To find the profile used for an incognito connection, tap the contact or group name on top of the chat. - No comment provided by engineer. - - - To make a new connection - No comment provided by engineer. - - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - No comment provided by engineer. - - - To protect timezone, image/voice files use UTC. - No comment provided by engineer. - - - To protect your information, turn on SimpleX Lock. -You will be prompted to complete authentication before this feature is enabled. - No comment provided by engineer. - - - To record voice message please grant permission to use Microphone. - No comment provided by engineer. - - - To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. - No comment provided by engineer. - - - To support instant push notifications the chat database has to be migrated. - No comment provided by engineer. - - - To verify end-to-end encryption with your contact compare (or scan) the code on your devices. - No comment provided by engineer. - - - Transport isolation - No comment provided by engineer. - - - Trying to connect to the server used to receive messages from this contact (error: %@). - No comment provided by engineer. - - - Trying to connect to the server used to receive messages from this contact. - No comment provided by engineer. - - - Turn off - No comment provided by engineer. - - - Turn off notifications? - No comment provided by engineer. - - - Turn on - No comment provided by engineer. - - - Unable to record voice message - No comment provided by engineer. - - - Unexpected error: %@ - No comment provided by engineer. - - - Unexpected migration state - No comment provided by engineer. - - - Unhide - No comment provided by engineer. - - - Unhide chat profile - No comment provided by engineer. - - - Unhide profile - No comment provided by engineer. - - - Unit - No comment provided by engineer. - - - Unknown caller - callkit banner - - - Unknown database error: %@ - No comment provided by engineer. - - - Unknown error - No comment provided by engineer. - - - Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. - No comment provided by engineer. - - - Unless your contact deleted the connection or this link was already used, it might be a bug - please report it. -To connect, please ask your contact to create another connection link and check that you have a stable network connection. - No comment provided by engineer. - - - Unlock - No comment provided by engineer. - - - Unlock app - authentication reason - - - Unmute - No comment provided by engineer. - - - Unread - No comment provided by engineer. - - - Update - No comment provided by engineer. - - - Update .onion hosts setting? - No comment provided by engineer. - - - Update database passphrase - No comment provided by engineer. - - - Update network settings? - No comment provided by engineer. - - - Update transport isolation mode? - No comment provided by engineer. - - - Updating settings will re-connect the client to all servers. - No comment provided by engineer. - - - Updating this setting will re-connect the client to all servers. - No comment provided by engineer. - - - Upgrade and open chat - No comment provided by engineer. - - - Upload file - server test step - - - Use .onion hosts - No comment provided by engineer. - - - Use SimpleX Chat servers? - No comment provided by engineer. - - - Use chat - No comment provided by engineer. - - - Use for new connections - No comment provided by engineer. - - - Use iOS call interface - No comment provided by engineer. - - - Use server - No comment provided by engineer. - - - User profile - No comment provided by engineer. - - - Using .onion hosts requires compatible VPN provider. - No comment provided by engineer. - - - Using SimpleX Chat servers. - No comment provided by engineer. - - - Verify connection security - No comment provided by engineer. - - - Verify security code - No comment provided by engineer. - - - Via browser - No comment provided by engineer. - - - Video call - No comment provided by engineer. - - - Video will be received when your contact completes uploading it. - No comment provided by engineer. - - - Video will be received when your contact is online, please wait or check later! - No comment provided by engineer. - - - Videos and files up to 1gb - No comment provided by engineer. - - - View security code - No comment provided by engineer. - - - Voice messages - chat feature - - - Voice messages are prohibited in this chat. - No comment provided by engineer. - - - Voice messages are prohibited in this group. - No comment provided by engineer. - - - Voice messages prohibited! - No comment provided by engineer. - - - Voice message… - No comment provided by engineer. - - - Waiting for file - No comment provided by engineer. - - - Waiting for image - No comment provided by engineer. - - - Waiting for video - No comment provided by engineer. - - - Warning: you may lose some data! - No comment provided by engineer. - - - WebRTC ICE servers - No comment provided by engineer. - - - Welcome %@! - No comment provided by engineer. - - - Welcome message - No comment provided by engineer. - - - What's new - No comment provided by engineer. - - - When available - No comment provided by engineer. - - - When people request to connect, you can accept or reject it. - No comment provided by engineer. - - - When you share an incognito profile with somebody, this profile will be used for the groups they invite you to. - No comment provided by engineer. - - - With optional welcome message. - No comment provided by engineer. - - - Wrong database passphrase - No comment provided by engineer. - - - Wrong passphrase! - No comment provided by engineer. - - - XFTP servers - No comment provided by engineer. - - - You - No comment provided by engineer. - - - You accepted connection - No comment provided by engineer. - - - You allow - No comment provided by engineer. - - - You already have a chat profile with the same display name. Please choose another name. - No comment provided by engineer. - - - You are already connected to %@. - No comment provided by engineer. - - - You are connected to the server used to receive messages from this contact. - No comment provided by engineer. - - - You are invited to group - No comment provided by engineer. - - - You can accept calls from lock screen, without device and app authentication. - No comment provided by engineer. - - - You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button. - No comment provided by engineer. - - - You can create it later - No comment provided by engineer. - - - You can hide or mute a user profile - swipe it to the right. - No comment provided by engineer. - - - You can now send messages to %@ - notification body - - - You can set lock screen notification preview via settings. - No comment provided by engineer. - - - You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it. - No comment provided by engineer. - - - You can share this address with your contacts to let them connect with **%@**. - No comment provided by engineer. - - - You can share your address as a link or QR code - anybody can connect to you. - No comment provided by engineer. - - - You can start chat via app Settings / Database or by restarting the app - No comment provided by engineer. - - - You can turn on SimpleX Lock via Settings. - No comment provided by engineer. - - - You can use markdown to format messages: - No comment provided by engineer. - - - You can't send messages! - No comment provided by engineer. - - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - - - You could not be verified; please try again. - No comment provided by engineer. - - - You have no chats - No comment provided by engineer. - - - You have to enter passphrase every time the app starts - it is not stored on the device. - No comment provided by engineer. - - - You invited your contact - No comment provided by engineer. - - - You joined this group - No comment provided by engineer. - - - You joined this group. Connecting to inviting group member. - No comment provided by engineer. - - - You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. - No comment provided by engineer. - - - You need to allow your contact to send voice messages to be able to send them. - No comment provided by engineer. - - - You rejected group invitation - No comment provided by engineer. - - - You sent group invitation - No comment provided by engineer. - - - You will be connected to group when the group host's device is online, please wait or check later! - No comment provided by engineer. - - - You will be connected when your connection request is accepted, please wait or check later! - No comment provided by engineer. - - - You will be connected when your contact's device is online, please wait or check later! - No comment provided by engineer. - - - You will be required to authenticate when you start or resume the app after 30 seconds in background. - No comment provided by engineer. - - - You will join a group this link refers to and connect to its group members. - No comment provided by engineer. - - - You will still receive calls and notifications from muted profiles when they are active. - No comment provided by engineer. - - - You will stop receiving messages from this group. Chat history will be preserved. - No comment provided by engineer. - - - You won't lose your contacts if you later delete your address. - No comment provided by engineer. - - - You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile - No comment provided by engineer. - - - You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed - No comment provided by engineer. - - - Your %@ servers - No comment provided by engineer. - - - Your ICE servers - No comment provided by engineer. - - - Your SMP servers - No comment provided by engineer. - - - Your SimpleX address - No comment provided by engineer. - - - Your XFTP servers - No comment provided by engineer. - - - Your calls - No comment provided by engineer. - - - Your chat database - No comment provided by engineer. - - - Your chat database is not encrypted - set passphrase to encrypt it. - No comment provided by engineer. - - - Your chat profile will be sent to group members - No comment provided by engineer. - - - Your chat profile will be sent to your contact - No comment provided by engineer. - - - Your chat profiles - No comment provided by engineer. - - - Your chats - No comment provided by engineer. - - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - No comment provided by engineer. - - - Your contact sent a file that is larger than currently supported maximum size (%@). - No comment provided by engineer. - - - Your contacts can allow full message deletion. - No comment provided by engineer. - - - Your contacts in SimpleX will see it. -You can change it in Settings. - No comment provided by engineer. - - - Your contacts will remain connected. - No comment provided by engineer. - - - Your current chat database will be DELETED and REPLACED with the imported one. - No comment provided by engineer. - - - Your current profile - No comment provided by engineer. - - - Your preferences - No comment provided by engineer. - - - Your privacy - No comment provided by engineer. - - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - No comment provided by engineer. - - - Your profile will be sent to the contact that you received this link from - No comment provided by engineer. - - - Your profile, contacts and delivered messages are stored on your device. - No comment provided by engineer. - - - Your random profile - No comment provided by engineer. - - - Your server - No comment provided by engineer. - - - Your server address - No comment provided by engineer. - - - Your settings - No comment provided by engineer. - - - [Contribute](https://github.com/simplex-chat/simplex-chat#contribute) - No comment provided by engineer. - - - [Send us email](mailto:chat@simplex.chat) - No comment provided by engineer. - - - [Star on GitHub](https://github.com/simplex-chat/simplex-chat) - No comment provided by engineer. - - - \_italic_ - No comment provided by engineer. - - - \`a + b` - No comment provided by engineer. - - - above, then choose: - No comment provided by engineer. - - - accepted call - call status - - - admin - member role - - - always - pref value - - - audio call (not e2e encrypted) - No comment provided by engineer. - - - bad message ID - integrity error chat item - - - bad message hash - integrity error chat item - - - bold - No comment provided by engineer. - - - call error - call status - - - call in progress - call status - - - calling… - call status - - - cancelled %@ - feature offered item - - - changed address for you - chat item text - - - changed role of %1$@ to %2$@ - rcv group event chat item - - - changed your role to %@ - rcv group event chat item - - - changing address for %@... - chat item text - - - changing address... - chat item text - - - colored - No comment provided by engineer. - - - complete - No comment provided by engineer. - - - connect to SimpleX Chat developers. - No comment provided by engineer. - - - connected - No comment provided by engineer. - - - connecting - No comment provided by engineer. - - - connecting (accepted) - No comment provided by engineer. - - - connecting (announced) - No comment provided by engineer. - - - connecting (introduced) - No comment provided by engineer. - - - connecting (introduction invitation) - No comment provided by engineer. - - - connecting call… - call status - - - connecting… - chat list item title - - - connection established - chat list item title (it should not be shown - - - connection:%@ - connection information - - - contact has e2e encryption - No comment provided by engineer. - - - contact has no e2e encryption - No comment provided by engineer. - - - creator - No comment provided by engineer. - - - custom - dropdown time picker choice - - - database version is newer than the app, but no down migration for: %@ - No comment provided by engineer. - - - days - time unit - - - default (%@) - pref value - - - deleted - deleted chat item - - - deleted group - rcv group event chat item - - - different migration in the app/database: %@ / %@ - No comment provided by engineer. - - - direct - connection level description - - - duplicate message - integrity error chat item - - - e2e encrypted - No comment provided by engineer. - - - enabled - enabled status - - - enabled for contact - enabled status - - - enabled for you - enabled status - - - ended - No comment provided by engineer. - - - ended call %@ - call status - - - error - No comment provided by engineer. - - - group deleted - No comment provided by engineer. - - - group profile updated - snd group event chat item - - - hours - time unit - - - iOS Keychain is used to securely store passphrase - it allows receiving push notifications. - No comment provided by engineer. - - - iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications. - No comment provided by engineer. - - - incognito via contact address link - chat list item description - - - incognito via group link - chat list item description - - - incognito via one-time link - chat list item description - - - indirect (%d) - connection level description - - - invalid chat - invalid chat data - - - invalid chat data - No comment provided by engineer. - - - invalid data - invalid chat item - - - invitation to group %@ - group name - - - invited - No comment provided by engineer. - - - invited %@ - rcv group event chat item - - - invited to connect - chat list item title - - - invited via your group link - rcv group event chat item - - - italic - No comment provided by engineer. - - - join as %@ - No comment provided by engineer. - - - left - rcv group event chat item - - - marked deleted - marked deleted chat item preview text - - - member - member role - - - connected - rcv group event chat item - - - message received - notification - - - minutes - time unit - - - missed call - call status - - - moderated - moderated chat item - - - moderated by %@ - No comment provided by engineer. - - - months - time unit - - - never - No comment provided by engineer. - - - new message - notification - - - no - pref value - - - no e2e encryption - No comment provided by engineer. - - - no text - copied message info in history - - - observer - member role - - - off - enabled status - group pref value - - - offered %@ - feature offered item - - - offered %1$@: %2$@ - feature offered item - - - on - group pref value - - - or chat with the developers - No comment provided by engineer. - - - owner - member role - - - peer-to-peer - No comment provided by engineer. - - - received answer… - No comment provided by engineer. - - - received confirmation… - No comment provided by engineer. - - - rejected call - call status - - - removed - No comment provided by engineer. - - - removed %@ - rcv group event chat item - - - removed you - rcv group event chat item - - - sec - network option - - - seconds - time unit - - - secret - No comment provided by engineer. - - - starting… - No comment provided by engineer. - - - strike - No comment provided by engineer. - - - this contact - notification title - - - unknown - connection info - - - updated group profile - rcv group event chat item - - - v%@ (%@) - No comment provided by engineer. - - - via contact address link - chat list item description - - - via group link - chat list item description - - - via one-time link - chat list item description - - - via relay - No comment provided by engineer. - - - video call (not e2e encrypted) - No comment provided by engineer. - - - waiting for answer… - No comment provided by engineer. - - - waiting for confirmation… - No comment provided by engineer. - - - wants to connect to you! - No comment provided by engineer. - - - weeks - time unit - - - yes - pref value - - - you are invited to group - No comment provided by engineer. - - - you are observer - No comment provided by engineer. - - - you changed address - chat item text - - - you changed address for %@ - chat item text - - - you changed role for yourself to %@ - snd group event chat item - - - you changed role of %1$@ to %2$@ - snd group event chat item - - - you left - snd group event chat item - - - you removed %@ - snd group event chat item - - - you shared one-time link - chat list item description - - - you shared one-time link incognito - chat list item description - - - you: - No comment provided by engineer. - - - \~strike~ - No comment provided by engineer. - - -
- -
- -
- - - SimpleX - Bundle name - - - SimpleX needs camera access to scan QR codes to connect to other users and for video calls. - Privacy - Camera Usage Description - - - SimpleX uses Face ID for local authentication - Privacy - Face ID Usage Description - - - SimpleX needs microphone access for audio and video calls, and to record voice messages. - Privacy - Microphone Usage Description - - - SimpleX needs access to Photo Library for saving captured and received media - Privacy - Photo Library Additions Usage Description - - -
- -
- -
- - - SimpleX NSE - Bundle display name - - - SimpleX NSE - Bundle name - - - Copyright © 2022 SimpleX Chat. All rights reserved. - Copyright (human-readable) - - -
-
diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff index 105552ec84..4008c57ac0 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (kan gekopieerd worden) @@ -109,6 +82,7 @@ %@ downloaded + %@ gedownload No comment provided by engineer. @@ -126,6 +100,11 @@ %@ is geverifieerd No comment provided by engineer. + + %@ server + %@ server + No comment provided by engineer. + %@ servers %@ servers @@ -133,6 +112,7 @@ %@ uploaded + %@ geüpload No comment provided by engineer. @@ -140,6 +120,11 @@ %@ wil verbinding maken! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ en %lld leden @@ -160,11 +145,36 @@ %d dagen time interval + + %d file(s) are still being downloaded. + %d bestand(en) worden nog gedownload. + forward confirmation reason + + + %d file(s) failed to download. + %d bestand(en) konden niet worden gedownload. + forward confirmation reason + + + %d file(s) were deleted. + %d bestand(en) zijn verwijderd. + forward confirmation reason + + + %d file(s) were not downloaded. + %d bestand(en) zijn niet gedownload. + forward confirmation reason + %d hours %d uren time interval + + %d messages not forwarded + %d berichten niet doorgestuurd + alert title + %d min %d min @@ -180,6 +190,11 @@ %d sec time interval + + %d seconds(s) + %d seconden + delete after time + %d skipped message(s) %d overgeslagen bericht(en) @@ -250,11 +265,6 @@ %lld nieuwe interface-talen No comment provided by engineer. - - %lld second(s) - %lld seconde(n) - No comment provided by engineer. - %lld seconds %lld seconden @@ -305,11 +315,6 @@ %u berichten zijn overgeslagen. No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) (nieuw) @@ -320,38 +325,29 @@ (dit apparaat v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. + + **Create 1-time link**: to create and share a new invitation link. **Contact toevoegen**: om een nieuwe uitnodigingslink aan te maken, of verbinding te maken via een link die u heeft ontvangen. No comment provided by engineer. - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Nieuw contact toevoegen**: om uw eenmalige QR-code of link voor uw contact te maken. - No comment provided by engineer. - **Create group**: to create a new group. **Groep aanmaken**: om een nieuwe groep aan te maken. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **Meer privé**: bekijk elke 20 minuten nieuwe berichten. Apparaattoken wordt gedeeld met de SimpleX Chat-server, maar niet hoeveel contacten of berichten u heeft. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **Meest privé**: gebruik geen SimpleX Chat-notificatie server, controleer berichten regelmatig op de achtergrond (afhankelijk van hoe vaak u de app gebruikt). No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Let op**: als u dezelfde database op twee apparaten gebruikt, wordt de decodering van berichten van uw verbindingen verbroken, als veiligheidsmaatregel. No comment provided by engineer. @@ -359,11 +355,16 @@ **Let op**: u kunt het wachtwoord NIET herstellen of wijzigen als u het kwijtraakt. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Aanbevolen**: apparaattoken en meldingen worden naar de SimpleX Chat-meldingsserver gestuurd, maar niet de berichtinhoud, -grootte of van wie het afkomstig is. No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + **Link scannen/plakken**: om verbinding te maken via een link die u hebt ontvangen. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **Waarschuwing**: voor directe push meldingen is een wachtwoord vereist dat is opgeslagen in de Keychain. @@ -371,6 +372,7 @@ **Warning**: the archive will be removed. + **Waarschuwing**: het archief wordt verwijderd. No comment provided by engineer. @@ -388,11 +390,6 @@ \*vetgedrukt* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -429,11 +426,6 @@ - bewerkingsgeschiedenis. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 sec @@ -447,7 +439,8 @@ 1 day 1 dag - time interval + delete after time +time interval 1 hour @@ -462,12 +455,29 @@ 1 month 1 maand - time interval + delete after time +time interval 1 week 1 week - time interval + delete after time +time interval + + + 1 year + 1 jaar + delete after time + + + 1-time link + Eenmalige link + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + Eenmalige link die *slechts met één contactpersoon* kan worden gebruikt - deel persoonlijk of via een messenger. + No comment provided by engineer. 5 minutes @@ -484,11 +494,6 @@ 30 seconden No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -513,7 +518,7 @@ A separate TCP connection will be used **for each chat profile you have in the app**. - Er wordt een aparte TCP-verbinding gebruikt **voor elk chat profiel dat je in de app hebt**. + Er wordt een aparte TCP-verbinding gebruikt **voor elk chatprofiel dat je in de app hebt**. No comment provided by engineer. @@ -538,31 +543,32 @@ Adres wijziging afbreken? No comment provided by engineer. - - About SimpleX - Over SimpleX - No comment provided by engineer. - About SimpleX Chat Over SimpleX Chat No comment provided by engineer. - - About SimpleX address - Over SimpleX adres + + About operators + Over operatoren No comment provided by engineer. - - Accent color - Accent kleur + + Accent + Accent No comment provided by engineer. Accept Accepteer accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Accepteer voorwaarden + No comment provided by engineer. Accept connection request? @@ -577,21 +583,47 @@ Accept incognito Accepteer incognito - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + Geaccepteerde voorwaarden + No comment provided by engineer. + + + Acknowledged + Erkend + No comment provided by engineer. + + + Acknowledgement errors + Bevestigingsfouten + No comment provided by engineer. + + + Active + actief + token status text + + + Active connections + Actieve verbindingen + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Voeg een adres toe aan uw profiel, zodat uw contacten het met andere mensen kunnen delen. Profiel update wordt naar uw contacten verzonden. No comment provided by engineer. - - Add contact - Contact toevoegen + + Add friends + Vrienden toevoegen No comment provided by engineer. - - Add preset servers - Vooraf ingestelde servers toevoegen + + Add list + Lijst toevoegen No comment provided by engineer. @@ -599,14 +631,19 @@ Profiel toevoegen No comment provided by engineer. + + Add server + Server toevoegen + No comment provided by engineer. + Add servers by scanning QR codes. Servers toevoegen door QR-codes te scannen. No comment provided by engineer. - - Add server… - Server toevoegen… + + Add team members + Teamleden toevoegen No comment provided by engineer. @@ -614,9 +651,44 @@ Toevoegen aan een ander apparaat No comment provided by engineer. + + Add to list + Toevoegen aan lijst + No comment provided by engineer. + Add welcome message - Welkomst bericht toevoegen + Welkom bericht toevoegen + No comment provided by engineer. + + + Add your team members to the conversations. + Voeg uw teamleden toe aan de gesprekken. + No comment provided by engineer. + + + Added media & file servers + Media- en bestandsservers toegevoegd + No comment provided by engineer. + + + Added message servers + Berichtservers toegevoegd + No comment provided by engineer. + + + Additional accent + Extra accent + No comment provided by engineer. + + + Additional accent 2 + Extra accent 2 + No comment provided by engineer. + + + Additional secondary + Extra secundair No comment provided by engineer. @@ -629,8 +701,19 @@ Adres wijziging wordt afgebroken. Het oude ontvangstadres wordt gebruikt. No comment provided by engineer. + + Address or 1-time link? + Adres of eenmalige link? + No comment provided by engineer. + + + Address settings + Adres instellingen + No comment provided by engineer. + Admins can block a member for all. + Beheerders kunnen een lid voor iedereen blokkeren. No comment provided by engineer. @@ -643,6 +726,16 @@ Geavanceerde netwerk instellingen No comment provided by engineer. + + Advanced settings + Geavanceerde instellingen + No comment provided by engineer. + + + All + alle + No comment provided by engineer. + All app data is deleted. Alle app-gegevens worden verwijderd. @@ -653,16 +746,31 @@ Alle chats en berichten worden verwijderd, dit kan niet ongedaan worden gemaakt! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + Alle chats worden uit de lijst %@ verwijderd en de lijst wordt verwijderd. + alert message + All data is erased when it is entered. Alle gegevens worden bij het invoeren gewist. No comment provided by engineer. + + All data is kept private on your device. + Alle gegevens zijn privé op uw apparaat. + No comment provided by engineer. + All group members will remain connected. Alle groepsleden blijven verbonden. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + Alle berichten en bestanden worden **end-to-end versleuteld** verzonden, met post-quantumbeveiliging in directe berichten. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! Alle berichten worden verwijderd. Dit kan niet ongedaan worden gemaakt! @@ -678,6 +786,20 @@ Alle nieuwe berichten van %@ worden verborgen! No comment provided by engineer. + + All profiles + Alle profielen + profile dropdown + + + All reports will be archived for you. + Alle rapporten worden voor u gearchiveerd. + No comment provided by engineer. + + + All servers + No comment provided by engineer. + All your contacts will remain connected. Al uw contacten blijven verbonden. @@ -690,6 +812,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Al uw contacten, gesprekken en bestanden worden veilig gecodeerd en in delen geüpload naar geconfigureerde XFTP-relays. No comment provided by engineer. @@ -702,24 +825,34 @@ Sta oproepen alleen toe als uw contact dit toestaat. No comment provided by engineer. + + Allow calls? + Oproepen toestaan? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Sta verdwijnende berichten alleen toe als uw contact dit toestaat. No comment provided by engineer. + + Allow downgrade + Downgraden toestaan + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) - Sta het onomkeerbaar verwijderen van berichten alleen toe als uw contact dit toestaat. (24 uur) + Sta het definitief verwijderen van berichten alleen toe als uw contact dit toestaat. (24 uur) No comment provided by engineer. Allow message reactions only if your contact allows them. - Sta berichtreacties alleen toe als uw contact dit toestaat. + Sta bericht reacties alleen toe als uw contact dit toestaat. No comment provided by engineer. Allow message reactions. - Sta berichtreacties toe. + Sta bericht reacties toe. No comment provided by engineer. @@ -732,9 +865,24 @@ Toestaan dat verdwijnende berichten worden verzonden. No comment provided by engineer. + + Allow sharing + Delen toestaan + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) - Sta toe om verzonden berichten onomkeerbaar te verwijderen. (24 uur) + Sta toe om verzonden berichten definitief te verwijderen. (24 uur) + No comment provided by engineer. + + + Allow to report messsages to moderators. + Hiermee kunt u berichten rapporteren aan moderators. + No comment provided by engineer. + + + Allow to send SimpleX links. + Sta toe dat SimpleX-links worden verzonden. No comment provided by engineer. @@ -759,7 +907,7 @@ Allow your contacts adding message reactions. - Sta uw contactpersonen toe om berichtreacties toe te voegen. + Sta uw contactpersonen toe om bericht reacties toe te voegen. No comment provided by engineer. @@ -769,7 +917,7 @@ Allow your contacts to irreversibly delete sent messages. (24 hours) - Laat uw contacten verzonden berichten onomkeerbaar verwijderen. (24 uur) + Laat uw contacten verzonden berichten definitief verwijderen. (24 uur) No comment provided by engineer. @@ -797,6 +945,11 @@ Al lid van de groep! No comment provided by engineer. + + Always use private routing. + Gebruik altijd privéroutering. + No comment provided by engineer. + Always use relay Altijd relay gebruiken @@ -807,11 +960,21 @@ Er wordt een leeg chatprofiel met de opgegeven naam gemaakt en de app wordt zoals gewoonlijk geopend. No comment provided by engineer. + + Another reason + Een andere reden + report reason + Answer call Beantwoord oproep No comment provided by engineer. + + Anybody can host servers. + Iedereen kan servers hosten. + No comment provided by engineer. + App build: %@ App build: %@ @@ -819,6 +982,7 @@ App data migration + Migratie van app-gegevens No comment provided by engineer. @@ -826,6 +990,11 @@ App versleutelt nieuwe lokale bestanden (behalve video's). No comment provided by engineer. + + App group: + App-groep: + No comment provided by engineer. + App icon App icon @@ -841,6 +1010,11 @@ De app-toegangscode wordt vervangen door een zelfvernietigings wachtwoord. No comment provided by engineer. + + App session + Appsessie + No comment provided by engineer. + App version App versie @@ -858,14 +1032,62 @@ Apply + Toepassen + No comment provided by engineer. + + + Apply to + Toepassen op + No comment provided by engineer. + + + Archive + Archief + No comment provided by engineer. + + + Archive %lld reports? + %lld rapporten archiveren? + No comment provided by engineer. + + + Archive all reports? + Alle rapporten archiveren? No comment provided by engineer. Archive and upload + Archiveren en uploaden + No comment provided by engineer. + + + Archive contacts to chat later. + Archiveer contacten om later te chatten. + No comment provided by engineer. + + + Archive report + Rapport archiveren + No comment provided by engineer. + + + Archive report? + Rapport archiveren? + No comment provided by engineer. + + + Archive reports + Rapporten archiveren + swipe action + + + Archived contacts + Gearchiveerde contacten No comment provided by engineer. Archiving database + Database archiveren No comment provided by engineer. @@ -890,7 +1112,7 @@ Audio/video calls are prohibited. - Audio/video gesprekken zijn verboden. + Audio/video gesprekken zijn niet toegestaan. No comment provided by engineer. @@ -928,11 +1150,21 @@ Afbeeldingen automatisch accepteren No comment provided by engineer. + + Auto-accept settings + Instellingen automatisch accepteren + alert title + Back Terug No comment provided by engineer. + + Background + Achtergrond + No comment provided by engineer. + Bad desktop address Onjuist desktopadres @@ -948,16 +1180,61 @@ Onjuiste bericht hash No comment provided by engineer. + + Better calls + Betere gesprekken + No comment provided by engineer. + Better groups Betere groepen No comment provided by engineer. + + Better groups performance + Betere prestaties van groepen + No comment provided by engineer. + + + Better message dates. + Betere datums voor berichten. + No comment provided by engineer. + Better messages Betere berichten No comment provided by engineer. + + Better networking + Beter netwerk + No comment provided by engineer. + + + Better notifications + Betere meldingen + No comment provided by engineer. + + + Better privacy and security + Betere privacy en veiligheid + No comment provided by engineer. + + + Better security ✅ + Betere beveiliging ✅ + No comment provided by engineer. + + + Better user experience + Betere gebruikerservaring + No comment provided by engineer. + + + Black + Zwart + No comment provided by engineer. + Block Blokkeren @@ -993,9 +1270,19 @@ Geblokkeerd door beheerder No comment provided by engineer. + + Blur for better privacy. + Vervagen voor betere privacy. + No comment provided by engineer. + + + Blur media + Vervaag media + No comment provided by engineer. + Both you and your contact can add message reactions. - Zowel u als uw contact kunnen berichtreacties toevoegen. + Zowel u als uw contact kunnen bericht reacties toevoegen. No comment provided by engineer. @@ -1023,9 +1310,33 @@ Bulgaars, Fins, Thais en Oekraïens - dankzij de gebruikers en [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + Zakelijk adres + No comment provided by engineer. + + + Business chats + Zakelijke chats + No comment provided by engineer. + + + Businesses + bedrijven + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). - Via chat profiel (standaard) of [via verbinding](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). + Via chatprofiel (standaard) of [via verbinding](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). + No comment provided by engineer. + + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + Door SimpleX Chat te gebruiken, gaat u ermee akkoord: +- alleen legale content te versturen in openbare groepen. +- andere gebruikers te respecteren – geen spam. No comment provided by engineer. @@ -1038,11 +1349,26 @@ Oproepen No comment provided by engineer. + + Calls prohibited! + Bellen niet toegestaan! + No comment provided by engineer. + Camera not available Camera niet beschikbaar No comment provided by engineer. + + Can't call contact + Kan contact niet bellen + No comment provided by engineer. + + + Can't call member + Kan lid niet bellen + No comment provided by engineer. + Can't invite contact! Kan contact niet uitnodigen! @@ -1053,13 +1379,20 @@ Kan geen contacten uitnodigen! No comment provided by engineer. + + Can't message member + Kan geen bericht sturen naar lid + No comment provided by engineer. + Cancel Annuleren - No comment provided by engineer. + alert action +alert button Cancel migration + Migratie annuleren No comment provided by engineer. @@ -1067,9 +1400,24 @@ Geen toegang tot de keychain om database wachtwoord op te slaan No comment provided by engineer. + + Cannot forward message + Kan bericht niet doorsturen + No comment provided by engineer. + Cannot receive file Kan bestand niet ontvangen + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Capaciteit overschreden - ontvanger heeft eerder verzonden berichten niet ontvangen. + snd error text + + + Cellular + Mobiel No comment provided by engineer. @@ -1077,6 +1425,16 @@ Veranderen No comment provided by engineer. + + Change automatic message deletion? + Automatisch verwijderen van berichten wijzigen? + alert title + + + Change chat profiles + Gebruikersprofielen wijzigen + authentication reason + Change database passphrase? Wachtwoord database wijzigen? @@ -1121,11 +1479,26 @@ Change self-destruct passcode Zelfvernietigings code wijzigen authentication reason - set passcode view +set passcode view - - Chat archive - Gesprek archief + + Chat + Chat + No comment provided by engineer. + + + Chat already exists + Chat bestaat al + No comment provided by engineer. + + + Chat already exists! + Chat bestaat al! + No comment provided by engineer. + + + Chat colors + Chat kleuren No comment provided by engineer. @@ -1143,6 +1516,11 @@ Chat database verwijderd No comment provided by engineer. + + Chat database exported + Chat database geëxporteerd + No comment provided by engineer. + Chat database imported Chat database geïmporteerd @@ -1163,8 +1541,14 @@ Chat is gestopt. Als je deze database al op een ander apparaat hebt gebruikt, moet je deze terugzetten voordat je met chatten begint. No comment provided by engineer. + + Chat list + Chatlijst + No comment provided by engineer. + Chat migrated! + Chat gemigreerd! No comment provided by engineer. @@ -1172,15 +1556,50 @@ Gesprek voorkeuren No comment provided by engineer. + + Chat preferences were changed. + Chatvoorkeuren zijn gewijzigd. + alert message + + + Chat profile + Gebruikers profiel + No comment provided by engineer. + + + Chat theme + Chat thema + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + De chat wordt voor alle leden verwijderd - dit kan niet ongedaan worden gemaakt! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + De chat wordt voor je verwijderd - dit kan niet ongedaan worden gemaakt! + No comment provided by engineer. + Chats - Gesprekken + Chats + No comment provided by engineer. + + + Check messages every 20 min. + Controleer uw berichten elke 20 minuten. + No comment provided by engineer. + + + Check messages when allowed. + Controleer berichten indien toegestaan. No comment provided by engineer. Check server address and try again. Controleer het server adres en probeer het opnieuw. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1189,6 +1608,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + Kies _Migreren vanaf een ander apparaat_ op het nieuwe apparaat en scan de QR-code. No comment provided by engineer. @@ -1201,10 +1621,25 @@ Kies uit bibliotheek No comment provided by engineer. + + Chunks deleted + Stukken verwijderd + No comment provided by engineer. + + + Chunks downloaded + Stukken gedownload + No comment provided by engineer. + + + Chunks uploaded + Stukken geüpload + No comment provided by engineer. + Clear Wissen - No comment provided by engineer. + swipe action Clear conversation @@ -1216,6 +1651,16 @@ Gesprek wissen? No comment provided by engineer. + + Clear group? + Groep wissen? + No comment provided by engineer. + + + Clear or delete group? + Groep wissen of verwijderen? + No comment provided by engineer. + Clear private notes? Privénotities verwijderen? @@ -1226,11 +1671,21 @@ Verwijderd verificatie No comment provided by engineer. - - Colors - Kleuren + + Color chats with the new themes. + Kleurchats met de nieuwe thema's. No comment provided by engineer. + + Color mode + Kleur mode + No comment provided by engineer. + + + Community guidelines violation + Schending van de communityrichtlijnen + report reason + Compare file Bestand vergelijken @@ -1241,11 +1696,56 @@ Vergelijk beveiligingscodes met je contacten. No comment provided by engineer. + + Completed + Voltooid + No comment provided by engineer. + + + Conditions accepted on: %@. + Voorwaarden geaccepteerd op: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + Voorwaarden worden geaccepteerd voor de operator(s): **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + Voorwaarden zijn reeds geaccepteerd voor de volgende operator(s): **%@**. + No comment provided by engineer. + + + Conditions of use + Gebruiksvoorwaarden + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + Voorwaarden worden geaccepteerd voor de operator(s): **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + Voorwaarden worden geaccepteerd op: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + Voorwaarden worden automatisch geaccepteerd voor ingeschakelde operators op: %@. + No comment provided by engineer. + Configure ICE servers ICE servers configureren No comment provided by engineer. + + Configure server operators + Serveroperators configureren + No comment provided by engineer. + Confirm Bevestigen @@ -1256,13 +1756,24 @@ Bevestig toegangscode No comment provided by engineer. + + Confirm contact deletion? + Contact verwijderen bevestigen? + No comment provided by engineer. + Confirm database upgrades Bevestig database upgrades No comment provided by engineer. + + Confirm files from unknown servers. + Bevestig bestanden van onbekende servers. + No comment provided by engineer. + Confirm network settings + Bevestig netwerk instellingen No comment provided by engineer. @@ -1277,12 +1788,19 @@ Confirm that you remember database passphrase to migrate it. + Bevestig dat u het wachtwoord voor de database onthoudt om deze te migreren. No comment provided by engineer. Confirm upload + Bevestig het uploaden No comment provided by engineer. + + Confirmed + Bevestigd + token status text + Connect Verbind @@ -1303,6 +1821,11 @@ Verbinden met desktop No comment provided by engineer. + + Connect to your friends faster. + Maak sneller verbinding met je vrienden. + No comment provided by engineer. + Connect to yourself? Verbinding maken met jezelf? @@ -1342,16 +1865,31 @@ Dit is uw eigen eenmalige link! Verbonden met %@ No comment provided by engineer. + + Connected + Verbonden + No comment provided by engineer. + Connected desktop Verbonden desktop No comment provided by engineer. + + Connected servers + Verbonden servers + No comment provided by engineer. + Connected to desktop Verbonden met desktop No comment provided by engineer. + + Connecting + Verbinden + No comment provided by engineer. + Connecting to server… Verbinden met de server… @@ -1362,6 +1900,11 @@ Dit is uw eigen eenmalige link! Verbinden met server... (fout: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + Er wordt verbinding gemaakt met het contact. Even geduld of controleer het later! + No comment provided by engineer. + Connecting to desktop Verbinding maken met desktop @@ -1372,6 +1915,16 @@ Dit is uw eigen eenmalige link! Verbinding No comment provided by engineer. + + Connection and servers status. + Verbindings- en serverstatus. + No comment provided by engineer. + + + Connection blocked + Verbinding geblokkeerd + No comment provided by engineer. + Connection error Verbindingsfout @@ -1382,11 +1935,38 @@ Dit is uw eigen eenmalige link! Verbindingsfout (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + Verbinding is geblokkeerd door serveroperator: +%@ + No comment provided by engineer. + + + Connection not ready. + Verbinding nog niet klaar. + No comment provided by engineer. + + + Connection notifications + Verbindingsmeldingen + No comment provided by engineer. + Connection request sent! Verbindingsverzoek verzonden! No comment provided by engineer. + + Connection requires encryption renegotiation. + Verbinding vereist heronderhandeling over encryptie. + No comment provided by engineer. + + + Connection security + Beveiliging van de verbinding + No comment provided by engineer. + Connection terminated Verbinding beëindigd @@ -1397,6 +1977,16 @@ Dit is uw eigen eenmalige link! Timeout verbinding No comment provided by engineer. + + Connection with desktop stopped + Verbinding met desktop is gestopt + No comment provided by engineer. + + + Connections + Verbindingen + No comment provided by engineer. + Contact allows Contact maakt het mogelijk @@ -1407,6 +1997,11 @@ Dit is uw eigen eenmalige link! Contact bestaat al No comment provided by engineer. + + Contact deleted! + Contact verwijderd! + No comment provided by engineer. + Contact hidden: Contact verborgen: @@ -1417,9 +2012,9 @@ Dit is uw eigen eenmalige link! Contact is verbonden notification - - Contact is not connected yet! - Contact is nog niet verbonden! + + Contact is deleted. + Contact is verwijderd. No comment provided by engineer. @@ -1432,6 +2027,11 @@ Dit is uw eigen eenmalige link! Contact voorkeuren No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + Het contact wordt verwijderd. Dit kan niet ongedaan worden gemaakt! + No comment provided by engineer. + Contacts Contacten @@ -1442,21 +2042,41 @@ Dit is uw eigen eenmalige link! Contact personen kunnen berichten markeren voor verwijdering; u kunt ze wel bekijken. No comment provided by engineer. + + Content violates conditions of use + Inhoud schendt de gebruiksvoorwaarden + blocking reason + Continue Doorgaan No comment provided by engineer. + + Conversation deleted! + Gesprek verwijderd! + No comment provided by engineer. + Copy Kopiëren - chat item action + No comment provided by engineer. + + + Copy error + Kopieerfout + No comment provided by engineer. Core version: v%@ Core versie: v% @ No comment provided by engineer. + + Corner + Hoek + No comment provided by engineer. + Correct name to %@? Juiste naam voor %@? @@ -1467,6 +2087,11 @@ Dit is uw eigen eenmalige link! Maak No comment provided by engineer. + + Create 1-time link + Eenmalige link maken + No comment provided by engineer. + Create SimpleX address Maak een SimpleX adres aan @@ -1477,11 +2102,6 @@ Dit is uw eigen eenmalige link! Maak een groep met een willekeurig profiel. No comment provided by engineer. - - Create an address to let people connect with you. - Maak een adres aan zodat mensen contact met je kunnen opnemen. - No comment provided by engineer. - Create file Bestand maken @@ -1502,6 +2122,11 @@ Dit is uw eigen eenmalige link! Maak link No comment provided by engineer. + + Create list + Maak een lijst + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 Maak een nieuw profiel aan in [desktop-app](https://simplex.chat/downloads/). 💻 @@ -1527,6 +2152,11 @@ Dit is uw eigen eenmalige link! Maak je profiel aan No comment provided by engineer. + + Created + Gemaakt + No comment provided by engineer. + Created at Gemaakt op @@ -1537,13 +2167,9 @@ Dit is uw eigen eenmalige link! Aangemaakt op: %@ copied message info - - Created on %@ - Gemaakt op %@ - No comment provided by engineer. - Creating archive link + Archief link maken No comment provided by engineer. @@ -1556,11 +2182,21 @@ Dit is uw eigen eenmalige link! Huidige toegangscode No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + De tekst van de huidige voorwaarden kon niet worden geladen. U kunt de voorwaarden bekijken via deze link: + No comment provided by engineer. + Current passphrase… Huidige wachtwoord… No comment provided by engineer. + + Current profile + Huidig profiel + No comment provided by engineer. + Currently maximum supported file size is %@. De momenteel maximaal ondersteunde bestandsgrootte is %@. @@ -1571,11 +2207,26 @@ Dit is uw eigen eenmalige link! Aangepaste tijd No comment provided by engineer. + + Customizable message shape. + Aanpasbare berichtvorm. + No comment provided by engineer. + + + Customize theme + Thema aanpassen + No comment provided by engineer. + Dark Donker No comment provided by engineer. + + Dark mode colors + Kleuren in donkere modus + No comment provided by engineer. + Database ID Database-ID @@ -1647,7 +2298,7 @@ Dit is uw eigen eenmalige link! Database passphrase is required to open chat. - Database wachtwoord is vereist om je gesprekken te openen. + Database wachtwoord is vereist om je chats te openen. No comment provided by engineer. @@ -1674,6 +2325,11 @@ Dit is uw eigen eenmalige link! De database wordt gemigreerd wanneer de app opnieuw wordt opgestart No comment provided by engineer. + + Debug delivery + Foutopsporing bezorging + No comment provided by engineer. + Decentralized Gedecentraliseerd @@ -1687,18 +2343,19 @@ Dit is uw eigen eenmalige link! Delete Verwijderen - chat item action + alert action +swipe action + + + Delete %lld messages of members? + %lld berichten van leden verwijderen? + No comment provided by engineer. Delete %lld messages? %lld berichten verwijderen? No comment provided by engineer. - - Delete Contact - Verwijder contact - No comment provided by engineer. - Delete address Adres verwijderen @@ -1724,24 +2381,29 @@ Dit is uw eigen eenmalige link! Verwijderen en contact op de hoogte stellen No comment provided by engineer. - - Delete archive - Archief verwijderen + + Delete chat + Chat verwijderen No comment provided by engineer. - - Delete chat archive? - Chat archief verwijderen? + + Delete chat messages from your device. + Verwijder chatberichten van uw apparaat. No comment provided by engineer. Delete chat profile - Chat profiel verwijderen + Chatprofiel verwijderen No comment provided by engineer. Delete chat profile? - Chat profiel verwijderen? + Chatprofiel verwijderen? + No comment provided by engineer. + + + Delete chat? + Chat verwijderen? No comment provided by engineer. @@ -1754,11 +2416,9 @@ Dit is uw eigen eenmalige link! Verwijder contact No comment provided by engineer. - - Delete contact? -This cannot be undone! - Verwijder contact? -Dit kan niet ongedaan gemaakt worden! + + Delete contact? + Verwijder contact? No comment provided by engineer. @@ -1768,6 +2428,7 @@ Dit kan niet ongedaan gemaakt worden! Delete database from this device + Verwijder de database van dit apparaat No comment provided by engineer. @@ -1782,7 +2443,7 @@ Dit kan niet ongedaan gemaakt worden! Delete files for all chat profiles - Verwijder bestanden voor alle chat profielen + Verwijder bestanden voor alle chatprofielen No comment provided by engineer. @@ -1820,6 +2481,11 @@ Dit kan niet ongedaan gemaakt worden! Link verwijderen? No comment provided by engineer. + + Delete list? + Lijst verwijderen? + alert title + Delete member message? Bericht van lid verwijderen? @@ -1833,7 +2499,7 @@ Dit kan niet ongedaan gemaakt worden! Delete messages Verwijder berichten - No comment provided by engineer. + alert button Delete messages after @@ -1850,9 +2516,9 @@ Dit kan niet ongedaan gemaakt worden! Oude database verwijderen? No comment provided by engineer. - - Delete pending connection - Wachtende verbinding verwijderen + + Delete or moderate up to 200 messages. + Maximaal 200 berichten verwijderen of modereren. No comment provided by engineer. @@ -1870,11 +2536,31 @@ Dit kan niet ongedaan gemaakt worden! Wachtrij verwijderen server test step + + Delete report + Rapport verwijderen + No comment provided by engineer. + + + Delete up to 20 messages at once. + Verwijder maximaal 20 berichten tegelijk. + No comment provided by engineer. + Delete user profile? Gebruikers profiel verwijderen? No comment provided by engineer. + + Delete without notification + Verwijderen zonder melding + No comment provided by engineer. + + + Deleted + Verwijderd + No comment provided by engineer. + Deleted at Verwijderd om @@ -1885,6 +2571,16 @@ Dit kan niet ongedaan gemaakt worden! Verwijderd om: %@ copied message info + + Deletion errors + Verwijderingsfouten + No comment provided by engineer. + + + Delivered even when Apple drops them. + Geleverd ook als Apple ze verliest + No comment provided by engineer. + Delivery Bezorging @@ -1920,11 +2616,41 @@ Dit kan niet ongedaan gemaakt worden! Desktop apparaten No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + Het bestemmingsserveradres van %@ is niet compatibel met de doorstuurserverinstellingen %@. + No comment provided by engineer. + + + Destination server error: %@ + Bestemmingsserverfout: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + De versie van de bestemmingsserver %@ is niet compatibel met de doorstuurserver %@. + No comment provided by engineer. + + + Detailed statistics + Gedetailleerde statistieken + No comment provided by engineer. + + + Details + Details + No comment provided by engineer. + Develop Ontwikkelen No comment provided by engineer. + + Developer options + Ontwikkelaars opties + No comment provided by engineer. + Developer tools Ontwikkel gereedschap @@ -1955,9 +2681,14 @@ Dit kan niet ongedaan gemaakt worden! Directe berichten chat feature - - Direct messages between members are prohibited in this group. - Directe berichten tussen leden zijn verboden in deze groep. + + Direct messages between members are prohibited in this chat. + Directe berichten tussen leden zijn in deze chat niet toegestaan. + No comment provided by engineer. + + + Direct messages between members are prohibited. + Directe berichten tussen leden zijn niet toegestaan. No comment provided by engineer. @@ -1970,11 +2701,26 @@ Dit kan niet ongedaan gemaakt worden! SimpleX Vergrendelen uitschakelen authentication reason + + Disable automatic message deletion? + Automatisch verwijderen van berichten uitschakelen? + alert title + + + Disable delete messages + Berichten verwijderen uitschakelen + alert button + Disable for all Uitschakelen voor iedereen No comment provided by engineer. + + Disabled + Uitgeschakeld + No comment provided by engineer. + Disappearing message Verdwijnend bericht @@ -1987,12 +2733,12 @@ Dit kan niet ongedaan gemaakt worden! Disappearing messages are prohibited in this chat. - Verdwijnende berichten zijn verboden in dit gesprek. + Verdwijnende berichten zijn niet toegestaan in dit gesprek. No comment provided by engineer. - - Disappearing messages are prohibited in this group. - Verdwijnende berichten zijn verboden in deze groep. + + Disappearing messages are prohibited. + Verdwijnende berichten zijn niet toegestaan. No comment provided by engineer. @@ -2025,11 +2771,21 @@ Dit kan niet ongedaan gemaakt worden! Ontdek via het lokale netwerk No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + Stuur GEEN berichten rechtstreeks, zelfs als uw of de bestemmingsserver geen privéroutering ondersteunt. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. Gebruik SimpleX NIET voor noodoproepen. No comment provided by engineer. + + Do NOT use private routing. + Gebruik GEEN privéroutering. + No comment provided by engineer. + Do it later Doe het later @@ -2040,6 +2796,16 @@ Dit kan niet ongedaan gemaakt worden! Stuur geen geschiedenis naar nieuwe leden. No comment provided by engineer. + + Do not use credentials with proxy. + Gebruik geen inloggegevens met proxy. + No comment provided by engineer. + + + Documents: + Documenten: + No comment provided by engineer. + Don't create address Maak geen adres aan @@ -2050,18 +2816,40 @@ Dit kan niet ongedaan gemaakt worden! Niet inschakelen No comment provided by engineer. + + Don't miss important messages. + Mis geen belangrijke berichten. + No comment provided by engineer. + Don't show again Niet meer weergeven No comment provided by engineer. + + Done + Klaar + No comment provided by engineer. + Downgrade and open chat Downgraden en chat openen No comment provided by engineer. + + Download + Downloaden + alert button +chat item action + + + Download errors + Downloadfouten + No comment provided by engineer. + Download failed + Download mislukt No comment provided by engineer. @@ -2069,12 +2857,29 @@ Dit kan niet ongedaan gemaakt worden! Download bestand server test step + + Download files + ‐Bestanden downloaden + alert action + + + Downloaded + Gedownload + No comment provided by engineer. + + + Downloaded files + Gedownloade bestanden + No comment provided by engineer. + Downloading archive + Archief downloaden No comment provided by engineer. Downloading link details + Link gegevens downloaden No comment provided by engineer. @@ -2087,6 +2892,11 @@ Dit kan niet ongedaan gemaakt worden! Duur No comment provided by engineer. + + E2E encrypted notifications. + E2E versleutelde meldingen. + No comment provided by engineer. + Edit Bewerk @@ -2107,6 +2917,11 @@ Dit kan niet ongedaan gemaakt worden! Inschakelen (overschrijvingen behouden) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + Schakel Flux in bij Netwerk- en serverinstellingen voor betere privacy van metagegevens. + No comment provided by engineer. + Enable SimpleX Lock SimpleX Vergrendelen inschakelen @@ -2120,7 +2935,7 @@ Dit kan niet ongedaan gemaakt worden! Enable automatic message deletion? Automatisch verwijderen van berichten aanzetten? - No comment provided by engineer. + alert title Enable camera access @@ -2134,6 +2949,7 @@ Dit kan niet ongedaan gemaakt worden! Enable in direct chats (BETA)! + Activeer in directe chats (BETA)! No comment provided by engineer. @@ -2166,6 +2982,16 @@ Dit kan niet ongedaan gemaakt worden! Zelfvernietigings wachtwoord inschakelen set passcode view + + Enabled + Ingeschakeld + No comment provided by engineer. + + + Enabled for + Ingeschakeld voor + No comment provided by engineer. + Encrypt Versleutelen @@ -2236,6 +3062,11 @@ Dit kan niet ongedaan gemaakt worden! Opnieuw onderhandelen over de codering is mislukt. No comment provided by engineer. + + Encryption renegotiation in progress. + Er wordt opnieuw onderhandeld over de encryptie. + No comment provided by engineer. + Enter Passcode Voer toegangscode in @@ -2253,6 +3084,7 @@ Dit kan niet ongedaan gemaakt worden! Enter passphrase + Voer het wachtwoord in No comment provided by engineer. @@ -2277,12 +3109,12 @@ Dit kan niet ongedaan gemaakt worden! Enter welcome message… - Welkomst bericht invoeren… + Welkom bericht invoeren… placeholder Enter welcome message… (optional) - Voer welkomst bericht in... (optioneel) + Voer welkom bericht in... (optioneel) placeholder @@ -2300,30 +3132,36 @@ Dit kan niet ongedaan gemaakt worden! Fout bij het afbreken van adres wijziging No comment provided by engineer. + + Error accepting conditions + Fout bij het accepteren van voorwaarden + alert title + Error accepting contact request Fout bij het accepteren van een contactverzoek No comment provided by engineer. - - Error accessing database file - Fout bij toegang tot database bestand - No comment provided by engineer. - Error adding member(s) Fout bij het toevoegen van leden No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + Fout bij toevoegen server + alert title Error changing address Fout bij wijzigen van adres No comment provided by engineer. + + Error changing connection profile + Fout bij wijzigen van verbindingsprofiel + No comment provided by engineer. + Error changing role Fout bij wisselen van rol @@ -2334,6 +3172,21 @@ Dit kan niet ongedaan gemaakt worden! Fout bij wijzigen van instelling No comment provided by engineer. + + Error changing to incognito! + Fout bij het overschakelen naar incognito! + No comment provided by engineer. + + + Error checking token status + Fout bij het controleren van de tokenstatus + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + Fout bij het verbinden met doorstuurserver %@. Probeer het later opnieuw. + No comment provided by engineer. + Error creating address Fout bij aanmaken van adres @@ -2349,6 +3202,11 @@ Dit kan niet ongedaan gemaakt worden! Fout bij maken van groep link No comment provided by engineer. + + Error creating list + Fout bij het aanmaken van de lijst + alert title + Error creating member contact Fout bij aanmaken contact @@ -2364,6 +3222,11 @@ Dit kan niet ongedaan gemaakt worden! Fout bij aanmaken van profiel! No comment provided by engineer. + + Error creating report + Fout bij het rapporteren + No comment provided by engineer. + Error decrypting file Fout bij het ontsleutelen van bestand @@ -2384,11 +3247,6 @@ Dit kan niet ongedaan gemaakt worden! Fout bij verwijderen van verbinding No comment provided by engineer. - - Error deleting contact - Fout bij het verwijderen van contact - No comment provided by engineer. - Error deleting database Fout bij het verwijderen van de database @@ -2411,6 +3269,7 @@ Dit kan niet ongedaan gemaakt worden! Error downloading the archive + Fout bij het downloaden van het archief No comment provided by engineer. @@ -2433,6 +3292,11 @@ Dit kan niet ongedaan gemaakt worden! Fout bij het exporteren van de chat database No comment provided by engineer. + + Error exporting theme: %@ + Fout bij exporteren van thema: %@ + No comment provided by engineer. + Error importing chat database Fout bij het importeren van de chat database @@ -2443,9 +3307,14 @@ Dit kan niet ongedaan gemaakt worden! Fout bij lid worden van groep No comment provided by engineer. - - Error loading %@ servers - Fout bij het laden van %@ servers + + Error loading servers + Fout bij het laden van servers + alert title + + + Error migrating settings + Fout bij migreren van instellingen No comment provided by engineer. @@ -2456,16 +3325,36 @@ Dit kan niet ongedaan gemaakt worden! Error receiving file Fout bij ontvangen van bestand + alert title + + + Error reconnecting server + Fout bij opnieuw verbinding maken met de server No comment provided by engineer. + + Error reconnecting servers + Fout bij opnieuw verbinden van servers + No comment provided by engineer. + + + Error registering for notifications + Fout bij registreren voor meldingen + alert title + Error removing member Fout bij verwijderen van lid No comment provided by engineer. - - Error saving %@ servers - Fout bij opslaan van %@ servers + + Error reordering lists + Fout bij het opnieuw ordenen van lijsten + alert title + + + Error resetting statistics + Fout bij het resetten van statistieken No comment provided by engineer. @@ -2473,6 +3362,11 @@ Dit kan niet ongedaan gemaakt worden! Fout bij opslaan van ICE servers No comment provided by engineer. + + Error saving chat list + Fout bij het opslaan van chatlijst + alert title + Error saving group profile Fout bij opslaan van groep profiel @@ -2488,8 +3382,14 @@ Dit kan niet ongedaan gemaakt worden! Fout bij opslaan van wachtwoord in de keychain No comment provided by engineer. + + Error saving servers + Fout bij het opslaan van servers + alert title + Error saving settings + Fout bij opslaan van instellingen when migrating @@ -2532,16 +3432,26 @@ Dit kan niet ongedaan gemaakt worden! Fout bij het stoppen van de chat No comment provided by engineer. + + Error switching profile + Fout bij wisselen van profiel + No comment provided by engineer. + Error switching profile! Fout bij wisselen van profiel! - No comment provided by engineer. + alertTitle Error synchronizing connection Fout bij het synchroniseren van de verbinding No comment provided by engineer. + + Error testing server connection + Fout bij het testen van de serververbinding + No comment provided by engineer. + Error updating group link Fout bij bijwerken van groep link @@ -2552,6 +3462,11 @@ Dit kan niet ongedaan gemaakt worden! Fout bij updaten van bericht No comment provided by engineer. + + Error updating server + Fout bij het updaten van de server + alert title + Error updating settings Fout bij bijwerken van instellingen @@ -2564,10 +3479,12 @@ Dit kan niet ongedaan gemaakt worden! Error uploading the archive + Fout bij het uploaden van het archief No comment provided by engineer. Error verifying passphrase: + Fout bij het verifiëren van het wachtwoord: No comment provided by engineer. @@ -2578,7 +3495,9 @@ Dit kan niet ongedaan gemaakt worden! Error: %@ Fout: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2590,6 +3509,16 @@ Dit kan niet ongedaan gemaakt worden! Fout: geen database bestand No comment provided by engineer. + + Errors + Fouten + No comment provided by engineer. + + + Errors in servers configuration. + Fouten in de serverconfiguratie. + servers error + Even when disabled in the conversation. Zelfs wanneer uitgeschakeld in het gesprek. @@ -2605,6 +3534,11 @@ Dit kan niet ongedaan gemaakt worden! Uitklappen chat item action + + Expired + Verlopen + token status text + Export database Database exporteren @@ -2615,6 +3549,11 @@ Dit kan niet ongedaan gemaakt worden! Exportfout: No comment provided by engineer. + + Export theme + Exporteer thema + No comment provided by engineer. + Exported database archive. Geëxporteerd database archief. @@ -2622,6 +3561,7 @@ Dit kan niet ongedaan gemaakt worden! Exported file doesn't exist + Geëxporteerd bestand bestaat niet No comment provided by engineer. @@ -2639,16 +3579,70 @@ Dit kan niet ongedaan gemaakt worden! Snel en niet wachten tot de afzender online is! No comment provided by engineer. + + Faster deletion of groups. + Sneller verwijderen van groepen. + No comment provided by engineer. + Faster joining and more reliable messages. Snellere deelname en betrouwbaardere berichten. No comment provided by engineer. + + Faster sending messages. + Sneller verzenden van berichten. + No comment provided by engineer. + Favorite Favoriet + swipe action + + + Favorites + Favorieten No comment provided by engineer. + + File error + Bestandsfout + file error alert title + + + File errors: +%@ + Bestandsfouten: +%@ + alert message + + + File is blocked by server operator: +%@. + Bestand is geblokkeerd door serveroperator: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + Bestand niet gevonden - hoogstwaarschijnlijk is het bestand verwijderd of geannuleerd. + file error text + + + File server error: %@ + Bestandsserverfout: %@ + file error text + + + File status + Bestandsstatus + No comment provided by engineer. + + + File status: %@ + Bestandsstatus: %@ + copied message info + File will be deleted from servers. Het bestand wordt van de servers verwijderd. @@ -2669,6 +3663,11 @@ Dit kan niet ongedaan gemaakt worden! Bestand: %@ No comment provided by engineer. + + Files + Bestanden + No comment provided by engineer. + Files & media Bestanden en media @@ -2679,14 +3678,19 @@ Dit kan niet ongedaan gemaakt worden! Bestanden en media chat feature - - Files and media are prohibited in this group. - Bestanden en media zijn verboden in deze groep. + + Files and media are prohibited. + Bestanden en media zijn niet toegestaan. + No comment provided by engineer. + + + Files and media not allowed + Bestanden en media niet toegestaan No comment provided by engineer. Files and media prohibited! - Bestanden en media verboden! + Bestanden en media niet toegestaan! No comment provided by engineer. @@ -2696,10 +3700,12 @@ Dit kan niet ongedaan gemaakt worden! Finalize migration + Voltooi de migratie No comment provided by engineer. Finalize migration on another device. + Voltooi de migratie op een ander apparaat. No comment provided by engineer. @@ -2709,7 +3715,7 @@ Dit kan niet ongedaan gemaakt worden! Find chats faster - Vind gesprekken sneller + Vind chats sneller No comment provided by engineer. @@ -2742,11 +3748,115 @@ Dit kan niet ongedaan gemaakt worden! Herstel wordt niet ondersteund door groepslid No comment provided by engineer. + + For all moderators + Voor alle moderators + No comment provided by engineer. + + + For chat profile %@: + Voor chatprofiel %@: + servers error + For console Voor console No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + Als uw contactpersoon bijvoorbeeld berichten ontvangt via een SimpleX Chat-server, worden deze door uw app via een Flux-server verzonden. + No comment provided by engineer. + + + For me + Voor mij + No comment provided by engineer. + + + For private routing + Voor privé-routering + No comment provided by engineer. + + + For social media + Voor social media + No comment provided by engineer. + + + Forward + Doorsturen + chat item action + + + Forward %d message(s)? + %d bericht(en) doorsturen? + alert title + + + Forward and save messages + Berichten doorsturen en opslaan + No comment provided by engineer. + + + Forward messages + Berichten doorsturen + alert action + + + Forward messages without files? + Berichten doorsturen zonder bestanden? + alert message + + + Forward up to 20 messages at once. + Stuur maximaal 20 berichten tegelijk door. + No comment provided by engineer. + + + Forwarded + Doorgestuurd + No comment provided by engineer. + + + Forwarded from + Doorgestuurd vanuit + No comment provided by engineer. + + + Forwarding %lld messages + %lld berichten doorsturen + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + De doorstuurserver %@ kon geen verbinding maken met de bestemmingsserver %@. Probeer het later opnieuw. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + Het adres van de doorstuurserver is niet compatibel met de netwerkinstellingen: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + De doorstuurserverversie is niet compatibel met de netwerkinstellingen: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + Doorstuurserver: %1$@ +Bestemmingsserverfout: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + Doorstuurserver: %1$@ +Fout: %2$@ + snd error text + Found desktop Desktop gevonden @@ -2767,11 +3877,6 @@ Dit kan niet ongedaan gemaakt worden! Volledige naam (optioneel) No comment provided by engineer. - - Full name: - Volledige naam: - No comment provided by engineer. - Fully decentralized – visible only to members. Volledig gedecentraliseerd – alleen zichtbaar voor leden. @@ -2792,6 +3897,21 @@ Dit kan niet ongedaan gemaakt worden! GIF's en stickers No comment provided by engineer. + + Get notified when mentioned. + Ontvang een melding als u vermeld wordt. + No comment provided by engineer. + + + Good afternoon! + Goedemiddag! + message preview + + + Good morning! + Goedemorgen! + message preview + Group Groep @@ -2847,36 +3967,6 @@ Dit kan niet ongedaan gemaakt worden! Groep links No comment provided by engineer. - - Group members can add message reactions. - Groepsleden kunnen berichtreacties toevoegen. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Groepsleden kunnen verzonden berichten onherroepelijk verwijderen. (24 uur) - No comment provided by engineer. - - - Group members can send direct messages. - Groepsleden kunnen directe berichten sturen. - No comment provided by engineer. - - - Group members can send disappearing messages. - Groepsleden kunnen verdwijnende berichten sturen. - No comment provided by engineer. - - - Group members can send files and media. - Groepsleden kunnen bestanden en media verzenden. - No comment provided by engineer. - - - Group members can send voice messages. - Groepsleden kunnen spraak berichten verzenden. - No comment provided by engineer. - Group message: Groep bericht: @@ -2904,7 +3994,7 @@ Dit kan niet ongedaan gemaakt worden! Group welcome message - Groep welkomst bericht + Groep welkom bericht No comment provided by engineer. @@ -2917,11 +4007,21 @@ Dit kan niet ongedaan gemaakt worden! De groep wordt voor u verwijderd, dit kan niet ongedaan worden gemaakt! No comment provided by engineer. + + Groups + Groepen + No comment provided by engineer. + Help Help No comment provided by engineer. + + Help admins moderating their groups. + Help beheerders bij het modereren van hun groepen. + No comment provided by engineer. + Hidden Verborgen @@ -2929,7 +4029,7 @@ Dit kan niet ongedaan gemaakt worden! Hidden chat profiles - Verborgen chat profielen + Verborgen chatprofielen No comment provided by engineer. @@ -2972,10 +4072,20 @@ Dit kan niet ongedaan gemaakt worden! Hoe SimpleX werkt No comment provided by engineer. + + How it affects privacy + Hoe het de privacy beïnvloedt + No comment provided by engineer. + + + How it helps privacy + Hoe het de privacy helpt + No comment provided by engineer. + How it works Hoe het werkt - No comment provided by engineer. + alert button How to @@ -2994,6 +4104,7 @@ Dit kan niet ongedaan gemaakt worden! Hungarian interface + Hongaarse interface No comment provided by engineer. @@ -3001,6 +4112,11 @@ Dit kan niet ongedaan gemaakt worden! ICE servers (één per lijn) No comment provided by engineer. + + IP address + IP-adres + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Als je elkaar niet persoonlijk kunt ontmoeten, laat dan de QR-code zien in een videogesprek of deel de link. @@ -3008,7 +4124,7 @@ Dit kan niet ongedaan gemaakt worden! If you enter this passcode when opening the app, all app data will be irreversibly removed! - Als u deze toegangscode invoert bij het openen van de app, worden alle app-gegevens onomkeerbaar verwijderd! + Als u deze toegangscode invoert bij het openen van de app, worden alle app-gegevens definitief verwijderd! No comment provided by engineer. @@ -3041,8 +4157,8 @@ Dit kan niet ongedaan gemaakt worden! Onmiddellijk No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Immuun voor spam en misbruik No comment provided by engineer. @@ -3063,10 +4179,24 @@ Dit kan niet ongedaan gemaakt worden! Import failed + Importeren is mislukt + No comment provided by engineer. + + + Import theme + Thema importeren No comment provided by engineer. Importing archive + Archief importeren + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Verbeterde levering, minder data gebruik. +Binnenkort meer verbeteringen! No comment provided by engineer. @@ -3086,6 +4216,7 @@ Dit kan niet ongedaan gemaakt worden! In order to continue, chat should be stopped. + Om verder te kunnen gaan, moet de chat worden gestopt. No comment provided by engineer. @@ -3093,6 +4224,21 @@ Dit kan niet ongedaan gemaakt worden! In antwoord op No comment provided by engineer. + + In-call sounds + Geluiden tijdens het bellen + No comment provided by engineer. + + + Inappropriate content + Ongepaste inhoud + report reason + + + Inappropriate profile + Ongepast profiel + report reason + Incognito Incognito @@ -3163,6 +4309,11 @@ Dit kan niet ongedaan gemaakt worden! Installeer [SimpleX Chat voor terminal](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Direct + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3170,16 +4321,41 @@ Dit kan niet ongedaan gemaakt worden! No comment provided by engineer. - - Instantly - Direct - No comment provided by engineer. - Interface Interface No comment provided by engineer. + + Interface colors + Interface kleuren + No comment provided by engineer. + + + Invalid + Ongeldig + token status text + + + Invalid (bad token) + Ongeldig (ongeldig token) + token status text + + + Invalid (expired) + Ongeldig (verlopen) + token status text + + + Invalid (unregistered) + Ongeldig (niet geregistreerd) + token status text + + + Invalid (wrong topic) + Ongeldig (verkeerd onderwerp) + token status text + Invalid QR code Ongeldige QR-code @@ -3202,6 +4378,7 @@ Dit kan niet ongedaan gemaakt worden! Invalid migration confirmation + Ongeldige migratie bevestiging No comment provided by engineer. @@ -3217,7 +4394,7 @@ Dit kan niet ongedaan gemaakt worden! Invalid server address! Ongeldig server adres! - No comment provided by engineer. + alert title Invalid status @@ -3239,6 +4416,11 @@ Dit kan niet ongedaan gemaakt worden! Nodig leden uit No comment provided by engineer. + + Invite to chat + Uitnodigen voor een chat + No comment provided by engineer. + Invite to group Uitnodigen voor groep @@ -3251,17 +4433,17 @@ Dit kan niet ongedaan gemaakt worden! Irreversible message deletion is prohibited in this chat. - Het onomkeerbaar verwijderen van berichten is verboden in dit gesprek. + Het definitief verwijderen van berichten is niet toegestaan in dit gesprek. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. - Het onomkeerbaar verwijderen van berichten is verboden in deze groep. + + Irreversible message deletion is prohibited. + Het definitief verwijderen van berichten is verbHet definitief verwijderen van berichten is niet toegestaan.. No comment provided by engineer. It allows having many anonymous connections without any shared data between them in a single chat profile. - Het maakt het mogelijk om veel anonieme verbindingen te hebben zonder enige gedeelde gegevens tussen hen in een enkel chat profiel. + Het maakt het mogelijk om veel anonieme verbindingen te hebben zonder enige gedeelde gegevens tussen hen in een enkel chatprofiel. No comment provided by engineer. @@ -3280,6 +4462,11 @@ Dit kan niet ongedaan gemaakt worden! 3. De verbinding is verbroken. No comment provided by engineer. + + It protects your IP address and connections. + Het beschermt uw IP-adres en verbindingen. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Het lijkt erop dat u al bent verbonden via deze link. Als dit niet het geval is, is er een fout opgetreden (%@). @@ -3297,8 +4484,8 @@ Dit kan niet ongedaan gemaakt worden! Join - Word lid van - No comment provided by engineer. + Word lid + swipe action Join group @@ -3340,6 +4527,11 @@ Dit is jouw link voor groep %@! Keep Bewaar + alert action + + + Keep conversation + Behoud het gesprek No comment provided by engineer. @@ -3350,7 +4542,7 @@ Dit is jouw link voor groep %@! Keep unused invitation? Ongebruikte uitnodiging bewaren? - No comment provided by engineer. + alert title Keep your connections @@ -3385,6 +4577,16 @@ Dit is jouw link voor groep %@! Leave Verlaten + swipe action + + + Leave chat + Chat verlaten + No comment provided by engineer. + + + Leave chat? + Chat verlaten? No comment provided by engineer. @@ -3427,6 +4629,21 @@ Dit is jouw link voor groep %@! Gelinkte desktops No comment provided by engineer. + + List + Lijst + swipe action + + + List name and emoji should be different for all lists. + De naam en emoji van de lijst moeten voor alle lijsten verschillend zijn. + No comment provided by engineer. + + + List name... + Naam van lijst... + No comment provided by engineer. + Live message! Live bericht! @@ -3437,11 +4654,6 @@ Dit is jouw link voor groep %@! Live berichten No comment provided by engineer. - - Local - Lokaal - No comment provided by engineer. - Local name Lokale naam @@ -3462,11 +4674,6 @@ Dit is jouw link voor groep %@! Vergrendeling modus No comment provided by engineer. - - Make a private connection - Maak een privéverbinding - No comment provided by engineer. - Make one message disappear Eén bericht laten verdwijnen @@ -3477,21 +4684,11 @@ Dit is jouw link voor groep %@! Profiel privé maken! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Zorg ervoor dat %@ server adressen de juiste indeling hebben, regel gescheiden zijn en niet gedupliceerd zijn (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Zorg ervoor dat WebRTC ICE server adressen de juiste indeling hebben, regel gescheiden zijn en niet gedupliceerd zijn. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Veel mensen vroegen: *als SimpleX geen gebruikers-ID's heeft, hoe kan het dan berichten bezorgen?* - No comment provided by engineer. - Mark deleted for everyone Markeer verwijderd voor iedereen @@ -3517,11 +4714,36 @@ Dit is jouw link voor groep %@! Max 30 seconden, direct ontvangen. No comment provided by engineer. + + Media & file servers + Media- en bestandsservers + No comment provided by engineer. + + + Medium + Medium + blur media + Member Lid No comment provided by engineer. + + Member inactive + Lid inactief + item status text + + + Member reports + Ledenrapporten + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + De rol van het lid wordt gewijzigd naar "%@". Alle chatleden worden op de hoogte gebracht. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. De rol van lid wordt gewijzigd in "%@". Alle groepsleden worden op de hoogte gebracht. @@ -3532,11 +4754,66 @@ Dit is jouw link voor groep %@! De rol van lid wordt gewijzigd in "%@". Het lid ontvangt een nieuwe uitnodiging. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + Lid wordt verwijderd uit de chat - dit kan niet ongedaan worden gemaakt! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! Lid wordt uit de groep verwijderd, dit kan niet ongedaan worden gemaakt! No comment provided by engineer. + + Members can add message reactions. + Groepsleden kunnen bericht reacties toevoegen. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + Groepsleden kunnen verzonden berichten onherroepelijk verwijderen. (24 uur) + No comment provided by engineer. + + + Members can report messsages to moderators. + Leden kunnen berichten melden bij moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + Groepsleden kunnen SimpleX-links verzenden. + No comment provided by engineer. + + + Members can send direct messages. + Groepsleden kunnen directe berichten sturen. + No comment provided by engineer. + + + Members can send disappearing messages. + Groepsleden kunnen verdwijnende berichten sturen. + No comment provided by engineer. + + + Members can send files and media. + Groepsleden kunnen bestanden en media verzenden. + No comment provided by engineer. + + + Members can send voice messages. + Groepsleden kunnen spraak berichten verzenden. + No comment provided by engineer. + + + Mention members 👋 + Vermeld leden 👋 + No comment provided by engineer. + + + Menus + Menu's + No comment provided by engineer. + Message delivery error Fout bij bezorging van bericht @@ -3547,11 +4824,31 @@ Dit is jouw link voor groep %@! Ontvangst bevestiging voor berichten! No comment provided by engineer. + + Message delivery warning + Waarschuwing voor berichtbezorging + item status text + Message draft Concept bericht No comment provided by engineer. + + Message forwarded + Bericht doorgestuurd + item status text + + + Message may be delivered later if member becomes active. + Het bericht kan later worden bezorgd als het lid actief wordt. + item status description + + + Message queue info + Informatie over berichtenwachtrij + No comment provided by engineer. + Message reactions Reacties op berichten @@ -3559,14 +4856,44 @@ Dit is jouw link voor groep %@! Message reactions are prohibited in this chat. - Reacties op berichten zijn verboden in deze chat. + Reacties op berichten zijn niet toegestaan in deze chat. No comment provided by engineer. - - Message reactions are prohibited in this group. - Reacties op berichten zijn verboden in deze groep. + + Message reactions are prohibited. + Reacties op berichten zijn niet toegestaan. No comment provided by engineer. + + Message reception + Bericht ontvangst + No comment provided by engineer. + + + Message servers + Berichtservers + No comment provided by engineer. + + + Message shape + Berichtvorm + No comment provided by engineer. + + + Message source remains private. + Berichtbron blijft privé. + No comment provided by engineer. + + + Message status + Berichtstatus + No comment provided by engineer. + + + Message status: %@ + Berichtstatus: %@ + copied message info + Message text Bericht tekst @@ -3574,6 +4901,7 @@ Dit is jouw link voor groep %@! Message too large + Bericht te groot No comment provided by engineer. @@ -3591,36 +4919,64 @@ Dit is jouw link voor groep %@! Berichten van %@ worden getoond! No comment provided by engineer. + + Messages in this chat will never be deleted. + Berichten in deze chat zullen nooit worden verwijderd. + alert message + + + Messages received + Berichten ontvangen + No comment provided by engineer. + + + Messages sent + Berichten verzonden + No comment provided by engineer. + + + Messages were deleted after you selected them. + Berichten zijn verwijderd nadat u ze had geselecteerd. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + Berichten, bestanden en oproepen worden beschermd door **end-to-end codering** met perfecte voorwaartse geheimhouding, afwijzing en inbraakherstel. No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + Berichten, bestanden en oproepen worden beschermd door **kwantumbestendige e2e encryptie** met perfecte voorwaartse geheimhouding, afwijzing en inbraakherstel. No comment provided by engineer. Migrate device + Apparaat migreren No comment provided by engineer. Migrate from another device + Migreer vanaf een ander apparaat No comment provided by engineer. Migrate here + Migreer hierheen No comment provided by engineer. Migrate to another device + Migreer naar een ander apparaat No comment provided by engineer. Migrate to another device via QR code. + Migreer naar een ander apparaat via QR-code. No comment provided by engineer. Migrating + Migreren No comment provided by engineer. @@ -3630,6 +4986,7 @@ Dit is jouw link voor groep %@! Migration complete + Migratie voltooid No comment provided by engineer. @@ -3639,7 +4996,7 @@ Dit is jouw link voor groep %@! Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat). - Migratie mislukt. Tik hieronder op **Overslaan** om de huidige database te blijven gebruiken. Meld het probleem aan de app-ontwikkelaars via chat of e-mail [chat@simplex.chat](mailto:chat@simplex.chat). + Migratie mislukt. Tik hier onder op **Overslaan** om de huidige database te blijven gebruiken. Meld het probleem aan de app-ontwikkelaars via chat of e-mail [chat@simplex.chat](mailto:chat@simplex.chat). No comment provided by engineer. @@ -3647,9 +5004,9 @@ Dit is jouw link voor groep %@! Migratie is voltooid No comment provided by engineer. - - Migrations: %@ - Migraties: %@ + + Migrations: + Migraties: No comment provided by engineer. @@ -3667,30 +5024,45 @@ Dit is jouw link voor groep %@! Gemodereerd op: %@ copied message info + + More + Meer + swipe action + More improvements are coming soon! Meer verbeteringen volgen snel! No comment provided by engineer. + + More reliable network connection. + Betrouwbaardere netwerkverbinding. + No comment provided by engineer. + + + More reliable notifications + Betrouwbaardere meldingen + No comment provided by engineer. + Most likely this connection is deleted. Hoogstwaarschijnlijk is deze verbinding verwijderd. item status description - - Most likely this contact has deleted the connection with you. - Hoogstwaarschijnlijk heeft dit contact de verbinding met jou verwijderd. - No comment provided by engineer. - Multiple chat profiles - Meerdere chat profielen + Meerdere chatprofielen No comment provided by engineer. Mute Dempen - No comment provided by engineer. + notification label action + + + Mute all + Alles dempen + notification label action Muted when inactive! @@ -3700,13 +5072,38 @@ Dit is jouw link voor groep %@! Name Naam - No comment provided by engineer. + swipe action Network & servers Netwerk & servers No comment provided by engineer. + + Network connection + Netwerkverbinding + No comment provided by engineer. + + + Network decentralization + Netwerk decentralisatie + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + Netwerkproblemen - bericht is verlopen na vele pogingen om het te verzenden. + snd error text + + + Network management + Netwerkbeheer + No comment provided by engineer. + + + Network operator + Netwerkbeheerder + No comment provided by engineer. + Network settings Netwerk instellingen @@ -3717,16 +5114,36 @@ Dit is jouw link voor groep %@! Netwerk status No comment provided by engineer. + + New + Nieuw + token status text + New Passcode Nieuwe toegangscode No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Elke keer dat u de app start, worden er nieuwe SOCKS-inloggegevens gebruikt. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Voor elke server worden nieuwe SOCKS-inloggegevens gebruikt. + No comment provided by engineer. + New chat Nieuw gesprek No comment provided by engineer. + + New chat experience 🎉 + Nieuwe chatervaring 🎉 + No comment provided by engineer. + New contact request Nieuw contactverzoek @@ -3737,11 +5154,6 @@ Dit is jouw link voor groep %@! Nieuw contact: notification - - New database archive - Nieuw database archief - No comment provided by engineer. - New desktop app! Nieuwe desktop app! @@ -3752,11 +5164,21 @@ Dit is jouw link voor groep %@! Nieuwe weergavenaam No comment provided by engineer. + + New events + Nieuwe gebeurtenissen + notification + New in %@ Nieuw in %@ No comment provided by engineer. + + New media options + Nieuwe media-opties + No comment provided by engineer. + New member role Nieuwe leden rol @@ -3772,6 +5194,11 @@ Dit is jouw link voor groep %@! Nieuw wachtwoord… No comment provided by engineer. + + New server + Nieuwe server + No comment provided by engineer. + No Nee @@ -3782,6 +5209,21 @@ Dit is jouw link voor groep %@! Geen app wachtwoord Authentication unavailable + + No chats + Geen chats + No comment provided by engineer. + + + No chats found + Geen chats gevonden + No comment provided by engineer. + + + No chats in list %@ + Geen chats in lijst %@ + No comment provided by engineer. + No contacts selected Geen contacten geselecteerd @@ -3802,9 +5244,14 @@ Dit is jouw link voor groep %@! Geen apparaattoken! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + Nog geen directe verbinding, bericht wordt doorgestuurd door beheerder. + item status description + No filtered chats - Geen gefilterde gesprekken + Geen gefilterde chats No comment provided by engineer. @@ -3817,21 +5264,111 @@ Dit is jouw link voor groep %@! Geen geschiedenis No comment provided by engineer. + + No info, try to reload + Geen info, probeer opnieuw te laden + No comment provided by engineer. + + + No media & file servers. + Geen media- en bestandsservers. + servers error + + + No message + Geen bericht + No comment provided by engineer. + + + No message servers. + Geen berichtenservers. + servers error + + + No network connection + Geen netwerkverbinding + No comment provided by engineer. + + + No permission to record speech + Geen toestemming om spraak op te nemen + No comment provided by engineer. + + + No permission to record video + Geen toestemming om video op te nemen + No comment provided by engineer. + No permission to record voice message Geen toestemming om spraakbericht op te nemen No comment provided by engineer. + + No push server + Lokaal + No comment provided by engineer. + No received or sent files Geen ontvangen of verzonden bestanden No comment provided by engineer. + + No servers for private message routing. + Geen servers voor het routeren van privéberichten. + servers error + + + No servers to receive files. + Geen servers om bestanden te ontvangen. + servers error + + + No servers to receive messages. + Geen servers om berichten te ontvangen. + servers error + + + No servers to send files. + Geen servers om bestanden te verzenden. + servers error + + + No token! + Geen token! + alert title + + + No unread chats + Geen ongelezen chats + No comment provided by engineer. + + + No user identifiers. + Geen gebruikers-ID's. + No comment provided by engineer. + Not compatible! Niet compatibel! No comment provided by engineer. + + Notes + Notities + No comment provided by engineer. + + + Nothing selected + Niets geselecteerd + No comment provided by engineer. + + + Nothing to forward! + Niets om door te sturen! + alert title + Notifications Meldingen @@ -3842,6 +5379,21 @@ Dit is jouw link voor groep %@! Meldingen zijn uitgeschakeld! No comment provided by engineer. + + Notifications error + Meldingsfout + alert title + + + Notifications privacy + Privacy van meldingen + No comment provided by engineer. + + + Notifications status + Meldingsstatus + alert title + Now admins can: - delete members' messages. @@ -3859,36 +5411,35 @@ Dit is jouw link voor groep %@! Off Uit - No comment provided by engineer. + blur media Ok OK - No comment provided by engineer. + alert button Old database Oude database No comment provided by engineer. - - Old database archive - Oud database archief - No comment provided by engineer. - One-time invitation link Eenmalige uitnodiging link No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Onion hosts zullen nodig zijn voor verbinding. Vereist het inschakelen van VPN. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Onion hosts zullen nodig zijn voor verbinding. +Vereist het inschakelen van VPN. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Onion hosts worden gebruikt indien beschikbaar. Vereist het inschakelen van VPN. + + Onion hosts will be used when available. +Requires compatible VPN. + Onion hosts worden gebruikt indien beschikbaar. +Vereist het inschakelen van VPN. No comment provided by engineer. @@ -3896,11 +5447,21 @@ Dit is jouw link voor groep %@! Onion hosts worden niet gebruikt. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + Alleen chateigenaren kunnen voorkeuren wijzigen. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. Alleen client apparaten slaan gebruikers profielen, contacten, groepen en berichten op die zijn verzonden met **2-laags end-to-end-codering**. No comment provided by engineer. + + Only delete conversation + Alleen conversatie verwijderen + No comment provided by engineer. + Only group owners can change group preferences. Alleen groep eigenaren kunnen groep voorkeuren wijzigen. @@ -3916,14 +5477,24 @@ Dit is jouw link voor groep %@! Alleen groep eigenaren kunnen spraak berichten inschakelen. No comment provided by engineer. + + Only sender and moderators see it + Alleen de verzender en moderators zien het + No comment provided by engineer. + + + Only you and moderators see it + Alleen jij en moderators zien het + No comment provided by engineer. + Only you can add message reactions. - Alleen jij kunt berichtreacties toevoegen. + Alleen jij kunt bericht reacties toevoegen. No comment provided by engineer. Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours) - Alleen jij kunt berichten onomkeerbaar verwijderen (je contact kan ze markeren voor verwijdering). (24 uur) + Alleen jij kunt berichten definitief verwijderen (je contact kan ze markeren voor verwijdering). (24 uur) No comment provided by engineer. @@ -3943,7 +5514,7 @@ Dit is jouw link voor groep %@! Only your contact can add message reactions. - Alleen uw contact kan berichtreacties toevoegen. + Alleen uw contact kan bericht reacties toevoegen. No comment provided by engineer. @@ -3969,16 +5540,21 @@ Dit is jouw link voor groep %@! Open Open - No comment provided by engineer. + alert action Open Settings Open instellingen No comment provided by engineer. + + Open changes + Wijzigingen openen + No comment provided by engineer. + Open chat - Gesprekken openen + Chat openen No comment provided by engineer. @@ -3986,32 +5562,48 @@ Dit is jouw link voor groep %@! Chat console openen authentication reason + + Open conditions + Open voorwaarden + No comment provided by engineer. + Open group Open groep No comment provided by engineer. + + Open link? + alert title + Open migration to another device + Open de migratie naar een ander apparaat authentication reason - - Open user profiles - Gebruikers profielen openen - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Open-source protocol en code. Iedereen kan de servers draaien. - No comment provided by engineer. - Opening app… App openen… No comment provided by engineer. + + Operator + Operator + No comment provided by engineer. + + + Operator server + Operatorserver + alert title + + + Or import archive file + Of importeer archiefbestand + No comment provided by engineer. + Or paste archive link + Of plak de archief link No comment provided by engineer. @@ -4021,6 +5613,7 @@ Dit is jouw link voor groep %@! Or securely share this file link + Of deel deze bestands link veilig No comment provided by engineer. @@ -4028,6 +5621,28 @@ Dit is jouw link voor groep %@! Of laat deze code zien No comment provided by engineer. + + Or to share privately + Of om privé te delen + No comment provided by engineer. + + + Organize chats into lists + Organiseer chats in lijsten + No comment provided by engineer. + + + Other + Ander + No comment provided by engineer. + + + Other file errors: +%@ + Andere bestandsfouten: +%@ + alert message + PING count PING count @@ -4063,6 +5678,11 @@ Dit is jouw link voor groep %@! Toegangscode ingesteld! No comment provided by engineer. + + Password + Wachtwoord + No comment provided by engineer. + Password to show Wachtwoord om weer te geven @@ -4093,13 +5713,13 @@ Dit is jouw link voor groep %@! Plak de link die je hebt ontvangen No comment provided by engineer. - - People can connect to you only via the links you share. - Mensen kunnen alleen verbinding met u maken via de links die u deelt. + + Pending + in behandeling No comment provided by engineer. - - Periodically + + Periodic Periodiek No comment provided by engineer. @@ -4110,6 +5730,17 @@ Dit is jouw link voor groep %@! Picture-in-picture calls + Beeld-in-beeld oproepen + No comment provided by engineer. + + + Play from the chat list. + Afspelen via de chat lijst. + No comment provided by engineer. + + + Please ask your contact to enable calls. + Vraag uw contactpersoon om oproepen in te schakelen. No comment provided by engineer. @@ -4117,6 +5748,13 @@ Dit is jouw link voor groep %@! Vraag uw contact om het verzenden van spraak berichten in te schakelen. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + Controleer of mobiel en desktop met hetzelfde lokale netwerk zijn verbonden en of de desktopfirewall de verbinding toestaat. +Deel eventuele andere problemen met de ontwikkelaars. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Controleer of u de juiste link heeft gebruikt of vraag uw contact om u een andere te sturen. @@ -4134,6 +5772,7 @@ Dit is jouw link voor groep %@! Please confirm that network settings are correct for this device. + Controleer of de netwerk instellingen correct zijn voor dit apparaat. No comment provided by engineer. @@ -4175,7 +5814,7 @@ Fout: %@ Please store passphrase securely, you will NOT be able to access chat if you lose it. - Sla het wachtwoord veilig op. Als u deze kwijtraakt, heeft u GEEN toegang tot de gesprekken. + Sla het wachtwoord veilig op. Als u deze kwijtraakt, heeft u GEEN toegang tot de chats. No comment provided by engineer. @@ -4183,60 +5822,121 @@ Fout: %@ Bewaar het wachtwoord veilig, u kunt deze NIET wijzigen als u het kwijtraakt. No comment provided by engineer. + + Please try to disable and re-enable notfications. + Probeer meldingen uit en weer in te schakelen. + token info + + + Please wait for token activation to complete. + Wacht tot de tokenactivering voltooid is. + token info + + + Please wait for token to be registered. + Wacht tot het token is geregistreerd. + token info + Polish interface Poolse interface No comment provided by engineer. + + Port + Poort + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Mogelijk is de certificaat vingerafdruk in het server adres onjuist server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Bewaar het laatste berichtconcept, met bijlagen. No comment provided by engineer. - - Preset server - Vooraf ingestelde server - No comment provided by engineer. - Preset server address Vooraf ingesteld server adres No comment provided by engineer. + + Preset servers + Vooraf ingestelde servers + No comment provided by engineer. + Preview Voorbeeld No comment provided by engineer. + + Previously connected servers + Eerder verbonden servers + No comment provided by engineer. + Privacy & security Privacy en beveiliging No comment provided by engineer. + + Privacy for your customers. + Privacy voor uw klanten. + No comment provided by engineer. + + + Privacy policy and conditions of use. + Privacybeleid en gebruiksvoorwaarden. + No comment provided by engineer. + Privacy redefined Privacy opnieuw gedefinieerd No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + Privéchats, groepen en uw contacten zijn niet toegankelijk voor serverbeheerders. + No comment provided by engineer. + Private filenames Privé bestandsnamen No comment provided by engineer. + + Private media file names. + Namen van persoonlijke mediabestanden. + No comment provided by engineer. + + + Private message routing + Routering van privéberichten + No comment provided by engineer. + + + Private message routing 🚀 + Routing van privéberichten🚀 + No comment provided by engineer. + Private notes Privé notities name of notes to self + + Private routing + Privéroutering + No comment provided by engineer. + + + Private routing error + Fout in privéroutering + No comment provided by engineer. + Profile and server connections Profiel- en serververbindingen @@ -4247,14 +5947,9 @@ Fout: %@ profielfoto No comment provided by engineer. - - Profile name - Profielnaam - No comment provided by engineer. - - - Profile name: - Profielnaam: + + Profile images + Profiel afbeeldingen No comment provided by engineer. @@ -4262,10 +5957,15 @@ Fout: %@ Profiel wachtwoord No comment provided by engineer. + + Profile theme + Profiel thema + No comment provided by engineer. + Profile update will be sent to your contacts. Profiel update wordt naar uw contacten verzonden. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4274,12 +5974,12 @@ Fout: %@ Prohibit irreversible message deletion. - Verbied het onomkeerbaar verwijderen van berichten. + Verbied het definitief verwijderen van berichten. No comment provided by engineer. Prohibit message reactions. - Berichtreacties verbieden. + Bericht reacties verbieden. No comment provided by engineer. @@ -4287,6 +5987,16 @@ Fout: %@ Berichten reacties verbieden. No comment provided by engineer. + + Prohibit reporting messages to moderators. + Het melden van berichten aan moderators is niet toegestaan. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + Verbied het verzenden van SimpleX-links + No comment provided by engineer. + Prohibit sending direct messages to members. Verbied het sturen van directe berichten naar leden. @@ -4307,14 +6017,26 @@ Fout: %@ Verbieden het verzenden van spraak berichten. No comment provided by engineer. + + Protect IP address + Bescherm het IP-adres + No comment provided by engineer. + Protect app screen App scherm verbergen No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + Bescherm uw IP-adres tegen de berichtenrelais die door uw contacten zijn gekozen. +Schakel dit in in *Netwerk en servers*-instellingen. + No comment provided by engineer. + Protect your chat profiles with a password! - Bescherm je chat profielen met een wachtwoord! + Bescherm je chatprofielen met een wachtwoord! No comment provided by engineer. @@ -4327,6 +6049,21 @@ Fout: %@ Protocol timeout per KB No comment provided by engineer. + + Proxied + Proxied + No comment provided by engineer. + + + Proxied servers + Proxied servers + No comment provided by engineer. + + + Proxy requires password + Proxy vereist wachtwoord + No comment provided by engineer. + Push notifications Push meldingen @@ -4334,10 +6071,12 @@ Fout: %@ Push server + Push server No comment provided by engineer. Quantum resistant encryption + quantum bestendige encryptie No comment provided by engineer. @@ -4345,6 +6084,11 @@ Fout: %@ Beoordeel de app No comment provided by engineer. + + Reachable chat toolbar + Toegankelijke chatwerkbalk + No comment provided by engineer. + React… Reageer… @@ -4353,33 +6097,28 @@ Fout: %@ Read Lees - No comment provided by engineer. + swipe action Read more Lees meer No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Lees meer in de [Gebruikershandleiding](https://simplex.chat/docs/guide/app-settings.html#uw-simplex-contactadres). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). Lees meer in de [Gebruikershandleiding](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Lees meer in de [Gebruikershandleiding](https://simplex.chat/docs/guide/app-settings.html#uw-simplex-contactadres). + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). Lees meer in de [Gebruikershandleiding](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - - Read more in our GitHub repository. - Lees meer in onze GitHub repository. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). Lees meer in onze [GitHub-repository](https://github.com/simplex-chat/simplex-chat#readme). @@ -4390,6 +6129,11 @@ Fout: %@ Bevestigingen zijn uitgeschakeld No comment provided by engineer. + + Receive errors + Fouten ontvangen + No comment provided by engineer. + Received at Ontvangen op @@ -4410,6 +6154,21 @@ Fout: %@ Ontvangen bericht message info title + + Received messages + Ontvangen berichten + No comment provided by engineer. + + + Received reply + Antwoord ontvangen + No comment provided by engineer. + + + Received total + Totaal ontvangen + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. Het ontvangstadres wordt gewijzigd naar een andere server. Adres wijziging wordt voltooid nadat de afzender online is. @@ -4430,16 +6189,46 @@ Fout: %@ Recente geschiedenis en verbeterde [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + Ontvanger(s) kunnen niet zien van wie dit bericht afkomstig is. + No comment provided by engineer. + Recipients see updates as you type them. Ontvangers zien updates terwijl u ze typt. No comment provided by engineer. + + Reconnect + opnieuw verbinden + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Verbind alle verbonden servers opnieuw om de bezorging van berichten af te dwingen. Het maakt gebruik van extra data. No comment provided by engineer. + + Reconnect all servers + Maak opnieuw verbinding met alle servers + No comment provided by engineer. + + + Reconnect all servers? + Alle servers opnieuw verbinden? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + Maak opnieuw verbinding met de server om de bezorging van berichten te forceren. Er wordt gebruik gemaakt van extra verkeer.Maak opnieuw verbinding met de server om de bezorging van berichten te forceren. Er wordt gebruik gemaakt van extra data. + No comment provided by engineer. + + + Reconnect server? + Server opnieuw verbinden? + No comment provided by engineer. + Reconnect servers? Servers opnieuw verbinden? @@ -4460,10 +6249,26 @@ Fout: %@ Verminderd batterijgebruik No comment provided by engineer. + + Register + Register + No comment provided by engineer. + + + Register notification token? + Meldingstoken registreren? + token info + + + Registered + Geregistreerd + token status text + Reject Afwijzen - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4490,6 +6295,16 @@ Fout: %@ Verwijderen No comment provided by engineer. + + Remove archive? + Archief verwijderen? + No comment provided by engineer. + + + Remove image + Verwijder afbeelding + No comment provided by engineer. + Remove member Lid verwijderen @@ -4527,10 +6342,12 @@ Fout: %@ Repeat download + Herhaal het downloaden No comment provided by engineer. Repeat import + Herhaal import No comment provided by engineer. @@ -4540,6 +6357,7 @@ Fout: %@ Repeat upload + Herhaal het uploaden No comment provided by engineer. @@ -4547,6 +6365,56 @@ Fout: %@ Antwoord chat item action + + Report + rapporteren + chat item action + + + Report content: only group moderators will see it. + Inhoud melden: alleen groepsmoderators kunnen dit zien. + report reason + + + Report member profile: only group moderators will see it. + Rapporteer ledenprofiel: alleen groepsmoderators kunnen dit zien. + report reason + + + Report other: only group moderators will see it. + Anders melden: alleen groepsmoderators kunnen het zien. + report reason + + + Report reason? + Reden melding? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + Spam melden: alleen groepsmoderators kunnen het zien. + report reason + + + Report violation: only group moderators will see it. + Rapporteer overtreding: alleen groepsmoderators kunnen dit zien. + report reason + + + Report: %@ + rapporteer: %@ + report in notification + + + Reporting messages to moderators is prohibited. + Het is niet toegestaan om berichten aan moderators te melden. + No comment provided by engineer. + + + Reports + Rapporten + No comment provided by engineer. + Required Vereist @@ -4557,19 +6425,44 @@ Fout: %@ Resetten No comment provided by engineer. + + Reset all hints + Alle hints resetten + No comment provided by engineer. + + + Reset all statistics + Reset alle statistieken + No comment provided by engineer. + + + Reset all statistics? + Alle statistieken resetten? + No comment provided by engineer. + Reset colors Kleuren resetten No comment provided by engineer. + + Reset to app theme + Terugzetten naar app thema + No comment provided by engineer. + Reset to defaults Resetten naar standaardwaarden No comment provided by engineer. + + Reset to user theme + Terugzetten naar gebruikersthema + No comment provided by engineer. + Restart the app to create a new chat profile - Start de app opnieuw om een nieuw chat profiel aan te maken + Start de app opnieuw om een nieuw chatprofiel aan te maken No comment provided by engineer. @@ -4607,9 +6500,9 @@ Fout: %@ Onthullen chat item action - - Revert - Terugdraaien + + Review conditions + Voorwaarden bekijken No comment provided by engineer. @@ -4637,58 +6530,70 @@ Fout: %@ Chat uitvoeren No comment provided by engineer. - - SMP servers - SMP servers + + SMP server + SMP server + No comment provided by engineer. + + + SOCKS proxy + SOCKS proxy + No comment provided by engineer. + + + Safely receive files + Veilig bestanden ontvangen No comment provided by engineer. Safer groups + Veiligere groepen No comment provided by engineer. Save Opslaan - chat item action + alert button +chat item action Save (and notify contacts) Bewaar (en informeer contacten) - No comment provided by engineer. + alert button Save and notify contact Opslaan en Contact melden - No comment provided by engineer. + alert button Save and notify group members Opslaan en groep leden melden No comment provided by engineer. + + Save and reconnect + Opslaan en opnieuw verbinden + No comment provided by engineer. + Save and update group profile Groep profiel opslaan en bijwerken No comment provided by engineer. - - Save archive - Bewaar archief - No comment provided by engineer. - - - Save auto-accept settings - Sla instellingen voor automatisch accepteren op - No comment provided by engineer. - Save group profile Groep profiel opslaan No comment provided by engineer. + + Save list + Lijst opslaan + No comment provided by engineer. + Save passphrase and open chat - Bewaar het wachtwoord en open je gesprekken + Wachtwoord opslaan en open je chats No comment provided by engineer. @@ -4699,7 +6604,7 @@ Fout: %@ Save preferences? Voorkeuren opslaan? - No comment provided by engineer. + alert title Save profile password @@ -4708,22 +6613,27 @@ Fout: %@ Save servers - Bewaar servers + Servers opslaan No comment provided by engineer. Save servers? Servers opslaan? - No comment provided by engineer. - - - Save settings? - Instellingen opslaan? - No comment provided by engineer. + alert title Save welcome message? - Welkomst bericht opslaan? + Welkom bericht opslaan? + No comment provided by engineer. + + + Save your profile? + Uw profiel opslaan? + alert title + + + Saved + Opgeslagen No comment provided by engineer. @@ -4731,11 +6641,31 @@ Fout: %@ Opgeslagen WebRTC ICE servers worden verwijderd No comment provided by engineer. + + Saved from + Opgeslagen van + No comment provided by engineer. + Saved message Opgeslagen bericht message info title + + Saving %lld messages + %lld berichten opslaan + No comment provided by engineer. + + + Scale + Schaal + No comment provided by engineer. + + + Scan / Paste link + Link scannen/plakken + No comment provided by engineer. + Scan QR code Scan QR-code @@ -4776,11 +6706,21 @@ Fout: %@ Zoeken of plak een SimpleX link No comment provided by engineer. + + Secondary + Secundair + No comment provided by engineer. + Secure queue Veilige wachtrij server test step + + Secured + Beveiligd + No comment provided by engineer. + Security assessment Beveiligingsbeoordeling @@ -4794,6 +6734,21 @@ Fout: %@ Select Selecteer + chat item action + + + Select chat profile + Selecteer chatprofiel + No comment provided by engineer. + + + Selected %lld + %lld geselecteerd + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Geselecteerde chat voorkeuren verbieden dit bericht. No comment provided by engineer. @@ -4831,11 +6786,6 @@ Fout: %@ Stuur ontvangstbewijzen naar No comment provided by engineer. - - Send direct message - Direct bericht sturen - No comment provided by engineer. - Send direct message to connect Stuur een direct bericht om verbinding te maken @@ -4846,6 +6796,11 @@ Fout: %@ Stuur een verdwijnend bericht No comment provided by engineer. + + Send errors + Verzend fouten + No comment provided by engineer. + Send link previews Link voorbeelden verzenden @@ -4856,14 +6811,29 @@ Fout: %@ Stuur een livebericht No comment provided by engineer. + + Send message to enable calls. + Stuur een bericht om oproepen mogelijk te maken. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + Stuur berichten rechtstreeks als het IP-adres beschermd is en uw of bestemmingsserver geen privéroutering ondersteunt. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + Stuur berichten rechtstreeks wanneer uw of de doelserver geen privéroutering ondersteunt. + No comment provided by engineer. + Send notifications Meldingen verzenden No comment provided by engineer. - - Send notifications: - Meldingen verzenden: + + Send private reports + Rapporteer privé No comment provided by engineer. @@ -4889,7 +6859,7 @@ Fout: %@ Sender cancelled file transfer. Afzender heeft bestandsoverdracht geannuleerd. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4946,6 +6916,11 @@ Fout: %@ Verzonden op: %@ copied message info + + Sent directly + Direct verzonden + No comment provided by engineer. + Sent file event Verzonden bestandsgebeurtenis @@ -4956,11 +6931,71 @@ Fout: %@ Verzonden bericht message info title + + Sent messages + Verzonden berichten + No comment provided by engineer. + Sent messages will be deleted after set time. Verzonden berichten worden na ingestelde tijd verwijderd. No comment provided by engineer. + + Sent reply + Antwoord verzonden + No comment provided by engineer. + + + Sent total + Totaal verzonden + No comment provided by engineer. + + + Sent via proxy + Verzonden via proxy + No comment provided by engineer. + + + Server + Server + No comment provided by engineer. + + + Server added to operator %@. + Server toegevoegd aan operator %@. + alert message + + + Server address + Server adres + No comment provided by engineer. + + + Server address is incompatible with network settings. + Serveradres is niet compatibel met netwerkinstellingen. + srv error text. + + + Server address is incompatible with network settings: %@. + Serveradres is incompatibel met netwerkinstellingen: %@. + No comment provided by engineer. + + + Server operator changed. + Serveroperator gewijzigd. + alert title + + + Server operators + Serverbeheerders + No comment provided by engineer. + + + Server protocol changed. + Serverprotocol gewijzigd. + alert title + Server requires authorization to create queues, check password Server vereist autorisatie om wachtrijen te maken, controleer wachtwoord @@ -4976,11 +7011,36 @@ Fout: %@ Servertest mislukt! No comment provided by engineer. + + Server type + Server type + No comment provided by engineer. + + + Server version is incompatible with network settings. + Serverversie is incompatibel met netwerkinstellingen. + srv error text + + + Server version is incompatible with your app: %@. + Serverversie is incompatibel met uw app: %@. + No comment provided by engineer. + Servers Servers No comment provided by engineer. + + Servers info + Server informatie + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + Serverstatistieken worden gereset - dit kan niet ongedaan worden gemaakt! + No comment provided by engineer. + Session code Sessie code @@ -4991,11 +7051,21 @@ Fout: %@ Stel 1 dag in No comment provided by engineer. + + Set chat name… + Stel chatnaam in… + No comment provided by engineer. + Set contact name… Contactnaam instellen… No comment provided by engineer. + + Set default theme + Stel het standaard thema in + No comment provided by engineer. + Set group preferences Groep voorkeuren instellen @@ -5006,6 +7076,11 @@ Fout: %@ Stel het in in plaats van systeemverificatie. No comment provided by engineer. + + Set message expiration in chats. + Stel de berichtvervaldatum in chats in. + No comment provided by engineer. + Set passcode Toegangscode instellen @@ -5013,6 +7088,7 @@ Fout: %@ Set passphrase + Wachtwoord instellen No comment provided by engineer. @@ -5035,24 +7111,55 @@ Fout: %@ Instellingen No comment provided by engineer. + + Settings were changed. + Instellingen zijn gewijzigd. + alert message + + + Shape profile images + Vorm profiel afbeeldingen + No comment provided by engineer. + Share Deel - chat item action + alert action +chat item action Share 1-time link Eenmalige link delen No comment provided by engineer. + + Share 1-time link with a friend + Deel eenmalig een link met een vriend + No comment provided by engineer. + + + Share SimpleX address on social media. + Deel het SimpleX-adres op sociale media. + No comment provided by engineer. + Share address Adres delen No comment provided by engineer. + + Share address publicly + Adres openbaar delen + No comment provided by engineer. + Share address with contacts? Adres delen met contacten? + alert title + + + Share from other apps. + Delen vanuit andere apps. No comment provided by engineer. @@ -5060,18 +7167,33 @@ Fout: %@ Deel link No comment provided by engineer. + + Share profile + Profiel delen + No comment provided by engineer. + Share this 1-time invite link Deel deze eenmalige uitnodigingslink No comment provided by engineer. + + Share to SimpleX + Delen op SimpleX + No comment provided by engineer. + Share with contacts Delen met contacten No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code + Toon QR-code No comment provided by engineer. @@ -5089,21 +7211,46 @@ Fout: %@ Laat laatste berichten zien No comment provided by engineer. + + Show message status + Toon berichtstatus + No comment provided by engineer. + + + Show percentage + Percentage weergeven + No comment provided by engineer. + Show preview Toon voorbeeld No comment provided by engineer. + + Show → on messages sent via private routing. + Toon → bij berichten verzonden via privéroutering. + No comment provided by engineer. + Show: Toon: No comment provided by engineer. + + SimpleX + SimpleX + No comment provided by engineer. + SimpleX Address SimpleX adres No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + Simplex-chat en flux hebben een overeenkomst gemaakt om door flux geëxploiteerde servers in de app op te nemen. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. De beveiliging van SimpleX Chat is gecontroleerd door Trail of Bits. @@ -5134,6 +7281,20 @@ Fout: %@ SimpleX adres No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + SimpleX-adressen en eenmalige links kunnen veilig worden gedeeld via elke messenger. + No comment provided by engineer. + + + SimpleX address or 1-time link? + SimpleX adres of eenmalige link? + No comment provided by engineer. + + + SimpleX channel link + simplex link type + SimpleX contact address SimpleX contact adres @@ -5152,6 +7313,16 @@ Fout: %@ SimpleX links SimpleX links + chat feature + + + SimpleX links are prohibited. + SimpleX-links zijn niet toegestaan. + No comment provided by engineer. + + + SimpleX links not allowed + SimpleX-links zijn niet toegestaan No comment provided by engineer. @@ -5159,11 +7330,21 @@ Fout: %@ Eenmalige SimpleX uitnodiging simplex link type + + SimpleX protocols reviewed by Trail of Bits. + SimpleX-protocollen beoordeeld door Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Vereenvoudigde incognitomodus No comment provided by engineer. + + Size + Maat + No comment provided by engineer. + Skip Overslaan @@ -5179,16 +7360,54 @@ Fout: %@ Kleine groepen (max 20) No comment provided by engineer. + + Soft + Soft + blur media + + + Some app settings were not migrated. + Sommige app-instellingen zijn niet gemigreerd. + No comment provided by engineer. + + + Some file(s) were not exported: + Sommige bestanden zijn niet geëxporteerd: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. Er zijn enkele niet-fatale fouten opgetreden tijdens het importeren - u kunt de Chat console raadplegen voor meer details. No comment provided by engineer. + + Some non-fatal errors occurred during import: + Er zijn enkele niet-fatale fouten opgetreden tijdens het importeren: + No comment provided by engineer. + + + Some servers failed the test: +%@ + Sommige servers zijn niet geslaagd voor de test: +%@ + alert message + Somebody Iemand notification title + + Spam + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + Vierkant, cirkel of iets daartussenin. + No comment provided by engineer. + Start chat Begin gesprek @@ -5204,6 +7423,16 @@ Fout: %@ Start migratie No comment provided by engineer. + + Starting from %@. + Beginnend vanaf %@. + No comment provided by engineer. + + + Statistics + Statistieken + No comment provided by engineer. + Stop Stop @@ -5216,11 +7445,7 @@ Fout: %@ Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - Stop de chat om database acties mogelijk te maken + Stop chat No comment provided by engineer. @@ -5251,20 +7476,46 @@ Fout: %@ Stop sharing Stop met delen - No comment provided by engineer. + alert action Stop sharing address? Stop met het delen van adres? - No comment provided by engineer. + alert title Stopping chat + Chat stoppen No comment provided by engineer. + + Storage + Opslag + No comment provided by engineer. + + + Strong + Krachtig + blur media + Submit - Indienen + Bevestigen + No comment provided by engineer. + + + Subscribed + Subscribed + No comment provided by engineer. + + + Subscription errors + Subscription fouten + No comment provided by engineer. + + + Subscriptions ignored + Subscriptions genegeerd No comment provided by engineer. @@ -5272,6 +7523,16 @@ Fout: %@ Ondersteuning van SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Wisselen tussen audio en video tijdens het gesprek. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Wijzig chatprofiel voor eenmalige uitnodigingen. + No comment provided by engineer. + System Systeem @@ -5282,11 +7543,21 @@ Fout: %@ Systeem authenticatie No comment provided by engineer. + + TCP connection + TCP verbinding + No comment provided by engineer. + TCP connection timeout Timeout van TCP-verbinding No comment provided by engineer. + + TCP port for messaging + TCP-poort voor berichtenuitwisseling + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5302,11 +7573,21 @@ Fout: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + Staart + No comment provided by engineer. + Take picture Foto nemen No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + Tik op SimpleX-adres maken in het menu om het later te maken. + No comment provided by engineer. + Tap button Tik op de knop @@ -5314,44 +7595,49 @@ Fout: %@ Tap to Connect - Tik om verbinding te maken + Tik hier om verbinding te maken No comment provided by engineer. Tap to activate profile. - Tik om profiel te activeren. + Tik hier om profiel te activeren. No comment provided by engineer. Tap to join - Tik om lid te worden + Tik hier om lid te worden No comment provided by engineer. Tap to join incognito - Tik om incognito lid te worden + Tik hier om incognito lid te worden No comment provided by engineer. Tap to paste link - Tik om de link te plakken + Tik hier om de link te plakken No comment provided by engineer. Tap to scan - Tik om te scannen + Tik hier om te scannen No comment provided by engineer. - - Tap to start a new chat - Tik om een nieuw gesprek te starten - No comment provided by engineer. + + Temporary file error + Tijdelijke bestandsfout + file error alert title Test failed at step %@. Test mislukt bij stap %@. server test failure + + Test notifications + Testmeldingen + No comment provided by engineer. + Test server Server test @@ -5365,7 +7651,7 @@ Fout: %@ Tests failed! Testen mislukt! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5382,11 +7668,6 @@ Fout: %@ Dank aan de gebruikers – draag bij via Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - Het eerste platform zonder gebruikers-ID's, privé door ontwerp. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5399,6 +7680,16 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. De app kan u op de hoogte stellen wanneer u berichten of contact verzoeken ontvangt - open de instellingen om dit in te schakelen. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + De app beschermt uw privacy door in elk gesprek andere operatoren te gebruiken. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + De app vraagt om downloads van onbekende bestandsservers (behalve .onion) te bevestigen. + No comment provided by engineer. + The attempt to change database passphrase was not completed. De poging om het wachtwoord van de database te wijzigen is niet voltooid. @@ -5409,6 +7700,11 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. De code die u heeft gescand is geen SimpleX link QR-code. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + De verbinding heeft de limiet van niet-afgeleverde berichten bereikt. Uw contactpersoon is mogelijk offline. + No comment provided by engineer. + The connection you accepted will be cancelled! De door u geaccepteerde verbinding wordt geannuleerd! @@ -5429,6 +7725,11 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. De versleuteling werkt en de nieuwe versleutelingsovereenkomst is niet vereist. Dit kan leiden tot verbindingsfouten! No comment provided by engineer. + + The future of messaging + De volgende generatie privéberichten + No comment provided by engineer. + The hash of the previous message is different. De hash van het vorige bericht is anders. @@ -5444,9 +7745,14 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. Het bericht wordt gemarkeerd als gemodereerd voor alle leden. No comment provided by engineer. - - The next generation of private messaging - De volgende generatie privéberichten + + The messages will be deleted for all members. + De berichten worden voor alle leden verwijderd. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. + De berichten worden voor alle leden als gemodereerd gemarkeerd. No comment provided by engineer. @@ -5454,9 +7760,14 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. De oude database is niet verwijderd tijdens de migratie, deze kan worden verwijderd. No comment provided by engineer. - - The profile is only shared with your contacts. - Het profiel wordt alleen gedeeld met uw contacten. + + The same conditions will apply to operator **%@**. + Dezelfde voorwaarden gelden voor operator **%@**. + No comment provided by engineer. + + + The second preset operator in the app! + De tweede vooraf ingestelde operator in de app! No comment provided by engineer. @@ -5471,7 +7782,12 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. The servers for new connections of your current chat profile **%@**. - De servers voor nieuwe verbindingen van uw huidige chat profiel **%@**. + De servers voor nieuwe verbindingen van uw huidige chatprofiel **%@**. + No comment provided by engineer. + + + The servers for new files of your current chat profile **%@**. + De servers voor nieuwe bestanden van uw huidige chatprofiel **%@**. No comment provided by engineer. @@ -5479,9 +7795,19 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. De tekst die u hebt geplakt is geen SimpleX link. No comment provided by engineer. - - Theme - Thema + + The uploaded database archive will be permanently removed from the servers. + Het geüploade databasearchief wordt permanent van de servers verwijderd. + No comment provided by engineer. + + + Themes + Thema's + No comment provided by engineer. + + + These conditions will also apply for: **%@**. + Deze voorwaarden zijn ook van toepassing op: **%@**. No comment provided by engineer. @@ -5504,22 +7830,29 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. Deze actie kan niet ongedaan worden gemaakt, de berichten die eerder zijn verzonden en ontvangen dan geselecteerd, worden verwijderd. Het kan enkele minuten duren. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + Deze actie kan niet ongedaan worden gemaakt. De berichten die eerder in deze chat zijn verzonden en ontvangen dan geselecteerd, worden verwijderd. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. - Deze actie kan niet ongedaan worden gemaakt. Uw profiel, contacten, berichten en bestanden gaan onomkeerbaar verloren. + Deze actie kan niet ongedaan worden gemaakt. Uw profiel, contacten, berichten en bestanden gaan definitief verloren. No comment provided by engineer. This chat is protected by end-to-end encryption. + Deze chat is beveiligd met end-to-end codering. E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + Deze chat wordt beschermd door quantum bestendige end-to-end codering. E2EE info chat item This device name - Deze apparaatnaam + Naam van dit apparaat No comment provided by engineer. @@ -5547,9 +7880,28 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. Dit is uw eigen eenmalige link! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + Deze link is gebruikt met een ander mobiel apparaat. Maak een nieuwe link op de desktop. + No comment provided by engineer. + + + This message was deleted or not received yet. + Dit bericht is verwijderd of nog niet ontvangen. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. - Deze instelling is van toepassing op berichten in je huidige chat profiel **%@**. + Deze instelling is van toepassing op berichten in je huidige chatprofiel **%@**. + No comment provided by engineer. + + + Title + Titel No comment provided by engineer. @@ -5572,9 +7924,9 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. Om een nieuwe verbinding te maken No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Om de privacy te beschermen, heeft SimpleX in plaats van gebruikers-ID's die door alle andere platforms worden gebruikt, ID's voor berichten wachtrijen, afzonderlijk voor elk van uw contacten. + + To protect against your link being replaced, you can compare contact security codes. + Om te voorkomen dat uw link wordt vervangen, kunt u contactbeveiligingscodes vergelijken. No comment provided by engineer. @@ -5582,6 +7934,11 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. Om de tijdzone te beschermen, gebruiken afbeeldings-/spraakbestanden UTC. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + Om uw IP-adres te beschermen, gebruikt privéroutering uw SMP-servers om berichten te bezorgen. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5589,6 +7946,26 @@ You will be prompted to complete authentication before this feature is enabled.< U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingeschakeld. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Om de privacy te beschermen, heeft SimpleX in plaats van gebruikers-ID's die door alle andere platforms worden gebruikt, ID's voor berichten wachtrijen, afzonderlijk voor elk van uw contacten. + No comment provided by engineer. + + + To receive + Om te ontvangen + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + Geef toestemming om de microfoon te gebruiken om spraak op te nemen. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Om video op te nemen, dient u toestemming te geven om de camera te gebruiken. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Geef toestemming om de microfoon te gebruiken om een spraakbericht op te nemen. @@ -5596,7 +7973,12 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. - Om uw verborgen profiel te onthullen, voert u een volledig wachtwoord in een zoek veld in op de pagina **Uw chat profielen**. + Om uw verborgen profiel te onthullen, voert u een volledig wachtwoord in een zoek veld in op de pagina **Uw chatprofielen**. + No comment provided by engineer. + + + To send + Om te verzenden No comment provided by engineer. @@ -5604,21 +7986,51 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc Om directe push meldingen te ondersteunen, moet de chat database worden gemigreerd. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + Om de servers van **%@** te gebruiken, moet u de gebruiksvoorwaarden accepteren. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. Vergelijk (of scan) de code op uw apparaten om end-to-end-codering met uw contact te verifiëren. No comment provided by engineer. + + Toggle chat list: + Chatlijst wisselen: + No comment provided by engineer. + Toggle incognito when connecting. Schakel incognito in tijdens het verbinden. No comment provided by engineer. + + Token status: %@. + Tokenstatus: %@. + token status + + + Toolbar opacity + De transparantie van de werkbalk + No comment provided by engineer. + + + Total + Totaal + No comment provided by engineer. + Transport isolation Transport isolation No comment provided by engineer. + + Transport sessions + Transportsessies + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Proberen verbinding te maken met de server die wordt gebruikt om berichten van dit contact te ontvangen (fout: %@). @@ -5674,10 +8086,10 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc Lid deblokkeren? No comment provided by engineer. - - Unexpected error: %@ - Onverwachte fout: %@ - item status description + + Undelivered messages + Niet afgeleverde berichten + No comment provided by engineer. Unexpected migration state @@ -5687,7 +8099,7 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc Unfav. Niet fav. - No comment provided by engineer. + swipe action Unhide @@ -5696,7 +8108,7 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc Unhide chat profile - Chat profiel zichtbaar maken + Chatprofiel zichtbaar maken No comment provided by engineer. @@ -5724,6 +8136,11 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc Onbekende fout No comment provided by engineer. + + Unknown servers! + Onbekende servers! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. Schakel de modus Niet storen in om onderbrekingen te voorkomen, tenzij u de iOS-oproepinterface gebruikt. @@ -5759,11 +8176,15 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Unmute Dempen opheffen - No comment provided by engineer. + notification label action Unread Ongelezen + swipe action + + + Unsupported connection link No comment provided by engineer. @@ -5776,11 +8197,6 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Update No comment provided by engineer. - - Update .onion hosts setting? - .onion hosts-instelling updaten? - No comment provided by engineer. - Update database passphrase Database wachtwoord bijwerken @@ -5791,9 +8207,14 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Netwerk instellingen bijwerken? No comment provided by engineer. - - Update transport isolation mode? - Transportisolatiemodus updaten? + + Update settings? + Instellingen actualiseren? + No comment provided by engineer. + + + Updated conditions + Bijgewerkte voorwaarden No comment provided by engineer. @@ -5801,18 +8222,19 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Door de instellingen bij te werken, wordt de client opnieuw verbonden met alle servers. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - Als u deze instelling bijwerkt, wordt de client opnieuw verbonden met alle servers. - No comment provided by engineer. - Upgrade and open chat Upgrade en open chat No comment provided by engineer. + + Upload errors + Upload fouten + No comment provided by engineer. + Upload failed + Upload mislukt No comment provided by engineer. @@ -5820,8 +8242,24 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Upload bestand server test step + + Uploaded + Geüpload + No comment provided by engineer. + + + Uploaded files + Geüploade bestanden + No comment provided by engineer. + Uploading archive + Archief uploaden + No comment provided by engineer. + + + Use %@ + Gebruik %@ No comment provided by engineer. @@ -5829,11 +8267,25 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Gebruik .onion-hosts No comment provided by engineer. + + Use SOCKS proxy + Gebruik SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? SimpleX Chat servers gebruiken? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + Gebruik TCP-poort %@ als er geen poort is opgegeven. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat Gebruik chat @@ -5844,6 +8296,16 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Gebruik het huidige profiel No comment provided by engineer. + + Use for files + Gebruik voor bestanden + No comment provided by engineer. + + + Use for messages + Gebruik voor berichten + No comment provided by engineer. + Use for new connections Gebruik voor nieuwe verbindingen @@ -5869,28 +8331,58 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Alleen lokale meldingen gebruiken? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + Gebruik privéroutering met onbekende servers wanneer het IP-adres niet beveiligd is. + No comment provided by engineer. + + + Use private routing with unknown servers. + Gebruik privéroutering met onbekende servers. + No comment provided by engineer. + Use server Gebruik server No comment provided by engineer. + + Use servers + Gebruik servers + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. + Gebruik de app tijdens het gesprek. No comment provided by engineer. - - User profile - Gebruikers profiel + + Use the app with one hand. + Gebruik de app met één hand. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - Het gebruik van .onion-hosts vereist een compatibele VPN-provider. + + Use web port + Gebruik een webpoort + No comment provided by engineer. + + + User selection + Gebruikersselectie + No comment provided by engineer. + + + Username + Gebruikersnaam No comment provided by engineer. Using SimpleX Chat servers. - SimpleX Chat servers gebruiken. + Gebruik SimpleX Chat servers. No comment provided by engineer. @@ -5915,10 +8407,12 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Verify database passphrase + Controleer het wachtwoord van de database No comment provided by engineer. Verify passphrase + Controleer het wachtwoord No comment provided by engineer. @@ -5956,11 +8450,21 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Video's en bestanden tot 1 GB No comment provided by engineer. + + View conditions + Bekijk voorwaarden + No comment provided by engineer. + View security code Beveiligingscode bekijken No comment provided by engineer. + + View updated conditions + Bekijk de bijgewerkte voorwaarden + No comment provided by engineer. + Visible history Zichtbare geschiedenis @@ -5973,17 +8477,22 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Voice messages are prohibited in this chat. - Spraak berichten zijn verboden in deze chat. + Spraak berichten zijn niet toegestaan in dit gesprek. No comment provided by engineer. - - Voice messages are prohibited in this group. - Spraak berichten zijn verboden in deze groep. + + Voice messages are prohibited. + Spraak berichten zijn niet toegestaan. + No comment provided by engineer. + + + Voice messages not allowed + Spraakberichten niet toegestaan No comment provided by engineer. Voice messages prohibited! - Spraak berichten verboden! + Spraak berichten niet toegestaan! No comment provided by engineer. @@ -6011,8 +8520,19 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Wachten op video No comment provided by engineer. + + Wallpaper accent + Achtergrond accent + No comment provided by engineer. + + + Wallpaper background + Wallpaper achtergrond + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures + Waarschuwing: het starten van de chat op meerdere apparaten wordt niet ondersteund en zal leiden tot mislukte bezorging van berichten No comment provided by engineer. @@ -6032,11 +8552,12 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Welcome message - Welkomst bericht + Welkom bericht No comment provided by engineer. Welcome message is too long + Welkom bericht is te lang No comment provided by engineer. @@ -6049,9 +8570,14 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Wanneer beschikbaar No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Wanneer mensen vragen om verbinding te maken, kunt u dit accepteren of weigeren. + + When connecting audio and video calls. + Bij het verbinden van audio- en video-oproepen. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. + Wanneer er meer dan één operator is ingeschakeld, beschikt geen enkele operator over metagegevens om te achterhalen wie met wie communiceert. No comment provided by engineer. @@ -6059,6 +8585,21 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Wanneer je een incognito profiel met iemand deelt, wordt dit profiel gebruikt voor de groepen waarvoor ze je uitnodigen. No comment provided by engineer. + + WiFi + Wifi + No comment provided by engineer. + + + Will be enabled in direct chats! + Wordt ingeschakeld in directe chats! + No comment provided by engineer. + + + Wired ethernet + Bekabeld Ethernet + No comment provided by engineer. + With encrypted files and media. ‐Met versleutelde bestanden en media. @@ -6066,7 +8607,7 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak With optional welcome message. - Met optioneel welkomst bericht. + Met optioneel welkom bericht. No comment provided by engineer. @@ -6074,28 +8615,44 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Met verminderd batterijgebruik. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + Zonder Tor of VPN is uw IP-adres zichtbaar voor bestandsservers. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + Zonder Tor of VPN zal uw IP-adres zichtbaar zijn voor deze XFTP-relays: %@. + alert message + Wrong database passphrase Verkeerd wachtwoord voor de database No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + Verkeerde sleutel of onbekende verbinding - hoogstwaarschijnlijk is deze verbinding verwijderd. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + Verkeerde sleutel of onbekend bestanddeeladres - hoogstwaarschijnlijk is het bestand verwijderd. + file error text + Wrong passphrase! Verkeerd wachtwoord! No comment provided by engineer. - - XFTP servers - XFTP servers - No comment provided by engineer. - - - You - Jij + + XFTP server + XFTP server No comment provided by engineer. You **must not** use the same database on two devices. + U **mag** niet dezelfde database op twee apparaten gebruiken. No comment provided by engineer. @@ -6110,7 +8667,7 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak You already have a chat profile with the same display name. Please choose another name. - Je hebt al een chat profiel met dezelfde weergave naam. Kies een andere naam. + Je hebt al een chatprofiel met dezelfde weergave naam. Kies een andere naam. No comment provided by engineer. @@ -6118,6 +8675,11 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak U bent al verbonden met %@. No comment provided by engineer. + + You are already connected with %@. + U bent al verbonden met %@. + No comment provided by engineer. + You are already connecting to %@. U maakt al verbinding met %@. @@ -6165,11 +8727,26 @@ Deelnameverzoek herhalen? Je bent uitgenodigd voor de groep No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + U bent niet verbonden met deze servers. Privéroutering wordt gebruikt om berichten bij hen af te leveren. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. U kunt oproepen van het vergrendelingsscherm accepteren, zonder apparaat- en app-verificatie. No comment provided by engineer. + + You can change it in Appearance settings. + U kunt dit wijzigen in de instellingen onder uiterlijk. + No comment provided by engineer. + + + You can configure servers via settings. + U kunt servers configureren via instellingen. + No comment provided by engineer. + You can create it later U kan het later maken @@ -6187,6 +8764,7 @@ Deelnameverzoek herhalen? You can give another try. + Je kunt het nog een keer proberen. No comment provided by engineer. @@ -6199,11 +8777,21 @@ Deelnameverzoek herhalen? Je kunt het via Instellingen zichtbaar maken voor je SimpleX contacten. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ Je kunt nu berichten sturen naar %@ notification body + + You can send messages to %@ from Archived contacts. + U kunt berichten naar %@ sturen vanuit gearchiveerde contacten. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + U kunt een verbindingsnaam instellen, zodat u kunt onthouden met wie de link is gedeeld. + No comment provided by engineer. + You can set lock screen notification preview via settings. U kunt een voorbeeld van een melding op het vergrendeld scherm instellen via instellingen. @@ -6219,16 +8807,16 @@ Deelnameverzoek herhalen? U kunt dit adres delen met uw contacten om hen verbinding te laten maken met **%@**. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - U kunt uw adres delen als een link of als een QR-code. Iedereen kan verbinding met u maken. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app U kunt de chat starten via app Instellingen / Database of door de app opnieuw op te starten No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + Je kunt het gesprek met %@ nog steeds bekijken in de lijst met chats. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. Je kunt SimpleX Vergrendeling aanzetten via Instellingen. @@ -6242,23 +8830,23 @@ Deelnameverzoek herhalen? You can view invitation link again in connection details. U kunt de uitnodigingslink opnieuw bekijken in de verbindingsdetails. - No comment provided by engineer. + alert message You can't send messages! Je kunt geen berichten versturen! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - U bepaalt via welke server(s) de berichten **ontvangen**, uw contacten de servers die u gebruikt om ze berichten te sturen. - No comment provided by engineer. - You could not be verified; please try again. U kon niet worden geverifieerd; probeer het opnieuw. No comment provided by engineer. + + You decide who can connect. + Jij bepaalt wie er verbinding mag maken. + No comment provided by engineer. + You have already requested connection via this address! U heeft al een verbinding aangevraagd via dit adres! @@ -6271,11 +8859,6 @@ Repeat connection request? Verbindingsverzoek herhalen? No comment provided by engineer. - - You have no chats - Je hebt geen gesprekken - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. U moet elke keer dat de app start het wachtwoord invoeren, deze wordt niet op het apparaat opgeslagen. @@ -6296,11 +8879,26 @@ Verbindingsverzoek herhalen? Je bent lid geworden van deze groep. Verbinding maken met uitnodigend groepslid. No comment provided by engineer. + + You may migrate the exported database. + U kunt de geëxporteerde database migreren. + No comment provided by engineer. + + + You may save the exported archive. + U kunt het geëxporteerde archief opslaan. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. U mag ALLEEN de meest recente versie van uw chat database op één apparaat gebruiken, anders ontvangt u mogelijk geen berichten meer van sommige contacten. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + U moet uw contactpersoon toestemming geven om te bellen, zodat hij/zij je kan bellen. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. U moet uw contact toestemming geven om spraak berichten te verzenden om ze te kunnen verzenden. @@ -6316,6 +8914,11 @@ Verbindingsverzoek herhalen? Je hebt een groep uitnodiging verzonden No comment provided by engineer. + + You should receive notifications. + U zou meldingen moeten ontvangen. + token info + You will be connected to group when the group host's device is online, please wait or check later! Je wordt verbonden met de groep wanneer het apparaat van de groep host online is, even geduld a.u.b. of controleer het later! @@ -6351,6 +8954,11 @@ Verbindingsverzoek herhalen? U ontvangt nog steeds oproepen en meldingen van gedempte profielen wanneer deze actief zijn. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + U ontvangt geen berichten meer van deze chat. De chatgeschiedenis blijft bewaard. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Je ontvangt geen berichten meer van deze groep. Je gesprek geschiedenis blijft behouden. @@ -6371,31 +8979,16 @@ Verbindingsverzoek herhalen? Je gebruikt een incognito profiel voor deze groep. Om te voorkomen dat je je hoofdprofiel deelt, is het niet toegestaan om contacten uit te nodigen No comment provided by engineer. - - Your %@ servers - Uw %@ servers - No comment provided by engineer. - Your ICE servers Uw ICE servers No comment provided by engineer. - - Your SMP servers - Uw SMP servers - No comment provided by engineer. - Your SimpleX address Uw SimpleX adres No comment provided by engineer. - - Your XFTP servers - Uw XFTP servers - No comment provided by engineer. - Your calls Uw oproepen @@ -6411,16 +9004,19 @@ Verbindingsverzoek herhalen? Uw chat database is niet versleuteld, stel een wachtwoord in om deze te versleutelen. No comment provided by engineer. + + Your chat preferences + Uw chat voorkeuren + alert title + Your chat profiles Uw chat profielen No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Uw contact moet online zijn om de verbinding te voltooien. -U kunt deze verbinding verbreken en het contact verwijderen en later proberen met een nieuwe link. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Uw verbinding is verplaatst naar %@, maar er is een onverwachte fout opgetreden tijdens het omleiden naar het profiel. No comment provided by engineer. @@ -6438,6 +9034,11 @@ U kunt deze verbinding verbreken en het contact verwijderen en later proberen me Uw contacten blijven verbonden. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Uw inloggegevens worden mogelijk niet-versleuteld verzonden. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Uw huidige chat database wordt VERWIJDERD en VERVANGEN door de geïmporteerde. @@ -6468,33 +9069,36 @@ U kunt deze verbinding verbreken en het contact verwijderen en later proberen me Uw profiel **%@** wordt gedeeld. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Uw profiel wordt op uw apparaat opgeslagen en alleen gedeeld met uw contacten. -SimpleX servers kunnen uw profiel niet zien. + + Your profile is stored on your device and only shared with your contacts. + Het profiel wordt alleen gedeeld met uw contacten. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Uw profiel, contacten en afgeleverde berichten worden op uw apparaat opgeslagen. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Uw profiel wordt op uw apparaat opgeslagen en alleen gedeeld met uw contacten. SimpleX servers kunnen uw profiel niet zien. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Je profiel is gewijzigd. Als je het opslaat, wordt het bijgewerkte profiel naar al je contacten verzonden. + alert message + Your random profile Je willekeurige profiel No comment provided by engineer. - - Your server - Uw server - No comment provided by engineer. - Your server address Uw server adres No comment provided by engineer. + + Your servers + Uw servers + No comment provided by engineer. + Your settings Uw instellingen @@ -6535,11 +9139,21 @@ SimpleX servers kunnen uw profiel niet zien. geaccepteerde oproep call status + + accepted invitation + geaccepteerde uitnodiging + chat list item title + admin Beheerder member role + + admins + beheerders + feature role + agreeing encryption for %@… versleuteling overeenkomen voor %@… @@ -6550,6 +9164,11 @@ SimpleX servers kunnen uw profiel niet zien. versleuteling overeenkomen… chat item text + + all members + alle leden + feature role + always altijd @@ -6560,6 +9179,16 @@ SimpleX servers kunnen uw profiel niet zien. en %lld andere gebeurtenissen No comment provided by engineer. + + archived report + gearchiveerd rapport + No comment provided by engineer. + + + attempts + pogingen + No comment provided by engineer. + audio call (not e2e encrypted) audio oproep (niet e2e versleuteld) @@ -6587,19 +9216,25 @@ SimpleX servers kunnen uw profiel niet zien. blocked %@ - geblokkeerd %@ + blokkeerde %@ rcv group event chat item blocked by admin geblokkeerd door beheerder - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold vetgedrukt No comment provided by engineer. + + call + bellen + No comment provided by engineer. + call error oproepfout @@ -6703,7 +9338,7 @@ SimpleX servers kunnen uw profiel niet zien. connecting… Verbinden… - chat list item title + No comment provided by engineer. connection established @@ -6750,10 +9385,16 @@ SimpleX servers kunnen uw profiel niet zien. dagen time unit + + decryption errors + decoderingsfouten + No comment provided by engineer. + default (%@) standaard (%@) - pref value + delete after time +pref value default (no) @@ -6800,6 +9441,11 @@ SimpleX servers kunnen uw profiel niet zien. dubbel bericht integrity error chat item + + duplicates + duplicaten + No comment provided by engineer. + e2e encrypted e2e versleuteld @@ -6875,9 +9521,14 @@ SimpleX servers kunnen uw profiel niet zien. fout No comment provided by engineer. - - event happened - gebeurtenis gebeurd + + expired + verlopen + No comment provided by engineer. + + + forwarded + doorgestuurd No comment provided by engineer. @@ -6905,6 +9556,11 @@ SimpleX servers kunnen uw profiel niet zien. iOS-keychain wordt gebruikt om het wachtwoord veilig op te slaan nadat u de app opnieuw hebt opgestart of het wachtwoord hebt gewijzigd, hiermee kunt u push meldingen ontvangen. No comment provided by engineer. + + inactive + inactief + No comment provided by engineer. + incognito via contact address link incognito via contact adres link @@ -6945,6 +9601,11 @@ SimpleX servers kunnen uw profiel niet zien. uitnodiging voor groep %@ group name + + invite + uitnodiging + No comment provided by engineer. + invited uitgenodigd @@ -7000,6 +9661,11 @@ SimpleX servers kunnen uw profiel niet zien. is toegetreden rcv group event chat item + + message + bericht + No comment provided by engineer. + message received bericht ontvangen @@ -7025,6 +9691,11 @@ SimpleX servers kunnen uw profiel niet zien. gemodereerd door %@ marked deleted chat item preview text + + moderator + moderator + member role + months maanden @@ -7033,7 +9704,7 @@ SimpleX servers kunnen uw profiel niet zien. never nooit - No comment provided by engineer. + delete after time new message @@ -7064,8 +9735,8 @@ SimpleX servers kunnen uw profiel niet zien. off uit enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7082,18 +9753,44 @@ SimpleX servers kunnen uw profiel niet zien. aan group pref value + + other + overig + No comment provided by engineer. + + + other errors + overige fouten + No comment provided by engineer. + owner Eigenaar member role + + owners + eigenaren + feature role + peer-to-peer peer-to-peer No comment provided by engineer. + + pending + In behandeling + No comment provided by engineer. + + + pending approval + in afwachting van goedkeuring + No comment provided by engineer. + quantum resistant e2e encryption + quantum bestendige e2e-codering chat item text @@ -7106,6 +9803,11 @@ SimpleX servers kunnen uw profiel niet zien. bevestiging ontvangen… No comment provided by engineer. + + rejected + afgewezen + No comment provided by engineer. + rejected call geweigerde oproep @@ -7136,6 +9838,26 @@ SimpleX servers kunnen uw profiel niet zien. heeft je verwijderd rcv group event chat item + + requested to connect + verzocht om verbinding te maken + chat list item title + + + saved + opgeslagen + No comment provided by engineer. + + + saved from %@ + opgeslagen van %@ + No comment provided by engineer. + + + search + zoekopdracht + No comment provided by engineer. + sec sec @@ -7161,6 +9883,15 @@ SimpleX servers kunnen uw profiel niet zien. stuur een direct bericht No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + informatie over serverwachtrij: %1$@ + +laatst ontvangen bericht: %2$@ + queue info + set new contact address nieuw contactadres instellen @@ -7168,11 +9899,12 @@ SimpleX servers kunnen uw profiel niet zien. set new profile picture - nieuwe profielfoto instellen + nieuwe profielfoto profile update event chat item standard end-to-end encryption + standaard end-to-end encryptie chat item text @@ -7200,11 +9932,21 @@ SimpleX servers kunnen uw profiel niet zien. onbekend connection info + + unknown servers + onbekende relays + No comment provided by engineer. + unknown status onbekende status No comment provided by engineer. + + unprotected + onbeschermd + No comment provided by engineer. + updated group profile bijgewerkt groep profiel @@ -7245,6 +9987,11 @@ SimpleX servers kunnen uw profiel niet zien. via relay No comment provided by engineer. + + video + video + No comment provided by engineer. + video call (not e2e encrypted) video gesprek (niet e2e versleuteld) @@ -7270,11 +10017,21 @@ SimpleX servers kunnen uw profiel niet zien. weken time unit + + when IP hidden + wanneer IP verborgen is + No comment provided by engineer. + yes Ja pref value + + you + jij + No comment provided by engineer. + you are invited to group je bent uitgenodigd voor de groep @@ -7282,7 +10039,7 @@ SimpleX servers kunnen uw profiel niet zien. you are observer - jij bent waarnemer + je bent waarnemer No comment provided by engineer. @@ -7312,7 +10069,7 @@ SimpleX servers kunnen uw profiel niet zien. you left - jij bent vertrokken + je bent vertrokken snd group event chat item @@ -7349,7 +10106,7 @@ SimpleX servers kunnen uw profiel niet zien.
- +
@@ -7386,7 +10143,7 @@ SimpleX servers kunnen uw profiel niet zien.
- +
@@ -7406,4 +10163,249 @@ SimpleX servers kunnen uw profiel niet zien.
+ +
+ +
+ + + %d new events + ‐%d nieuwe gebeurtenissen + notification body + + + From %d chat(s) + notification body + + + From: %@ + Van: %@ + notification body + + + New events + Nieuwe gebeurtenissen + notification + + + New messages + Nieuwe berichten + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + App is vergrendeld! + No comment provided by engineer. + + + Cancel + Annuleren + No comment provided by engineer. + + + Cannot access keychain to save database password + Kan geen toegang krijgen tot de keychain om het database wachtwoord op te slaan + No comment provided by engineer. + + + Cannot forward message + Kan bericht niet doorsturen + No comment provided by engineer. + + + Comment + Opmerking + No comment provided by engineer. + + + Currently maximum supported file size is %@. + De momenteel maximaal ondersteunde bestandsgrootte is %@. + No comment provided by engineer. + + + Database downgrade required + Database downgrade vereist + No comment provided by engineer. + + + Database encrypted! + Database versleuteld! + No comment provided by engineer. + + + Database error + Database fout + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + Het wachtwoord van de database verschilt van het wachtwoord die in de keychain is opgeslagen. + No comment provided by engineer. + + + Database passphrase is required to open chat. + Database wachtwoord is vereist om je chats te openen. + No comment provided by engineer. + + + Database upgrade required + Database upgrade vereist + No comment provided by engineer. + + + Error preparing file + Fout bij voorbereiden bestand + No comment provided by engineer. + + + Error preparing message + Fout bij het voorbereiden van bericht + No comment provided by engineer. + + + Error: %@ + Fout: %@ + No comment provided by engineer. + + + File error + Bestandsfout + No comment provided by engineer. + + + Incompatible database version + Incompatibele database versie + No comment provided by engineer. + + + Invalid migration confirmation + Ongeldige migratie bevestiging + No comment provided by engineer. + + + Keychain error + Keychain fout + No comment provided by engineer. + + + Large file! + Groot bestand! + No comment provided by engineer. + + + No active profile + Geen actief profiel + No comment provided by engineer. + + + Ok + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + Open de app om de database te downgraden. + No comment provided by engineer. + + + Open the app to upgrade the database. + Open de app om de database te upgraden. + No comment provided by engineer. + + + Passphrase + Wachtwoord + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Maak een profiel aan in de SimpleX app + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Geselecteerde chat voorkeuren verbieden dit bericht. + No comment provided by engineer. + + + Sending a message takes longer than expected. + Het verzenden van een bericht duurt langer dan verwacht. + No comment provided by engineer. + + + Sending message… + Bericht versturen… + No comment provided by engineer. + + + Share + Deel + No comment provided by engineer. + + + Slow network? + Traag netwerk? + No comment provided by engineer. + + + Unknown database error: %@ + Onbekende database fout: %@ + No comment provided by engineer. + + + Unsupported format + Niet ondersteund formaat + No comment provided by engineer. + + + Wait + wachten + No comment provided by engineer. + + + Wrong database passphrase + Verkeerde database wachtwoord + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + U kunt delen toestaan in de instellingen voor Privacy en beveiliging / SimpleX Lock. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/contents.json b/apps/ios/SimpleX Localizations/nl.xcloc/contents.json index 20246f53d4..4b8d468de2 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/nl.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "nl", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff index 24e9d5dda2..175c8b4112 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (można skopiować) @@ -39,7 +12,7 @@ !1 colored! - !1 kolorowy! + !1 pokolorowany! No comment provided by engineer. @@ -109,6 +82,7 @@ %@ downloaded + %@ pobrane No comment provided by engineer. @@ -126,13 +100,19 @@ %@ jest zweryfikowany No comment provided by engineer. + + %@ server + %@ serwer + No comment provided by engineer. + %@ servers - %@ serwery + %@ serwery/ów No comment provided by engineer. %@ uploaded + %@ wgrane No comment provided by engineer. @@ -140,6 +120,11 @@ %@ chce się połączyć! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ i %lld członków @@ -160,11 +145,36 @@ %d dni time interval + + %d file(s) are still being downloaded. + %d plik(ów) jest dalej pobieranych. + forward confirmation reason + + + %d file(s) failed to download. + %d plik(ów) nie udało się pobrać. + forward confirmation reason + + + %d file(s) were deleted. + %d plik(ów) zostało usuniętych. + forward confirmation reason + + + %d file(s) were not downloaded. + %d plik(ów) nie zostało pobranych. + forward confirmation reason + %d hours %d godzin time interval + + %d messages not forwarded + %d wiadomości nie przekazanych + alert title + %d min %d min @@ -180,6 +190,11 @@ %d sek time interval + + %d seconds(s) + %d sekundach + delete after time + %d skipped message(s) %d pominięte wiadomość(i) @@ -250,11 +265,6 @@ %lld nowe języki interfejsu No comment provided by engineer. - - %lld second(s) - %lld sekund(y) - No comment provided by engineer. - %lld seconds %lld sekund @@ -305,11 +315,6 @@ %u pominiętych wiadomości. No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) (nowy) @@ -320,57 +325,54 @@ (to urządzenie v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. + + **Create 1-time link**: to create and share a new invitation link. **Dodaj kontakt**: aby utworzyć nowy link z zaproszeniem lub połączyć się za pomocą otrzymanego linku. No comment provided by engineer. - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Dodaj nowy kontakt**: aby stworzyć swój jednorazowy kod QR lub link dla kontaktu. - No comment provided by engineer. - **Create group**: to create a new group. **Utwórz grupę**: aby utworzyć nową grupę. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. - **Bardziej prywatny**: sprawdzanie nowych wiadomości co 20 minut. Token urządzenia jest współdzielony z serwerem SimpleX Chat, ale nie informacje o liczbie kontaktów lub wiadomości. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. + **Bardziej prywatny**: sprawdzanie nowych wiadomości odbywa się co 20 minut. Współdzielony z serwerem SimpleX Chat jest token urządzenia, lecz nie informacje o liczbie kontaktów lub wiadomości. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). - **Najbardziej prywatny**: nie korzystaj z serwera powiadomień SimpleX Chat, sprawdzaj wiadomości okresowo w tle (zależy jak często korzystasz z aplikacji). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. + **Najbardziej prywatny**: nie korzystaj z serwera powiadomień SimpleX Chat, wiadomości sprawdzane są co jakiś czas w tle (zależne od tego jak często korzystasz z aplikacji). No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + *Uwaga*: w celach bezpieczeństwa użycie tej samej bazy danych na dwóch różnych urządzeniach spowoduje brak możliwości odszyfrowywania wiadomości z Twoich połączeń. No comment provided by engineer. **Please note**: you will NOT be able to recover or change passphrase if you lose it. - **Uwaga**: NIE będziesz w stanie odzyskać lub zmienić hasła, jeśli je stracisz. + **Uwaga**: NIE będziesz w stanie odzyskać lub zmienić kodu dostępu, jeśli go stracisz. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. - **Zalecane**: token urządzenia i powiadomienia są wysyłane do serwera powiadomień SimpleX Chat, ale nie treść wiadomości, rozmiar lub od kogo jest. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. + **Zalecane**: do serwera powiadomień SimpleX Chat wysyłany jest token urządzenia i powiadomienia, lecz nie treść wiadomości, jej rozmiar lub od kogo ona jest. + No comment provided by engineer. + + + **Scan / Paste link**: to connect via a link you received. + **Zeskanuj / Wklej link**: aby połączyć się za pomocą otrzymanego linku. No comment provided by engineer. **Warning**: Instant push notifications require passphrase saved in Keychain. - **Uwaga**: Natychmiastowe powiadomienia push wymagają hasła zapisanego w Keychain. + **Uwaga**: Natychmiastowe powiadomienia push wymagają zapisania kodu dostępu w Keychain. No comment provided by engineer. **Warning**: the archive will be removed. + **Ostrzeżenie**: archiwum zostanie usunięte. No comment provided by engineer. @@ -388,11 +390,6 @@ \*pogrubiony* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -429,11 +426,6 @@ - historia edycji. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 sek @@ -447,7 +439,8 @@ 1 day 1 dzień - time interval + delete after time +time interval 1 hour @@ -462,12 +455,29 @@ 1 month 1 miesiąc - time interval + delete after time +time interval 1 week 1 tydzień - time interval + delete after time +time interval + + + 1 year + 1 roku + delete after time + + + 1-time link + link jednorazowy + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + Link jednorazowy może być użyty *tylko z jednym kontaktem* - udostępnij go osobiście lub przez dowolny komunikator. + No comment provided by engineer. 5 minutes @@ -484,11 +494,6 @@ 30 sekund No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -538,31 +543,32 @@ Przerwać zmianę adresu? No comment provided by engineer. - - About SimpleX - O SimpleX - No comment provided by engineer. - About SimpleX Chat O SimpleX Chat No comment provided by engineer. - - About SimpleX address - O adresie SimpleX + + About operators + O operatorach No comment provided by engineer. - - Accent color - Kolor akcentu + + Accent + Akcent No comment provided by engineer. Accept Akceptuj accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Zaakceptuj warunki + No comment provided by engineer. Accept connection request? @@ -577,21 +583,47 @@ Accept incognito Akceptuj incognito - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + Zaakceptowano warunki + No comment provided by engineer. + + + Acknowledged + Potwierdzono + No comment provided by engineer. + + + Acknowledgement errors + Błędy potwierdzenia + No comment provided by engineer. + + + Active + Aktywne + token status text + + + Active connections + Aktywne połączenia + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Dodaj adres do swojego profilu, aby Twoje kontakty mogły go udostępnić innym osobom. Aktualizacja profilu zostanie wysłana do Twoich kontaktów. No comment provided by engineer. - - Add contact - Dodaj kontakt + + Add friends + Dodaj znajomych No comment provided by engineer. - - Add preset servers - Dodaj gotowe serwery + + Add list + Dodaj listę No comment provided by engineer. @@ -599,14 +631,19 @@ Dodaj profil No comment provided by engineer. + + Add server + Dodaj serwer + No comment provided by engineer. + Add servers by scanning QR codes. Dodaj serwery, skanując kody QR. No comment provided by engineer. - - Add server… - Dodaj serwer… + + Add team members + Dodaj członków zespołu No comment provided by engineer. @@ -614,11 +651,46 @@ Dodaj do innego urządzenia No comment provided by engineer. + + Add to list + Dodaj do listy + No comment provided by engineer. + Add welcome message Dodaj wiadomość powitalną No comment provided by engineer. + + Add your team members to the conversations. + Dodaj członków zespołu do konwersacji. + No comment provided by engineer. + + + Added media & file servers + Dodano serwery multimediów i plików + No comment provided by engineer. + + + Added message servers + Dodano serwery wiadomości + No comment provided by engineer. + + + Additional accent + Dodatkowy akcent + No comment provided by engineer. + + + Additional accent 2 + Dodatkowy akcent 2 + No comment provided by engineer. + + + Additional secondary + Dodatkowy drugorzędny + No comment provided by engineer. + Address Adres @@ -629,8 +701,19 @@ Zmiana adresu zostanie przerwana. Użyty zostanie stary adres odbiorczy. No comment provided by engineer. + + Address or 1-time link? + Adres czy jednorazowy link? + No comment provided by engineer. + + + Address settings + Ustawienia adresu + No comment provided by engineer. + Admins can block a member for all. + Administratorzy mogą blokować członka dla wszystkich. No comment provided by engineer. @@ -643,6 +726,16 @@ Zaawansowane ustawienia sieci No comment provided by engineer. + + Advanced settings + Zaawansowane ustawienia + No comment provided by engineer. + + + All + Wszystko + No comment provided by engineer. + All app data is deleted. Wszystkie dane aplikacji są usunięte. @@ -653,16 +746,31 @@ Wszystkie czaty i wiadomości zostaną usunięte - nie można tego cofnąć! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + Wszystkie rozmowy zostaną usunięte z listy %@, a lista usunięta. + alert message + All data is erased when it is entered. Wszystkie dane są usuwane po jego wprowadzeniu. No comment provided by engineer. + + All data is kept private on your device. + Wszystkie dane są prywatne na Twoim urządzeniu. + No comment provided by engineer. + All group members will remain connected. Wszyscy członkowie grupy pozostaną połączeni. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + Wszystkie wiadomości i pliki są wysyłane **z szyfrowaniem end-to-end**, z bezpieczeństwem postkwantowym w wiadomościach bezpośrednich. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! Wszystkie wiadomości zostaną usunięte – nie można tego cofnąć! @@ -678,6 +786,20 @@ Wszystkie nowe wiadomości z %@ zostaną ukryte! No comment provided by engineer. + + All profiles + Wszystkie profile + profile dropdown + + + All reports will be archived for you. + Wszystkie raporty zostaną dla Ciebie zarchiwizowane. + No comment provided by engineer. + + + All servers + No comment provided by engineer. + All your contacts will remain connected. Wszystkie Twoje kontakty pozostaną połączone. @@ -690,6 +812,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Wszystkie twoje kontakty, konwersacje i pliki będą bezpiecznie szyfrowane i wgrywane w kawałkach do skonfigurowanych przekaźników XFTP. No comment provided by engineer. @@ -702,11 +825,21 @@ Zezwalaj na połączenia tylko wtedy, gdy Twój kontakt na to pozwala. No comment provided by engineer. + + Allow calls? + Zezwolić na połączenia? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Zezwól na znikające wiadomości tylko wtedy, gdy Twój kontakt Ci na to pozwoli. No comment provided by engineer. + + Allow downgrade + Zezwól na obniżenie wersji + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Zezwalaj na nieodwracalne usuwanie wiadomości tylko wtedy, gdy Twój kontakt Ci na to pozwoli. (24 godziny) @@ -732,11 +865,26 @@ Zezwól na wysyłanie znikających wiadomości. No comment provided by engineer. + + Allow sharing + Zezwól na udostępnianie + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Zezwól na nieodwracalne usunięcie wysłanych wiadomości. (24 godziny) No comment provided by engineer. + + Allow to report messsages to moderators. + Zezwól na zgłaszanie wiadomości moderatorom. + No comment provided by engineer. + + + Allow to send SimpleX links. + Zezwól na wysyłanie linków SimpleX. + No comment provided by engineer. + Allow to send files and media. Pozwól na wysyłanie plików i mediów. @@ -797,6 +945,11 @@ Już dołączono do grupy! No comment provided by engineer. + + Always use private routing. + Zawsze używaj prywatnego trasowania. + No comment provided by engineer. + Always use relay Zawsze używaj przekaźnika @@ -807,11 +960,21 @@ Tworzony jest pusty profil czatu o podanej nazwie, a aplikacja otwiera się jak zwykle. No comment provided by engineer. + + Another reason + Inny powód + report reason + Answer call Odbierz połączenie No comment provided by engineer. + + Anybody can host servers. + Każdy może hostować serwery. + No comment provided by engineer. + App build: %@ Kompilacja aplikacji: %@ @@ -819,6 +982,7 @@ App data migration + Migracja danych aplikacji No comment provided by engineer. @@ -826,6 +990,11 @@ Aplikacja szyfruje nowe lokalne pliki (bez filmów). No comment provided by engineer. + + App group: + Grupa aplikacji: + No comment provided by engineer. + App icon Ikona aplikacji @@ -841,6 +1010,11 @@ Pin aplikacji został zastąpiony pinem samozniszczenia. No comment provided by engineer. + + App session + Sesja aplikacji + No comment provided by engineer. + App version Wersja aplikacji @@ -858,14 +1032,62 @@ Apply + Zastosuj + No comment provided by engineer. + + + Apply to + Zastosuj dla + No comment provided by engineer. + + + Archive + Archiwizuj + No comment provided by engineer. + + + Archive %lld reports? + Archiwizować %lld reports? + No comment provided by engineer. + + + Archive all reports? + Archiwizować wszystkie zgłoszenia? No comment provided by engineer. Archive and upload + Archiwizuj i prześlij + No comment provided by engineer. + + + Archive contacts to chat later. + Archiwizuj kontakty aby porozmawiać później. + No comment provided by engineer. + + + Archive report + Archiwizuj zgłoszenie + No comment provided by engineer. + + + Archive report? + Archiwizować zgłoszenie? + No comment provided by engineer. + + + Archive reports + Archiwizuj zgłoszenia + swipe action + + + Archived contacts + Zarchiwizowane kontakty No comment provided by engineer. Archiving database + Archiwizowanie bazy danych No comment provided by engineer. @@ -928,11 +1150,21 @@ Automatyczne akceptowanie obrazów No comment provided by engineer. + + Auto-accept settings + Ustawienia automatycznej akceptacji + alert title + Back Wstecz No comment provided by engineer. + + Background + Tło + No comment provided by engineer. + Bad desktop address Zły adres komputera @@ -948,16 +1180,59 @@ Zły hash wiadomości No comment provided by engineer. + + Better calls + Lepsze połączenia + No comment provided by engineer. + Better groups Lepsze grupy No comment provided by engineer. + + Better groups performance + No comment provided by engineer. + + + Better message dates. + Lepsze daty wiadomości. + No comment provided by engineer. + Better messages Lepsze wiadomości No comment provided by engineer. + + Better networking + Lepsze sieciowanie + No comment provided by engineer. + + + Better notifications + Lepsze powiadomienia + No comment provided by engineer. + + + Better privacy and security + No comment provided by engineer. + + + Better security ✅ + Lepsze zabezpieczenia ✅ + No comment provided by engineer. + + + Better user experience + Lepszy interfejs użytkownika + No comment provided by engineer. + + + Black + Czarny + No comment provided by engineer. + Block Zablokuj @@ -993,6 +1268,16 @@ Zablokowany przez admina No comment provided by engineer. + + Blur for better privacy. + Rozmycie dla lepszej prywatności. + No comment provided by engineer. + + + Blur media + Rozmycie mediów + No comment provided by engineer. + Both you and your contact can add message reactions. Zarówno Ty, jak i Twój kontakt możecie dodawać reakcje wiadomości. @@ -1023,11 +1308,32 @@ Bułgarski, fiński, tajski i ukraiński – dzięki użytkownikom i [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + Adres firmowy + No comment provided by engineer. + + + Business chats + Czaty biznesowe + No comment provided by engineer. + + + Businesses + Firmy + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). Według profilu czatu (domyślnie) lub [według połączenia](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + No comment provided by engineer. + Call already ended! Połączenie już zakończone! @@ -1038,11 +1344,26 @@ Połączenia No comment provided by engineer. + + Calls prohibited! + Połączenia zakazane! + No comment provided by engineer. + Camera not available Kamera nie dostępna No comment provided by engineer. + + Can't call contact + Nie można zadzwonić do kontaktu + No comment provided by engineer. + + + Can't call member + Nie można zadzwonić do członka + No comment provided by engineer. + Can't invite contact! Nie można zaprosić kontaktu! @@ -1053,13 +1374,20 @@ Nie można zaprosić kontaktów! No comment provided by engineer. + + Can't message member + Nie można wysłać wiadomości do członka + No comment provided by engineer. + Cancel Anuluj - No comment provided by engineer. + alert action +alert button Cancel migration + Anuluj migrację No comment provided by engineer. @@ -1067,9 +1395,24 @@ Nie można uzyskać dostępu do pęku kluczy, aby zapisać hasło do bazy danych No comment provided by engineer. + + Cannot forward message + Nie można przekazać wiadomości + No comment provided by engineer. + Cannot receive file Nie można odebrać pliku + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Przekroczono pojemność - odbiorca nie otrzymał wcześniej wysłanych wiadomości. + snd error text + + + Cellular + Sieć komórkowa No comment provided by engineer. @@ -1077,6 +1420,15 @@ Zmień No comment provided by engineer. + + Change automatic message deletion? + alert title + + + Change chat profiles + Zmień profil czatu + authentication reason + Change database passphrase? Zmienić hasło bazy danych? @@ -1121,11 +1473,26 @@ Change self-destruct passcode Zmień pin samozniszczenia authentication reason - set passcode view +set passcode view - - Chat archive - Archiwum czatu + + Chat + Czat + No comment provided by engineer. + + + Chat already exists + Czat już istnieje + No comment provided by engineer. + + + Chat already exists! + Czat już istnieje! + No comment provided by engineer. + + + Chat colors + Kolory czatu No comment provided by engineer. @@ -1143,9 +1510,14 @@ Baza danych czatu usunięta No comment provided by engineer. + + Chat database exported + Wyeksportowano bazę danych czatów + No comment provided by engineer. + Chat database imported - Zaimportowano bazę danych czatu + Zaimportowano bazę danych czatów No comment provided by engineer. @@ -1163,8 +1535,14 @@ Czat został zatrzymany. Jeśli korzystałeś już z tej bazy danych na innym urządzeniu, powinieneś przenieść ją z powrotem przed rozpoczęciem czatu. No comment provided by engineer. + + Chat list + Lista czatów + No comment provided by engineer. + Chat migrated! + Czat zmigrowany! No comment provided by engineer. @@ -1172,15 +1550,50 @@ Preferencje czatu No comment provided by engineer. + + Chat preferences were changed. + Preferencje czatu zostały zmienione. + alert message + + + Chat profile + Profil użytkownika + No comment provided by engineer. + + + Chat theme + Motyw czatu + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + Czat zostanie usunięty dla wszystkich członków – tej operacji nie można cofnąć! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + Czat zostanie usunięty dla Ciebie – tej operacji nie można cofnąć! + No comment provided by engineer. + Chats Czaty No comment provided by engineer. + + Check messages every 20 min. + Sprawdzaj wiadomości co 20 min. + No comment provided by engineer. + + + Check messages when allowed. + Sprawdź wiadomości, gdy będzie to dopuszczone. + No comment provided by engineer. + Check server address and try again. Sprawdź adres serwera i spróbuj ponownie. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1189,6 +1602,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + Wybierz _Zmigruj z innego urządzenia_ na nowym urządzeniu i zeskanuj kod QR. No comment provided by engineer. @@ -1201,10 +1615,25 @@ Wybierz z biblioteki No comment provided by engineer. + + Chunks deleted + Fragmenty usunięte + No comment provided by engineer. + + + Chunks downloaded + Fragmenty pobrane + No comment provided by engineer. + + + Chunks uploaded + Fragmenty przesłane + No comment provided by engineer. + Clear Wyczyść - No comment provided by engineer. + swipe action Clear conversation @@ -1216,6 +1645,14 @@ Wyczyścić rozmowę? No comment provided by engineer. + + Clear group? + No comment provided by engineer. + + + Clear or delete group? + No comment provided by engineer. + Clear private notes? Wyczyścić prywatne notatki? @@ -1226,11 +1663,20 @@ Wyczyść weryfikację No comment provided by engineer. - - Colors - Kolory + + Color chats with the new themes. + Koloruj czaty z nowymi motywami. No comment provided by engineer. + + Color mode + Tryb koloru + No comment provided by engineer. + + + Community guidelines violation + report reason + Compare file Porównaj plik @@ -1241,11 +1687,52 @@ Porównaj kody bezpieczeństwa ze swoimi kontaktami. No comment provided by engineer. + + Completed + Zakończono + No comment provided by engineer. + + + Conditions accepted on: %@. + Warunki zaakceptowane dnia: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + Warunki zostały zaakceptowane przez operatora(-ów): **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + Warunki zostały już zaakceptowane przez tego(-ych) operatora(-ów): **%@**. + No comment provided by engineer. + + + Conditions of use + Warunki użytkowania + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + No comment provided by engineer. + Configure ICE servers Skonfiguruj serwery ICE No comment provided by engineer. + + Configure server operators + No comment provided by engineer. + Confirm Potwierdź @@ -1256,13 +1743,24 @@ Potwierdź Pin No comment provided by engineer. + + Confirm contact deletion? + Potwierdzić usunięcie kontaktu? + No comment provided by engineer. + Confirm database upgrades Potwierdź aktualizacje bazy danych No comment provided by engineer. + + Confirm files from unknown servers. + Potwierdzaj pliki z nieznanych serwerów. + No comment provided by engineer. + Confirm network settings + Potwierdź ustawienia sieciowe No comment provided by engineer. @@ -1277,12 +1775,18 @@ Confirm that you remember database passphrase to migrate it. + Potwierdź, że pamiętasz hasło do bazy danych, aby ją zmigrować. No comment provided by engineer. Confirm upload + Potwierdź wgranie No comment provided by engineer. + + Confirmed + token status text + Connect Połącz @@ -1303,6 +1807,11 @@ Połącz do komputera No comment provided by engineer. + + Connect to your friends faster. + Szybciej łącz się ze znajomymi. + No comment provided by engineer. + Connect to yourself? Połączyć się ze sobą? @@ -1342,16 +1851,31 @@ To jest twój jednorazowy link! Połącz z %@ No comment provided by engineer. + + Connected + Połączony + No comment provided by engineer. + Connected desktop Połączony komputer No comment provided by engineer. + + Connected servers + Połączone serwery + No comment provided by engineer. + Connected to desktop Połączony do komputera No comment provided by engineer. + + Connecting + Łączenie + No comment provided by engineer. + Connecting to server… Łączenie z serwerem… @@ -1362,6 +1886,11 @@ To jest twój jednorazowy link! Łączenie z serwerem... (błąd: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + Łączenie z kontaktem, poczekaj lub sprawdź później! + No comment provided by engineer. + Connecting to desktop Łączenie z komputerem @@ -1372,6 +1901,15 @@ To jest twój jednorazowy link! Połączenie No comment provided by engineer. + + Connection and servers status. + Stan połączenia i serwerów. + No comment provided by engineer. + + + Connection blocked + No comment provided by engineer. + Connection error Błąd połączenia @@ -1382,11 +1920,33 @@ To jest twój jednorazowy link! Błąd połączenia (UWIERZYTELNIANIE) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + No comment provided by engineer. + + + Connection not ready. + No comment provided by engineer. + + + Connection notifications + Powiadomienia o połączeniu + No comment provided by engineer. + Connection request sent! Prośba o połączenie wysłana! No comment provided by engineer. + + Connection requires encryption renegotiation. + No comment provided by engineer. + + + Connection security + No comment provided by engineer. + Connection terminated Połączenie zakończone @@ -1397,6 +1957,16 @@ To jest twój jednorazowy link! Czas połączenia minął No comment provided by engineer. + + Connection with desktop stopped + Połączenie z komputerem zakończone + No comment provided by engineer. + + + Connections + Połączenia + No comment provided by engineer. + Contact allows Kontakt pozwala @@ -1407,6 +1977,11 @@ To jest twój jednorazowy link! Kontakt już istnieje No comment provided by engineer. + + Contact deleted! + Kontakt usunięty! + No comment provided by engineer. + Contact hidden: Kontakt ukryty: @@ -1417,9 +1992,9 @@ To jest twój jednorazowy link! Kontakt jest połączony notification - - Contact is not connected yet! - Kontakt nie jest jeszcze połączony! + + Contact is deleted. + Kontakt jest usunięty. No comment provided by engineer. @@ -1432,6 +2007,11 @@ To jest twój jednorazowy link! Preferencje kontaktu No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + Kontakt zostanie usunięty – nie można tego cofnąć! + No comment provided by engineer. + Contacts Kontakty @@ -1442,21 +2022,40 @@ To jest twój jednorazowy link! Kontakty mogą oznaczać wiadomości do usunięcia; będziesz mógł je zobaczyć. No comment provided by engineer. + + Content violates conditions of use + blocking reason + Continue Kontynuuj No comment provided by engineer. + + Conversation deleted! + Rozmowa usunięta! + No comment provided by engineer. + Copy Kopiuj - chat item action + No comment provided by engineer. + + + Copy error + Kopiuj błąd + No comment provided by engineer. Core version: v%@ Wersja rdzenia: v%@ No comment provided by engineer. + + Corner + Róg + No comment provided by engineer. + Correct name to %@? Poprawić imię na %@? @@ -1467,6 +2066,10 @@ To jest twój jednorazowy link! Utwórz No comment provided by engineer. + + Create 1-time link + No comment provided by engineer. + Create SimpleX address Utwórz adres SimpleX @@ -1477,11 +2080,6 @@ To jest twój jednorazowy link! Utwórz grupę używając losowego profilu. No comment provided by engineer. - - Create an address to let people connect with you. - Utwórz adres, aby ludzie mogli się z Tobą połączyć. - No comment provided by engineer. - Create file Utwórz plik @@ -1502,6 +2100,10 @@ To jest twój jednorazowy link! Utwórz link No comment provided by engineer. + + Create list + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 Utwórz nowy profil w [aplikacji desktopowej](https://simplex.chat/downloads/). 💻 @@ -1527,6 +2129,11 @@ To jest twój jednorazowy link! Utwórz swój profil No comment provided by engineer. + + Created + Utworzono + No comment provided by engineer. + Created at Utworzony o @@ -1537,13 +2144,9 @@ To jest twój jednorazowy link! Utworzony o: %@ copied message info - - Created on %@ - Utworzony w dniu %@ - No comment provided by engineer. - Creating archive link + Tworzenie linku archiwum No comment provided by engineer. @@ -1556,11 +2159,20 @@ To jest twój jednorazowy link! Aktualny Pin No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + No comment provided by engineer. + Current passphrase… Obecne hasło… No comment provided by engineer. + + Current profile + Bieżący profil + No comment provided by engineer. + Currently maximum supported file size is %@. Obecnie maksymalna obsługiwana wielkość pliku wynosi %@. @@ -1571,11 +2183,25 @@ To jest twój jednorazowy link! Niestandardowy czas No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + + + Customize theme + Dostosuj motyw + No comment provided by engineer. + Dark Ciemny No comment provided by engineer. + + Dark mode colors + Kolory ciemnego trybu + No comment provided by engineer. + Database ID ID bazy danych @@ -1674,6 +2300,11 @@ To jest twój jednorazowy link! Baza danych zostanie zmigrowana po ponownym uruchomieniu aplikacji No comment provided by engineer. + + Debug delivery + Dostarczenie debugowania + No comment provided by engineer. + Decentralized Zdecentralizowane @@ -1687,18 +2318,19 @@ To jest twój jednorazowy link! Delete Usuń - chat item action + alert action +swipe action + + + Delete %lld messages of members? + Usunąć %lld wiadomości członków? + No comment provided by engineer. Delete %lld messages? Usunąć %lld wiadomości? No comment provided by engineer. - - Delete Contact - Usuń Kontakt - No comment provided by engineer. - Delete address Usuń adres @@ -1724,14 +2356,12 @@ To jest twój jednorazowy link! Usuń i powiadom kontakt No comment provided by engineer. - - Delete archive - Usuń archiwum + + Delete chat No comment provided by engineer. - - Delete chat archive? - Usunąć archiwum czatu? + + Delete chat messages from your device. No comment provided by engineer. @@ -1744,6 +2374,10 @@ To jest twój jednorazowy link! Usunąć profil czatu? No comment provided by engineer. + + Delete chat? + No comment provided by engineer. + Delete connection Usuń połączenie @@ -1754,11 +2388,9 @@ To jest twój jednorazowy link! Usuń kontakt No comment provided by engineer. - - Delete contact? -This cannot be undone! - Usunąć kontakt? -To nie może być cofnięte! + + Delete contact? + Usunąć kontakt? No comment provided by engineer. @@ -1768,6 +2400,7 @@ To nie może być cofnięte! Delete database from this device + Usuń bazę danych z tego urządzenia No comment provided by engineer. @@ -1820,6 +2453,10 @@ To nie może być cofnięte! Usunąć link? No comment provided by engineer. + + Delete list? + alert title + Delete member message? Usunąć wiadomość członka? @@ -1833,7 +2470,7 @@ To nie może być cofnięte! Delete messages Usuń wiadomości - No comment provided by engineer. + alert button Delete messages after @@ -1850,9 +2487,8 @@ To nie może być cofnięte! Usunąć starą bazę danych? No comment provided by engineer. - - Delete pending connection - Usuń oczekujące połączenie + + Delete or moderate up to 200 messages. No comment provided by engineer. @@ -1870,11 +2506,30 @@ To nie może być cofnięte! Usuń kolejkę server test step + + Delete report + No comment provided by engineer. + + + Delete up to 20 messages at once. + Usuń do 20 wiadomości na raz. + No comment provided by engineer. + Delete user profile? Usunąć profil użytkownika? No comment provided by engineer. + + Delete without notification + Usuń bez powiadomienia + No comment provided by engineer. + + + Deleted + Usunięto + No comment provided by engineer. + Deleted at Usunięto o @@ -1885,6 +2540,15 @@ To nie może być cofnięte! Usunięto o: %@ copied message info + + Deletion errors + Błędy usuwania + No comment provided by engineer. + + + Delivered even when Apple drops them. + No comment provided by engineer. + Delivery Dostarczenie @@ -1920,11 +2584,41 @@ To nie może być cofnięte! Urządzenia komputerowe No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + Adres serwera docelowego %@ jest niekompatybilny z ustawieniami serwera przekazującego %@. + No comment provided by engineer. + + + Destination server error: %@ + Błąd docelowego serwera: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + Wersja serwera docelowego %@ jest niekompatybilna z serwerem przekierowującym %@. + No comment provided by engineer. + + + Detailed statistics + Szczegółowe statystyki + No comment provided by engineer. + + + Details + Szczegóły + No comment provided by engineer. + Develop Deweloperskie No comment provided by engineer. + + Developer options + Opcje deweloperskie + No comment provided by engineer. + Developer tools Narzędzia deweloperskie @@ -1955,8 +2649,12 @@ To nie może być cofnięte! Bezpośrednie wiadomości chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + No comment provided by engineer. + + + Direct messages between members are prohibited. Bezpośrednie wiadomości między członkami są zabronione w tej grupie. No comment provided by engineer. @@ -1970,11 +2668,24 @@ To nie może być cofnięte! Wyłącz blokadę SimpleX authentication reason + + Disable automatic message deletion? + alert title + + + Disable delete messages + alert button + Disable for all Wyłącz dla wszystkich No comment provided by engineer. + + Disabled + Wyłączony + No comment provided by engineer. + Disappearing message Znikająca wiadomość @@ -1990,8 +2701,8 @@ To nie może być cofnięte! Znikające wiadomości są zabronione na tym czacie. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. Znikające wiadomości są zabronione w tej grupie. No comment provided by engineer. @@ -2025,11 +2736,21 @@ To nie może być cofnięte! Odkryj przez sieć lokalną No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + NIE wysyłaj wiadomości bezpośrednio, nawet jeśli serwer docelowy nie obsługuje prywatnego trasowania. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. NIE używaj SimpleX do połączeń alarmowych. No comment provided by engineer. + + Do NOT use private routing. + NIE używaj prywatnego trasowania. + No comment provided by engineer. + Do it later Zrób to później @@ -2040,6 +2761,15 @@ To nie może być cofnięte! Nie wysyłaj historii do nowych członków. No comment provided by engineer. + + Do not use credentials with proxy. + Nie używaj danych logowania do proxy. + No comment provided by engineer. + + + Documents: + No comment provided by engineer. + Don't create address Nie twórz adresu @@ -2050,18 +2780,38 @@ To nie może być cofnięte! Nie włączaj No comment provided by engineer. + + Don't miss important messages. + No comment provided by engineer. + Don't show again Nie pokazuj ponownie No comment provided by engineer. + + Done + No comment provided by engineer. + Downgrade and open chat Obniż wersję i otwórz czat No comment provided by engineer. + + Download + Pobierz + alert button +chat item action + + + Download errors + Błędy pobierania + No comment provided by engineer. + Download failed + Pobieranie nie udane No comment provided by engineer. @@ -2069,12 +2819,29 @@ To nie może być cofnięte! Pobierz plik server test step + + Download files + Pobierz pliki + alert action + + + Downloaded + Pobrane + No comment provided by engineer. + + + Downloaded files + Pobrane pliki + No comment provided by engineer. + Downloading archive + Pobieranie archiwum No comment provided by engineer. Downloading link details + Pobieranie szczegółów linku No comment provided by engineer. @@ -2087,6 +2854,10 @@ To nie może być cofnięte! Czas trwania No comment provided by engineer. + + E2E encrypted notifications. + No comment provided by engineer. + Edit Edytuj @@ -2107,6 +2878,10 @@ To nie może być cofnięte! Włącz (zachowaj nadpisania) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + No comment provided by engineer. + Enable SimpleX Lock Włącz blokadę SimpleX @@ -2120,7 +2895,7 @@ To nie może być cofnięte! Enable automatic message deletion? Czy włączyć automatyczne usuwanie wiadomości? - No comment provided by engineer. + alert title Enable camera access @@ -2134,6 +2909,7 @@ To nie może być cofnięte! Enable in direct chats (BETA)! + Włącz w czatach bezpośrednich (BETA)! No comment provided by engineer. @@ -2166,6 +2942,16 @@ To nie może być cofnięte! Włącz pin samodestrukcji set passcode view + + Enabled + Włączony + No comment provided by engineer. + + + Enabled for + Włączony dla + No comment provided by engineer. + Encrypt Szyfruj @@ -2236,6 +3022,10 @@ To nie może być cofnięte! Renegocjacja szyfrowania nie powiodła się. No comment provided by engineer. + + Encryption renegotiation in progress. + No comment provided by engineer. + Enter Passcode Wprowadź Pin @@ -2253,6 +3043,7 @@ To nie może być cofnięte! Enter passphrase + Wprowadź hasło No comment provided by engineer. @@ -2300,30 +3091,34 @@ To nie może być cofnięte! Błąd przerwania zmiany adresu No comment provided by engineer. + + Error accepting conditions + alert title + Error accepting contact request Błąd przyjmowania prośby o kontakt No comment provided by engineer. - - Error accessing database file - Błąd dostępu do pliku bazy danych - No comment provided by engineer. - Error adding member(s) Błąd dodawania członka(ów) No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + alert title Error changing address Błąd zmiany adresu No comment provided by engineer. + + Error changing connection profile + Błąd zmiany połączenia profilu + No comment provided by engineer. + Error changing role Błąd zmiany roli @@ -2334,6 +3129,20 @@ To nie może być cofnięte! Błąd zmiany ustawienia No comment provided by engineer. + + Error changing to incognito! + Błąd zmiany na incognito! + No comment provided by engineer. + + + Error checking token status + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + Błąd połączenia z serwerem przekierowania %@. Spróbuj ponownie później. + No comment provided by engineer. + Error creating address Błąd tworzenia adresu @@ -2349,6 +3158,10 @@ To nie może być cofnięte! Błąd tworzenia linku grupy No comment provided by engineer. + + Error creating list + alert title + Error creating member contact Błąd tworzenia kontaktu członka @@ -2364,6 +3177,10 @@ To nie może być cofnięte! Błąd tworzenia profilu! No comment provided by engineer. + + Error creating report + No comment provided by engineer. + Error decrypting file Błąd odszyfrowania pliku @@ -2384,11 +3201,6 @@ To nie może być cofnięte! Błąd usuwania połączenia No comment provided by engineer. - - Error deleting contact - Błąd usuwania kontaktu - No comment provided by engineer. - Error deleting database Błąd usuwania bazy danych @@ -2411,6 +3223,7 @@ To nie może być cofnięte! Error downloading the archive + Błąd pobierania archiwum No comment provided by engineer. @@ -2433,6 +3246,11 @@ To nie może być cofnięte! Błąd eksportu bazy danych czatu No comment provided by engineer. + + Error exporting theme: %@ + Błąd eksportowania motywu: %@ + No comment provided by engineer. + Error importing chat database Błąd importu bazy danych czatu @@ -2443,9 +3261,13 @@ To nie może być cofnięte! Błąd dołączenia do grupy No comment provided by engineer. - - Error loading %@ servers - Błąd ładowania %@ serwerów + + Error loading servers + alert title + + + Error migrating settings + Błąd migracji ustawień No comment provided by engineer. @@ -2456,16 +3278,34 @@ To nie może być cofnięte! Error receiving file Błąd odbioru pliku + alert title + + + Error reconnecting server + Błąd ponownego łączenia z serwerem No comment provided by engineer. + + Error reconnecting servers + Błąd ponownego łączenia serwerów + No comment provided by engineer. + + + Error registering for notifications + alert title + Error removing member Błąd usuwania członka No comment provided by engineer. - - Error saving %@ servers - Błąd zapisu %@ serwerów + + Error reordering lists + alert title + + + Error resetting statistics + Błąd resetowania statystyk No comment provided by engineer. @@ -2473,6 +3313,10 @@ To nie może być cofnięte! Błąd zapisu serwerów ICE No comment provided by engineer. + + Error saving chat list + alert title + Error saving group profile Błąd zapisu profilu grupy @@ -2488,8 +3332,13 @@ To nie może być cofnięte! Błąd zapisu hasła do pęku kluczy No comment provided by engineer. + + Error saving servers + alert title + Error saving settings + Błąd zapisywania ustawień when migrating @@ -2532,16 +3381,25 @@ To nie może być cofnięte! Błąd zatrzymania czatu No comment provided by engineer. + + Error switching profile + Błąd zmiany profilu + No comment provided by engineer. + Error switching profile! Błąd przełączania profilu! - No comment provided by engineer. + alertTitle Error synchronizing connection Błąd synchronizacji połączenia No comment provided by engineer. + + Error testing server connection + No comment provided by engineer. + Error updating group link Błąd aktualizacji linku grupy @@ -2552,6 +3410,10 @@ To nie może być cofnięte! Błąd aktualizacji wiadomości No comment provided by engineer. + + Error updating server + alert title + Error updating settings Błąd aktualizacji ustawień @@ -2564,10 +3426,12 @@ To nie może być cofnięte! Error uploading the archive + Błąd wgrywania archiwum No comment provided by engineer. Error verifying passphrase: + Błąd weryfikowania hasła: No comment provided by engineer. @@ -2578,7 +3442,9 @@ To nie może być cofnięte! Error: %@ Błąd: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2590,6 +3456,15 @@ To nie może być cofnięte! Błąd: brak pliku bazy danych No comment provided by engineer. + + Errors + Błędy + No comment provided by engineer. + + + Errors in servers configuration. + servers error + Even when disabled in the conversation. Nawet po wyłączeniu w rozmowie. @@ -2605,6 +3480,10 @@ To nie może być cofnięte! Rozszerz chat item action + + Expired + token status text + Export database Eksportuj bazę danych @@ -2615,6 +3494,11 @@ To nie może być cofnięte! Błąd eksportu: No comment provided by engineer. + + Export theme + Eksportuj motyw + No comment provided by engineer. + Exported database archive. Wyeksportowane archiwum bazy danych. @@ -2622,6 +3506,7 @@ To nie może być cofnięte! Exported file doesn't exist + Wyeksportowany plik nie istnieje No comment provided by engineer. @@ -2639,16 +3524,65 @@ To nie może być cofnięte! Szybko i bez czekania aż nadawca będzie online! No comment provided by engineer. + + Faster deletion of groups. + No comment provided by engineer. + Faster joining and more reliable messages. Szybsze dołączenie i bardziej niezawodne wiadomości. No comment provided by engineer. + + Faster sending messages. + No comment provided by engineer. + Favorite Ulubione + swipe action + + + Favorites No comment provided by engineer. + + File error + Błąd pliku + file error alert title + + + File errors: +%@ + Błędy pliku: +%@ + alert message + + + File is blocked by server operator: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + Nie odnaleziono pliku - najprawdopodobniej plik został usunięty lub anulowany. + file error text + + + File server error: %@ + Błąd serwera plików: %@ + file error text + + + File status + Status pliku + No comment provided by engineer. + + + File status: %@ + Status pliku: %@ + copied message info + File will be deleted from servers. Plik zostanie usunięty z serwerów. @@ -2669,6 +3603,11 @@ To nie może być cofnięte! Plik: %@ No comment provided by engineer. + + Files + Pliki + No comment provided by engineer. + Files & media Pliki i media @@ -2679,11 +3618,16 @@ To nie może być cofnięte! Pliki i media chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. Pliki i media są zabronione w tej grupie. No comment provided by engineer. + + Files and media not allowed + Pliki i multimedia nie są dozwolone + No comment provided by engineer. + Files and media prohibited! Pliki i media zabronione! @@ -2696,10 +3640,12 @@ To nie może być cofnięte! Finalize migration + Dokończ migrację No comment provided by engineer. Finalize migration on another device. + Dokończ migrację na innym urządzeniu. No comment provided by engineer. @@ -2742,11 +3688,108 @@ To nie może być cofnięte! Naprawa nie jest obsługiwana przez członka grupy No comment provided by engineer. + + For all moderators + No comment provided by engineer. + + + For chat profile %@: + servers error + For console Dla konsoli No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + No comment provided by engineer. + + + For me + No comment provided by engineer. + + + For private routing + No comment provided by engineer. + + + For social media + No comment provided by engineer. + + + Forward + Przekaż dalej + chat item action + + + Forward %d message(s)? + Przekazać %d wiadomość(i)? + alert title + + + Forward and save messages + Przesyłaj dalej i zapisuj wiadomości + No comment provided by engineer. + + + Forward messages + Przekaż wiadomości + alert action + + + Forward messages without files? + Przekazać wiadomości bez plików? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + + + Forwarded + Przekazane dalej + No comment provided by engineer. + + + Forwarded from + Przekazane dalej od + No comment provided by engineer. + + + Forwarding %lld messages + Przekazywanie %lld wiadomości + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + Serwer przekazujący %@ nie mógł połączyć się z serwerem docelowym %@. Spróbuj ponownie później. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + Adres serwera przekierowującego jest niekompatybilny z ustawieniami sieciowymi: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + Wersja serwera przekierowującego jest niekompatybilna z ustawieniami sieciowymi: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + Serwer przekazujący: %1$@ +Błąd serwera docelowego: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + Serwer przekazujący: %1$@ +Błąd: %2$@ + snd error text + Found desktop Znaleziono komputer @@ -2767,11 +3810,6 @@ To nie może być cofnięte! Pełna nazwa (opcjonalna) No comment provided by engineer. - - Full name: - Pełna nazwa: - No comment provided by engineer. - Fully decentralized – visible only to members. W pełni zdecentralizowana – widoczna tylko dla członków. @@ -2792,6 +3830,20 @@ To nie może być cofnięte! GIF-y i naklejki No comment provided by engineer. + + Get notified when mentioned. + No comment provided by engineer. + + + Good afternoon! + Dzień dobry! + message preview + + + Good morning! + Dzień dobry! + message preview + Group Grupa @@ -2847,36 +3899,6 @@ To nie może być cofnięte! Linki grupowe No comment provided by engineer. - - Group members can add message reactions. - Członkowie grupy mogą dodawać reakcje wiadomości. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Członkowie grupy mogą nieodwracalnie usuwać wysłane wiadomości. (24 godziny) - No comment provided by engineer. - - - Group members can send direct messages. - Członkowie grupy mogą wysyłać bezpośrednie wiadomości. - No comment provided by engineer. - - - Group members can send disappearing messages. - Członkowie grupy mogą wysyłać znikające wiadomości. - No comment provided by engineer. - - - Group members can send files and media. - Członkowie grupy mogą wysyłać pliki i media. - No comment provided by engineer. - - - Group members can send voice messages. - Członkowie grupy mogą wysyłać wiadomości głosowe. - No comment provided by engineer. - Group message: Wiadomość grupowa: @@ -2917,11 +3939,19 @@ To nie może być cofnięte! Grupa zostanie usunięta dla Ciebie - nie można tego cofnąć! No comment provided by engineer. + + Groups + No comment provided by engineer. + Help Pomoc No comment provided by engineer. + + Help admins moderating their groups. + No comment provided by engineer. + Hidden Ukryte @@ -2972,10 +4002,17 @@ To nie może być cofnięte! Jak działa SimpleX No comment provided by engineer. + + How it affects privacy + No comment provided by engineer. + + + How it helps privacy + No comment provided by engineer. + How it works - Jak to działa - No comment provided by engineer. + alert button How to @@ -2994,6 +4031,7 @@ To nie może być cofnięte! Hungarian interface + Węgierski interfejs No comment provided by engineer. @@ -3001,6 +4039,11 @@ To nie może być cofnięte! Serwery ICE (po jednym na linię) No comment provided by engineer. + + IP address + Adres IP + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Jeśli nie możesz spotkać się osobiście, pokaż kod QR w rozmowie wideo lub udostępnij link. @@ -3041,8 +4084,8 @@ To nie może być cofnięte! Natychmiast No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Odporność na spam i nadużycia No comment provided by engineer. @@ -3063,10 +4106,22 @@ To nie może być cofnięte! Import failed + Import nie udał się + No comment provided by engineer. + + + Import theme + Importuj motyw No comment provided by engineer. Importing archive + Importowanie archiwum + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! No comment provided by engineer. @@ -3086,6 +4141,7 @@ To nie może być cofnięte! In order to continue, chat should be stopped. + Aby konturować, czat musi zostać zatrzymany. No comment provided by engineer. @@ -3093,6 +4149,19 @@ To nie może być cofnięte! W odpowiedzi na No comment provided by engineer. + + In-call sounds + Dźwięki w rozmowie + No comment provided by engineer. + + + Inappropriate content + report reason + + + Inappropriate profile + report reason + Incognito Incognito @@ -3163,6 +4232,11 @@ To nie może być cofnięte! Zainstaluj [SimpleX Chat na terminal](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Natychmiastowo + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3170,16 +4244,36 @@ To nie może być cofnięte! No comment provided by engineer. - - Instantly - Natychmiastowo - No comment provided by engineer. - Interface Interfejs No comment provided by engineer. + + Interface colors + Kolory interfejsu + No comment provided by engineer. + + + Invalid + token status text + + + Invalid (bad token) + token status text + + + Invalid (expired) + token status text + + + Invalid (unregistered) + token status text + + + Invalid (wrong topic) + token status text + Invalid QR code Nieprawidłowy kod QR @@ -3202,6 +4296,7 @@ To nie może być cofnięte! Invalid migration confirmation + Nieprawidłowe potwierdzenie migracji No comment provided by engineer. @@ -3217,7 +4312,7 @@ To nie może być cofnięte! Invalid server address! Nieprawidłowy adres serwera! - No comment provided by engineer. + alert title Invalid status @@ -3239,6 +4334,10 @@ To nie może być cofnięte! Zaproś członków No comment provided by engineer. + + Invite to chat + No comment provided by engineer. + Invite to group Zaproś do grupy @@ -3254,8 +4353,8 @@ To nie może być cofnięte! Nieodwracalne usuwanie wiadomości jest na tym czacie zabronione. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. Nieodwracalne usuwanie wiadomości jest w tej grupie zabronione. No comment provided by engineer. @@ -3280,6 +4379,11 @@ To nie może być cofnięte! 3. Połączenie zostało skompromitowane. No comment provided by engineer. + + It protects your IP address and connections. + Chroni Twój adres IP i połączenia. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Wygląda na to, że jesteś już połączony przez ten link. Jeśli tak nie jest, wystąpił błąd (%@). @@ -3298,7 +4402,7 @@ To nie może być cofnięte! Join Dołącz - No comment provided by engineer. + swipe action Join group @@ -3340,6 +4444,11 @@ To jest twój link do grupy %@! Keep Zachowaj + alert action + + + Keep conversation + Zachowaj rozmowę No comment provided by engineer. @@ -3350,7 +4459,7 @@ To jest twój link do grupy %@! Keep unused invitation? Zachować nieużyte zaproszenie? - No comment provided by engineer. + alert title Keep your connections @@ -3385,6 +4494,14 @@ To jest twój link do grupy %@! Leave Opuść + swipe action + + + Leave chat + No comment provided by engineer. + + + Leave chat? No comment provided by engineer. @@ -3427,6 +4544,18 @@ To jest twój link do grupy %@! Połączone komputery No comment provided by engineer. + + List + swipe action + + + List name and emoji should be different for all lists. + No comment provided by engineer. + + + List name... + No comment provided by engineer. + Live message! Wiadomość na żywo! @@ -3437,11 +4566,6 @@ To jest twój link do grupy %@! Wiadomości na żywo No comment provided by engineer. - - Local - Lokalnie - No comment provided by engineer. - Local name Nazwa lokalna @@ -3462,11 +4586,6 @@ To jest twój link do grupy %@! Tryb blokady No comment provided by engineer. - - Make a private connection - Nawiąż prywatne połączenie - No comment provided by engineer. - Make one message disappear Spraw, aby jedna wiadomość zniknęła @@ -3477,21 +4596,11 @@ To jest twój link do grupy %@! Ustaw profil jako prywatny! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Upewnij się, że adresy serwerów %@ są w poprawnym formacie, rozdzielone liniami i nie są zduplikowane (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Upewnij się, że adresy serwerów WebRTC ICE są w poprawnym formacie, rozdzielone liniami i nie są zduplikowane. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Wiele osób pytało: *jeśli SimpleX nie ma identyfikatora użytkownika, jak może dostarczać wiadomości?* - No comment provided by engineer. - Mark deleted for everyone Oznacz jako usunięty dla wszystkich @@ -3517,11 +4626,34 @@ To jest twój link do grupy %@! Maksymalnie 30 sekund, odbierane natychmiast. No comment provided by engineer. + + Media & file servers + Serwery mediów i plików + No comment provided by engineer. + + + Medium + Średni + blur media + Member Członek No comment provided by engineer. + + Member inactive + Członek nieaktywny + item status text + + + Member reports + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. Rola członka grupy zostanie zmieniona na "%@". Wszyscy członkowie grupy zostaną powiadomieni. @@ -3532,11 +4664,63 @@ To jest twój link do grupy %@! Rola członka zostanie zmieniona na "%@". Członek otrzyma nowe zaproszenie. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! Członek zostanie usunięty z grupy - nie można tego cofnąć! No comment provided by engineer. + + Members can add message reactions. + Członkowie grupy mogą dodawać reakcje wiadomości. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + Członkowie grupy mogą nieodwracalnie usuwać wysłane wiadomości. (24 godziny) + No comment provided by engineer. + + + Members can report messsages to moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + Członkowie grupy mogą wysyłać linki SimpleX. + No comment provided by engineer. + + + Members can send direct messages. + Członkowie grupy mogą wysyłać bezpośrednie wiadomości. + No comment provided by engineer. + + + Members can send disappearing messages. + Członkowie grupy mogą wysyłać znikające wiadomości. + No comment provided by engineer. + + + Members can send files and media. + Członkowie grupy mogą wysyłać pliki i media. + No comment provided by engineer. + + + Members can send voice messages. + Członkowie grupy mogą wysyłać wiadomości głosowe. + No comment provided by engineer. + + + Mention members 👋 + No comment provided by engineer. + + + Menus + Menu + No comment provided by engineer. + Message delivery error Błąd dostarczenia wiadomości @@ -3547,11 +4731,31 @@ To jest twój link do grupy %@! Potwierdzenia dostarczenia wiadomości! No comment provided by engineer. + + Message delivery warning + Ostrzeżenie dostarczenia wiadomości + item status text + Message draft Wersja robocza wiadomości No comment provided by engineer. + + Message forwarded + Wiadomość przekazana + item status text + + + Message may be delivered later if member becomes active. + Wiadomość może zostać dostarczona później jeśli członek stanie się aktywny. + item status description + + + Message queue info + Informacje kolejki wiadomości + No comment provided by engineer. + Message reactions Reakcje wiadomości @@ -3562,11 +4766,41 @@ To jest twój link do grupy %@! Reakcje wiadomości są zabronione na tym czacie. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. Reakcje wiadomości są zabronione w tej grupie. No comment provided by engineer. + + Message reception + Odebranie wiadomości + No comment provided by engineer. + + + Message servers + Serwery wiadomości + No comment provided by engineer. + + + Message shape + Kształt wiadomości + No comment provided by engineer. + + + Message source remains private. + Źródło wiadomości pozostaje prywatne. + No comment provided by engineer. + + + Message status + Status wiadomości + No comment provided by engineer. + + + Message status: %@ + Status wiadomości: %@ + copied message info + Message text Tekst wiadomości @@ -3574,6 +4808,7 @@ To jest twój link do grupy %@! Message too large + Wiadomość jest zbyt duża No comment provided by engineer. @@ -3591,36 +4826,63 @@ To jest twój link do grupy %@! Wiadomości od %@ zostaną pokazane! No comment provided by engineer. + + Messages in this chat will never be deleted. + alert message + + + Messages received + Otrzymane wiadomości + No comment provided by engineer. + + + Messages sent + Wysłane wiadomości + No comment provided by engineer. + + + Messages were deleted after you selected them. + Wiadomości zostały usunięte po wybraniu ich. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + Wiadomości, pliki i połączenia są chronione przez **szyfrowanie end-to-end** z doskonałym utajnianiem z wyprzedzeniem i odzyskiem po złamaniu. No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + Wiadomości, pliki i połączenia są chronione przez **kwantowo odporne szyfrowanie end-to-end** z doskonałym utajnianiem z wyprzedzeniem i odzyskiem po złamaniu. No comment provided by engineer. Migrate device + Zmigruj urządzenie No comment provided by engineer. Migrate from another device + Zmigruj z innego urządzenia No comment provided by engineer. Migrate here + Zmigruj tutaj No comment provided by engineer. Migrate to another device + Zmigruj do innego urządzenia No comment provided by engineer. Migrate to another device via QR code. + Zmigruj do innego urządzenia przez kod QR. No comment provided by engineer. Migrating + Migrowanie No comment provided by engineer. @@ -3630,6 +4892,7 @@ To jest twój link do grupy %@! Migration complete + Migracja zakończona No comment provided by engineer. @@ -3647,9 +4910,9 @@ To jest twój link do grupy %@! Migracja została zakończona No comment provided by engineer. - - Migrations: %@ - Migracje: %@ + + Migrations: + Migracje: No comment provided by engineer. @@ -3667,21 +4930,29 @@ To jest twój link do grupy %@! Moderowany o: %@ copied message info + + More + swipe action + More improvements are coming soon! Więcej ulepszeń już wkrótce! No comment provided by engineer. + + More reliable network connection. + Bardziej niezawodne połączenia sieciowe. + No comment provided by engineer. + + + More reliable notifications + No comment provided by engineer. + Most likely this connection is deleted. Najprawdopodobniej to połączenie jest usunięte. item status description - - Most likely this contact has deleted the connection with you. - Najprawdopodobniej ten kontakt usunął połączenie z Tobą. - No comment provided by engineer. - Multiple chat profiles Wiele profili czatu @@ -3690,7 +4961,11 @@ To jest twój link do grupy %@! Mute Wycisz - No comment provided by engineer. + notification label action + + + Mute all + notification label action Muted when inactive! @@ -3700,13 +4975,36 @@ To jest twój link do grupy %@! Name Nazwa - No comment provided by engineer. + swipe action Network & servers Sieć i serwery No comment provided by engineer. + + Network connection + Połączenie z siecią + No comment provided by engineer. + + + Network decentralization + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + Błąd sieciowy - wiadomość wygasła po wielu próbach wysłania jej. + snd error text + + + Network management + Zarządzenie sieciowe + No comment provided by engineer. + + + Network operator + No comment provided by engineer. + Network settings Ustawienia sieci @@ -3717,16 +5015,35 @@ To jest twój link do grupy %@! Status sieci No comment provided by engineer. + + New + token status text + New Passcode Nowy Pin No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Nowe poświadczenia SOCKS będą używane przy każdym uruchomieniu aplikacji. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Dla każdego serwera zostaną użyte nowe poświadczenia SOCKS. + No comment provided by engineer. + New chat Nowy czat No comment provided by engineer. + + New chat experience 🎉 + Nowe możliwości czatu 🎉 + No comment provided by engineer. + New contact request Nowa prośba o kontakt @@ -3737,11 +5054,6 @@ To jest twój link do grupy %@! Nowy kontakt: notification - - New database archive - Nowe archiwum bazy danych - No comment provided by engineer. - New desktop app! Nowa aplikacja desktopowa! @@ -3752,11 +5064,20 @@ To jest twój link do grupy %@! Nowa wyświetlana nazwa No comment provided by engineer. + + New events + notification + New in %@ Nowość w %@ No comment provided by engineer. + + New media options + Nowe opcje mediów + No comment provided by engineer. + New member role Nowa rola członka @@ -3772,6 +5093,10 @@ To jest twój link do grupy %@! Nowe hasło… No comment provided by engineer. + + New server + No comment provided by engineer. + No Nie @@ -3782,6 +5107,18 @@ To jest twój link do grupy %@! Brak hasła aplikacji Authentication unavailable + + No chats + No comment provided by engineer. + + + No chats found + No comment provided by engineer. + + + No chats in list %@ + No comment provided by engineer. + No contacts selected Nie wybrano kontaktów @@ -3802,6 +5139,11 @@ To jest twój link do grupy %@! Brak tokenu urządzenia! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + Brak bezpośredniego połączenia, wiadomość została przekazana przez administratora. + item status description + No filtered chats Brak filtrowanych czatów @@ -3817,21 +5159,101 @@ To jest twój link do grupy %@! Brak historii No comment provided by engineer. + + No info, try to reload + Brak informacji, spróbuj przeładować + No comment provided by engineer. + + + No media & file servers. + servers error + + + No message + No comment provided by engineer. + + + No message servers. + servers error + + + No network connection + Brak połączenia z siecią + No comment provided by engineer. + + + No permission to record speech + Brak zezwoleń do nagrania rozmowy + No comment provided by engineer. + + + No permission to record video + Brak zezwoleń do nagrania wideo + No comment provided by engineer. + No permission to record voice message Brak uprawnień do nagrywania wiadomości głosowej No comment provided by engineer. + + No push server + Lokalnie + No comment provided by engineer. + No received or sent files Brak odebranych lub wysłanych plików No comment provided by engineer. + + No servers for private message routing. + servers error + + + No servers to receive files. + servers error + + + No servers to receive messages. + servers error + + + No servers to send files. + servers error + + + No token! + alert title + + + No unread chats + No comment provided by engineer. + + + No user identifiers. + Brak identyfikatorów użytkownika. + No comment provided by engineer. + Not compatible! Nie kompatybilny! No comment provided by engineer. + + Notes + No comment provided by engineer. + + + Nothing selected + Nic nie jest zaznaczone + No comment provided by engineer. + + + Nothing to forward! + Nic do przekazania! + alert title + Notifications Powiadomienia @@ -3842,6 +5264,18 @@ To jest twój link do grupy %@! Powiadomienia są wyłączone! No comment provided by engineer. + + Notifications error + alert title + + + Notifications privacy + No comment provided by engineer. + + + Notifications status + alert title + Now admins can: - delete members' messages. @@ -3859,36 +5293,35 @@ To jest twój link do grupy %@! Off Wyłączony - No comment provided by engineer. + blur media Ok Ok - No comment provided by engineer. + alert button Old database Stara baza danych No comment provided by engineer. - - Old database archive - Stare archiwum bazy danych - No comment provided by engineer. - One-time invitation link Jednorazowy link zaproszenia No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Hosty onion będą wymagane do połączenia. Wymaga włączenia VPN. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Hosty onion będą wymagane do połączenia. +Wymaga włączenia VPN. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Hosty onion będą używane, gdy będą dostępne. Wymaga włączenia VPN. + + Onion hosts will be used when available. +Requires compatible VPN. + Hosty onion będą używane, gdy będą dostępne. +Wymaga włączenia VPN. No comment provided by engineer. @@ -3896,11 +5329,20 @@ To jest twój link do grupy %@! Hosty onion nie będą używane. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. Tylko urządzenia klienckie przechowują profile użytkowników, kontakty, grupy i wiadomości wysyłane za pomocą **2-warstwowego szyfrowania end-to-end**. No comment provided by engineer. + + Only delete conversation + Usuń tylko rozmowę + No comment provided by engineer. + Only group owners can change group preferences. Tylko właściciele grup mogą zmieniać preferencje grupy. @@ -3916,6 +5358,14 @@ To jest twój link do grupy %@! Tylko właściciele grup mogą włączyć wiadomości głosowe. No comment provided by engineer. + + Only sender and moderators see it + No comment provided by engineer. + + + Only you and moderators see it + No comment provided by engineer. + Only you can add message reactions. Tylko Ty możesz dodawać reakcje wiadomości. @@ -3969,13 +5419,17 @@ To jest twój link do grupy %@! Open Otwórz - No comment provided by engineer. + alert action Open Settings Otwórz Ustawienia No comment provided by engineer. + + Open changes + No comment provided by engineer. + Open chat Otwórz czat @@ -3986,32 +5440,44 @@ To jest twój link do grupy %@! Otwórz konsolę czatu authentication reason + + Open conditions + No comment provided by engineer. + Open group Grupa otwarta No comment provided by engineer. + + Open link? + alert title + Open migration to another device + Otwórz migrację na innym urządzeniu authentication reason - - Open user profiles - Otwórz profile użytkownika - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Otwarto źródłowy protokół i kod - każdy może uruchomić serwery. - No comment provided by engineer. - Opening app… Otwieranie aplikacji… No comment provided by engineer. + + Operator + No comment provided by engineer. + + + Operator server + alert title + + + Or import archive file + No comment provided by engineer. + Or paste archive link + Lub wklej link archiwum No comment provided by engineer. @@ -4021,6 +5487,7 @@ To jest twój link do grupy %@! Or securely share this file link + Lub bezpiecznie udostępnij ten link pliku No comment provided by engineer. @@ -4028,6 +5495,26 @@ To jest twój link do grupy %@! Lub pokaż ten kod No comment provided by engineer. + + Or to share privately + No comment provided by engineer. + + + Organize chats into lists + No comment provided by engineer. + + + Other + Inne + No comment provided by engineer. + + + Other file errors: +%@ + Inne błędy pliku: +%@ + alert message + PING count Liczba PINGÓW @@ -4063,6 +5550,11 @@ To jest twój link do grupy %@! Pin ustawiony! No comment provided by engineer. + + Password + Hasło + No comment provided by engineer. + Password to show Hasło do wyświetlenia @@ -4093,13 +5585,13 @@ To jest twój link do grupy %@! Wklej link, który otrzymałeś No comment provided by engineer. - - People can connect to you only via the links you share. - Ludzie mogą się z Tobą połączyć tylko poprzez linki, które udostępniasz. + + Pending + Oczekujące No comment provided by engineer. - - Periodically + + Periodic Okresowo No comment provided by engineer. @@ -4110,6 +5602,17 @@ To jest twój link do grupy %@! Picture-in-picture calls + Połączenia obraz-w-obrazie + No comment provided by engineer. + + + Play from the chat list. + Odtwórz z listy czatów. + No comment provided by engineer. + + + Please ask your contact to enable calls. + Poproś kontakt o włącznie połączeń. No comment provided by engineer. @@ -4117,6 +5620,13 @@ To jest twój link do grupy %@! Poproś Twój kontakt o włączenie wysyłania wiadomości głosowych. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + Sprawdź, czy telefon i komputer są podłączone do tej samej sieci lokalnej i czy zapora sieciowa komputera umożliwia połączenie. +Proszę podzielić się innymi problemami z deweloperami. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Sprawdź, czy użyłeś prawidłowego linku lub poproś Twój kontakt o przesłanie innego. @@ -4134,6 +5644,7 @@ To jest twój link do grupy %@! Please confirm that network settings are correct for this device. + Proszę potwierdzić, że ustawienia sieciowe są prawidłowe dla tego urządzenia. No comment provided by engineer. @@ -4150,12 +5661,12 @@ Błąd: %@ Please enter correct current passphrase. - Wprowadź poprawne aktualne hasło. + Wprowadź poprawny obecny kod dostępu. No comment provided by engineer. Please enter the previous password after restoring database backup. This action can not be undone. - Proszę podać poprzednie hasło po przywróceniu kopii zapasowej bazy danych. Tej czynności nie można cofnąć. + Proszę podać poprzedni kod dostępu po przywróceniu kopii zapasowej bazy danych. Tej czynności nie można cofnąć. No comment provided by engineer. @@ -4175,68 +5686,121 @@ Błąd: %@ Please store passphrase securely, you will NOT be able to access chat if you lose it. - Prosimy o bezpieczne przechowywanie hasła, w przypadku jego utraty NIE będzie można uzyskać dostępu do czatu. + Przechowuj kod dostępu w bezpieczny sposób, w przypadku jego utraty NIE będzie można uzyskać dostępu do czatu. No comment provided by engineer. Please store passphrase securely, you will NOT be able to change it if you lose it. - Prosimy o bezpieczne przechowywanie hasła, w przypadku jego utraty NIE będzie można go zmienić. + Przechowuj kod dostępu w bezpieczny sposób, w przypadku jego utraty NIE będzie można go zmienić. No comment provided by engineer. + + Please try to disable and re-enable notfications. + token info + + + Please wait for token activation to complete. + token info + + + Please wait for token to be registered. + token info + Polish interface Polski interfejs No comment provided by engineer. + + Port + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Możliwe, że odcisk palca certyfikatu w adresie serwera jest nieprawidłowy server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Zachowaj ostatnią wersję roboczą wiadomości wraz z załącznikami. No comment provided by engineer. - - Preset server - Wstępnie ustawiony serwer - No comment provided by engineer. - Preset server address Wstępnie ustawiony adres serwera No comment provided by engineer. + + Preset servers + No comment provided by engineer. + Preview Podgląd No comment provided by engineer. + + Previously connected servers + Wcześniej połączone serwery + No comment provided by engineer. + Privacy & security Prywatność i bezpieczeństwo No comment provided by engineer. + + Privacy for your customers. + No comment provided by engineer. + + + Privacy policy and conditions of use. + No comment provided by engineer. + Privacy redefined Redefinicja prywatności No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + No comment provided by engineer. + Private filenames Prywatne nazwy plików No comment provided by engineer. + + Private media file names. + No comment provided by engineer. + + + Private message routing + Trasowanie prywatnych wiadomości + No comment provided by engineer. + + + Private message routing 🚀 + Trasowanie prywatnych wiadomości🚀 + No comment provided by engineer. + Private notes Prywatne notatki name of notes to self + + Private routing + Prywatne trasowanie + No comment provided by engineer. + + + Private routing error + Błąd prywatnego trasowania + No comment provided by engineer. + Profile and server connections Profil i połączenia z serwerem @@ -4247,14 +5811,9 @@ Błąd: %@ Zdjęcie profilowe No comment provided by engineer. - - Profile name - Nazwa profilu - No comment provided by engineer. - - - Profile name: - Nazwa profilu: + + Profile images + Zdjęcia profilowe No comment provided by engineer. @@ -4262,10 +5821,15 @@ Błąd: %@ Hasło profilu No comment provided by engineer. + + Profile theme + Motyw profilu + No comment provided by engineer. + Profile update will be sent to your contacts. Aktualizacja profilu zostanie wysłana do Twoich kontaktów. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4287,6 +5851,15 @@ Błąd: %@ Zabroń reakcje wiadomości. No comment provided by engineer. + + Prohibit reporting messages to moderators. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + Zabroń wysyłania linków SimpleX. + No comment provided by engineer. + Prohibit sending direct messages to members. Zabroń wysyłania bezpośrednich wiadomości do członków. @@ -4307,11 +5880,23 @@ Błąd: %@ Zabroń wysyłania wiadomości głosowych. No comment provided by engineer. + + Protect IP address + Chroń adres IP + No comment provided by engineer. + Protect app screen Chroń ekran aplikacji No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + Chroni Twój adres IP przed przekaźnikami wiadomości wybranych przez Twoje kontakty. +Włącz w ustawianiach *Sieć i serwery* . + No comment provided by engineer. + Protect your chat profiles with a password! Chroń swoje profile czatu hasłem! @@ -4327,6 +5912,21 @@ Błąd: %@ Limit czasu protokołu na KB No comment provided by engineer. + + Proxied + Trasowane przez proxy + No comment provided by engineer. + + + Proxied servers + Serwery trasowane przez proxy + No comment provided by engineer. + + + Proxy requires password + Proxy wymaga hasła + No comment provided by engineer. + Push notifications Powiadomienia push @@ -4334,10 +5934,12 @@ Błąd: %@ Push server + Serwer Push No comment provided by engineer. Quantum resistant encryption + Kwantowo odporne szyfrowanie No comment provided by engineer. @@ -4345,6 +5947,11 @@ Błąd: %@ Oceń aplikację No comment provided by engineer. + + Reachable chat toolbar + Osiągalny pasek narzędzi czatu + No comment provided by engineer. + React… Reaguj… @@ -4353,33 +5960,28 @@ Błąd: %@ Read Czytaj - No comment provided by engineer. + swipe action Read more Przeczytaj więcej No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Przeczytaj więcej w [Podręczniku Użytkownika](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). Przeczytaj więcej w [Poradniku Użytkownika](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Przeczytaj więcej w [Podręczniku Użytkownika](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). Przeczytaj więcej w [Podręczniku Użytkownika](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - - Read more in our GitHub repository. - Przeczytaj więcej na naszym repozytorium GitHub. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). Przeczytaj więcej na naszym [repozytorium GitHub](https://github.com/simplex-chat/simplex-chat#readme). @@ -4390,6 +5992,11 @@ Błąd: %@ Potwierdzenia są wyłączone No comment provided by engineer. + + Receive errors + Błędy otrzymania + No comment provided by engineer. + Received at Otrzymane o @@ -4410,6 +6017,21 @@ Błąd: %@ Otrzymano wiadomość message info title + + Received messages + Otrzymane wiadomości + No comment provided by engineer. + + + Received reply + Otrzymano odpowiedź + No comment provided by engineer. + + + Received total + Otrzymano łącznie + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. Adres odbiorczy zostanie zmieniony na inny serwer. Zmiana adresu zostanie zakończona gdy nadawca będzie online. @@ -4430,16 +6052,46 @@ Błąd: %@ Ostania historia i ulepszony [bot adresowy](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + Odbiorca/y nie mogą zobaczyć od kogo jest ta wiadomość. + No comment provided by engineer. + Recipients see updates as you type them. Odbiorcy widzą aktualizacje podczas ich wpisywania. No comment provided by engineer. + + Reconnect + Połącz ponownie + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Połącz ponownie wszystkie połączone serwery, aby wymusić dostarczanie wiadomości. Wykorzystuje dodatkowy ruch. No comment provided by engineer. + + Reconnect all servers + Połącz ponownie wszystkie serwery + No comment provided by engineer. + + + Reconnect all servers? + Połączyć ponownie wszystkie serwery? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + Ponownie połącz z serwerem w celu wymuszenia dostarczenia wiadomości. Wykorzystuje to dodatkowy ruch. + No comment provided by engineer. + + + Reconnect server? + Połączyć ponownie serwer? + No comment provided by engineer. + Reconnect servers? Ponownie połączyć serwery? @@ -4460,10 +6112,23 @@ Błąd: %@ Zmniejszone zużycie baterii No comment provided by engineer. + + Register + No comment provided by engineer. + + + Register notification token? + token info + + + Registered + token status text + Reject Odrzuć - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4490,6 +6155,16 @@ Błąd: %@ Usuń No comment provided by engineer. + + Remove archive? + Usunąć archiwum? + No comment provided by engineer. + + + Remove image + Usuń obraz + No comment provided by engineer. + Remove member Usuń członka @@ -4527,10 +6202,12 @@ Błąd: %@ Repeat download + Powtórz pobieranie No comment provided by engineer. Repeat import + Powtórz importowanie No comment provided by engineer. @@ -4540,6 +6217,7 @@ Błąd: %@ Repeat upload + Powtórz wgrywanie No comment provided by engineer. @@ -4547,6 +6225,46 @@ Błąd: %@ Odpowiedz chat item action + + Report + chat item action + + + Report content: only group moderators will see it. + report reason + + + Report member profile: only group moderators will see it. + report reason + + + Report other: only group moderators will see it. + report reason + + + Report reason? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + report reason + + + Report violation: only group moderators will see it. + report reason + + + Report: %@ + report in notification + + + Reporting messages to moderators is prohibited. + No comment provided by engineer. + + + Reports + No comment provided by engineer. + Required Wymagane @@ -4557,16 +6275,41 @@ Błąd: %@ Resetuj No comment provided by engineer. + + Reset all hints + Zresetuj wszystkie wskazówki + No comment provided by engineer. + + + Reset all statistics + Resetuj wszystkie statystyki + No comment provided by engineer. + + + Reset all statistics? + Zresetować wszystkie statystyki? + No comment provided by engineer. + Reset colors Resetuj kolory No comment provided by engineer. + + Reset to app theme + Zresetuj do motywu aplikacji + No comment provided by engineer. + Reset to defaults Przywróć wartości domyślne No comment provided by engineer. + + Reset to user theme + Zresetuj do motywu użytkownika + No comment provided by engineer. + Restart the app to create a new chat profile Uruchom ponownie aplikację, aby utworzyć nowy profil czatu @@ -4607,9 +6350,8 @@ Błąd: %@ Ujawnij chat item action - - Revert - Przywrócić + + Review conditions No comment provided by engineer. @@ -4637,55 +6379,66 @@ Błąd: %@ Uruchom czat No comment provided by engineer. - - SMP servers - Serwery SMP + + SMP server + Serwer SMP + No comment provided by engineer. + + + SOCKS proxy + Proxy SOCKS + No comment provided by engineer. + + + Safely receive files + Bezpiecznie otrzymuj pliki No comment provided by engineer. Safer groups + Bezpieczniejsze grupy No comment provided by engineer. Save Zapisz - chat item action + alert button +chat item action Save (and notify contacts) Zapisz (i powiadom kontakty) - No comment provided by engineer. + alert button Save and notify contact Zapisz i powiadom kontakt - No comment provided by engineer. + alert button Save and notify group members Zapisz i powiadom członków grupy No comment provided by engineer. + + Save and reconnect + Zapisz i połącz ponownie + No comment provided by engineer. + Save and update group profile Zapisz i zaktualizuj profil grupowy No comment provided by engineer. - - Save archive - Zapisz archiwum - No comment provided by engineer. - - - Save auto-accept settings - Zapisz ustawienia automatycznej akceptacji - No comment provided by engineer. - Save group profile Zapisz profil grupy No comment provided by engineer. + + Save list + No comment provided by engineer. + Save passphrase and open chat Zapisz hasło i otwórz czat @@ -4699,7 +6452,7 @@ Błąd: %@ Save preferences? Zapisać preferencje? - No comment provided by engineer. + alert title Save profile password @@ -4714,28 +6467,53 @@ Błąd: %@ Save servers? Zapisać serwery? - No comment provided by engineer. - - - Save settings? - Zapisać ustawienia? - No comment provided by engineer. + alert title Save welcome message? Zapisać wiadomość powitalną? No comment provided by engineer. + + Save your profile? + Zapisać Twój profil? + alert title + + + Saved + Zapisane + No comment provided by engineer. + Saved WebRTC ICE servers will be removed Zapisane serwery WebRTC ICE zostaną usunięte No comment provided by engineer. + + Saved from + Zapisane od + No comment provided by engineer. + Saved message Zachowano wiadomość message info title + + Saving %lld messages + Zapisywanie %lld wiadomości + No comment provided by engineer. + + + Scale + Skaluj + No comment provided by engineer. + + + Scan / Paste link + Skanuj / Wklej link + No comment provided by engineer. + Scan QR code Zeskanuj kod QR @@ -4776,11 +6554,21 @@ Błąd: %@ Wyszukaj lub wklej link SimpleX No comment provided by engineer. + + Secondary + Drugorzędny + No comment provided by engineer. + Secure queue Bezpieczna kolejka server test step + + Secured + Zabezpieczone + No comment provided by engineer. + Security assessment Ocena bezpieczeństwa @@ -4794,6 +6582,21 @@ Błąd: %@ Select Wybierz + chat item action + + + Select chat profile + Wybierz profil czatu + No comment provided by engineer. + + + Selected %lld + Zaznaczono %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Wybrane preferencje czatu zabraniają tej wiadomości. No comment provided by engineer. @@ -4831,11 +6634,6 @@ Błąd: %@ Wyślij potwierdzenia dostawy do No comment provided by engineer. - - Send direct message - Wyślij wiadomość bezpośrednią - No comment provided by engineer. - Send direct message to connect Wyślij wiadomość bezpośrednią aby połączyć @@ -4846,6 +6644,11 @@ Błąd: %@ Wyślij znikającą wiadomość No comment provided by engineer. + + Send errors + Wyślij błędy + No comment provided by engineer. + Send link previews Wyślij podgląd linku @@ -4856,14 +6659,28 @@ Błąd: %@ Wyślij wiadomość na żywo No comment provided by engineer. + + Send message to enable calls. + Wyślij wiadomość aby włączyć połączenia. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + Wysyłaj wiadomości bezpośrednio, gdy adres IP jest chroniony i Twój lub docelowy serwer nie obsługuje prywatnego trasowania. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + Wysyłaj wiadomości bezpośrednio, gdy Twój lub docelowy serwer nie obsługuje prywatnego trasowania. + No comment provided by engineer. + Send notifications Wyślij powiadomienia No comment provided by engineer. - - Send notifications: - Wyślij powiadomienia: + + Send private reports No comment provided by engineer. @@ -4889,7 +6706,7 @@ Błąd: %@ Sender cancelled file transfer. Nadawca anulował transfer pliku. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4946,6 +6763,11 @@ Błąd: %@ Wysłano o: %@ copied message info + + Sent directly + Wysłano bezpośrednio + No comment provided by engineer. + Sent file event Wyślij zdarzenie pliku @@ -4956,11 +6778,67 @@ Błąd: %@ Wyślij wiadomość message info title + + Sent messages + Wysłane wiadomości + No comment provided by engineer. + Sent messages will be deleted after set time. Wysłane wiadomości zostaną usunięte po ustawionym czasie. No comment provided by engineer. + + Sent reply + Wyślij odpowiedź + No comment provided by engineer. + + + Sent total + Wysłano łącznie + No comment provided by engineer. + + + Sent via proxy + Wysłano przez proxy + No comment provided by engineer. + + + Server + Serwer + No comment provided by engineer. + + + Server added to operator %@. + alert message + + + Server address + Adres serwera + No comment provided by engineer. + + + Server address is incompatible with network settings. + Adres serwera jest niekompatybilny z ustawieniami sieciowymi. + srv error text. + + + Server address is incompatible with network settings: %@. + Adres serwera jest niekompatybilny z ustawieniami sieci: %@. + No comment provided by engineer. + + + Server operator changed. + alert title + + + Server operators + No comment provided by engineer. + + + Server protocol changed. + alert title + Server requires authorization to create queues, check password Serwer wymaga autoryzacji do tworzenia kolejek, sprawdź hasło @@ -4976,11 +6854,36 @@ Błąd: %@ Test serwera nie powiódł się! No comment provided by engineer. + + Server type + Typ serwera + No comment provided by engineer. + + + Server version is incompatible with network settings. + Wersja serwera jest niekompatybilna z ustawieniami sieciowymi. + srv error text + + + Server version is incompatible with your app: %@. + Wersja serwera jest niekompatybilna z aplikacją: %@. + No comment provided by engineer. + Servers Serwery No comment provided by engineer. + + Servers info + Informacje o serwerach + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + Statystyki serwerów zostaną zresetowane - nie można tego cofnąć! + No comment provided by engineer. + Session code Kod sesji @@ -4991,11 +6894,20 @@ Błąd: %@ Ustaw 1 dzień No comment provided by engineer. + + Set chat name… + No comment provided by engineer. + Set contact name… Ustaw nazwę kontaktu… No comment provided by engineer. + + Set default theme + Ustaw domyślny motyw + No comment provided by engineer. + Set group preferences Ustaw preferencje grupy @@ -5006,6 +6918,10 @@ Błąd: %@ Ustaw go zamiast uwierzytelniania systemowego. No comment provided by engineer. + + Set message expiration in chats. + No comment provided by engineer. + Set passcode Ustaw pin @@ -5013,6 +6929,7 @@ Błąd: %@ Set passphrase + Ustaw hasło No comment provided by engineer. @@ -5035,24 +6952,52 @@ Błąd: %@ Ustawienia No comment provided by engineer. + + Settings were changed. + Ustawienia zostały zmienione. + alert message + + + Shape profile images + Kształtuj obrazy profilowe + No comment provided by engineer. + Share Udostępnij - chat item action + alert action +chat item action Share 1-time link Udostępnij 1-razowy link No comment provided by engineer. + + Share 1-time link with a friend + No comment provided by engineer. + + + Share SimpleX address on social media. + No comment provided by engineer. + Share address Udostępnij adres No comment provided by engineer. + + Share address publicly + No comment provided by engineer. + Share address with contacts? Udostępnić adres kontaktom? + alert title + + + Share from other apps. + Udostępnij z innych aplikacji. No comment provided by engineer. @@ -5060,18 +7005,33 @@ Błąd: %@ Udostępnij link No comment provided by engineer. + + Share profile + Udostępnij profil + No comment provided by engineer. + Share this 1-time invite link Udostępnij ten jednorazowy link No comment provided by engineer. + + Share to SimpleX + Udostępnij do SimpleX + No comment provided by engineer. + Share with contacts Udostępnij kontaktom No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code + Pokaż kod QR No comment provided by engineer. @@ -5089,21 +7049,45 @@ Błąd: %@ Pokaż ostatnie wiadomości No comment provided by engineer. + + Show message status + Pokaż status wiadomości + No comment provided by engineer. + + + Show percentage + Pokaż procent + No comment provided by engineer. + Show preview Pokaż podgląd No comment provided by engineer. + + Show → on messages sent via private routing. + Pokaż → na wiadomościach wysłanych przez prywatne trasowanie. + No comment provided by engineer. + Show: Pokaż: No comment provided by engineer. + + SimpleX + SimpleX + No comment provided by engineer. + SimpleX Address Adres SimpleX No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. Bezpieczeństwo SimpleX Chat zostało zaudytowane przez Trail of Bits. @@ -5134,6 +7118,18 @@ Błąd: %@ Adres SimpleX No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + No comment provided by engineer. + + + SimpleX address or 1-time link? + No comment provided by engineer. + + + SimpleX channel link + simplex link type + SimpleX contact address Adres kontaktowy SimpleX @@ -5152,6 +7148,16 @@ Błąd: %@ SimpleX links Linki SimpleX + chat feature + + + SimpleX links are prohibited. + Linki SimpleX są zablokowane na tej grupie. + No comment provided by engineer. + + + SimpleX links not allowed + Linki SimpleX są niedozwolone No comment provided by engineer. @@ -5159,11 +7165,20 @@ Błąd: %@ Zaproszenie jednorazowe SimpleX simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Uproszczony tryb incognito No comment provided by engineer. + + Size + Rozmiar + No comment provided by engineer. + Skip Pomiń @@ -5179,16 +7194,51 @@ Błąd: %@ Małe grupy (maks. 20) No comment provided by engineer. + + Soft + Łagodny + blur media + + + Some app settings were not migrated. + Niektóre ustawienia aplikacji nie zostały zmigrowane. + No comment provided by engineer. + + + Some file(s) were not exported: + Niektóre plik(i) nie zostały wyeksportowane: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. Podczas importu wystąpiły niekrytyczne błędy - więcej szczegółów można znaleźć w konsoli czatu. No comment provided by engineer. + + Some non-fatal errors occurred during import: + Podczas importu wystąpiły niekrytyczne błędy: + No comment provided by engineer. + + + Some servers failed the test: +%@ + alert message + Somebody Ktoś notification title + + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + Kwadrat, okrąg lub cokolwiek pomiędzy. + No comment provided by engineer. + Start chat Rozpocznij czat @@ -5204,6 +7254,16 @@ Błąd: %@ Rozpocznij migrację No comment provided by engineer. + + Starting from %@. + Zaczynanie od %@. + No comment provided by engineer. + + + Statistics + Statystyki + No comment provided by engineer. + Stop Zatrzymaj @@ -5216,11 +7276,7 @@ Błąd: %@ Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - Zatrzymaj czat, aby umożliwić działania na bazie danych + Zatrzymaj czat No comment provided by engineer. @@ -5251,27 +7307,60 @@ Błąd: %@ Stop sharing Przestań udostępniać - No comment provided by engineer. + alert action Stop sharing address? Przestać udostępniać adres? - No comment provided by engineer. + alert title Stopping chat + Zatrzymywanie czatu No comment provided by engineer. + + Storage + No comment provided by engineer. + + + Strong + Silne + blur media + Submit Zatwierdź No comment provided by engineer. + + Subscribed + Zasubskrybowano + No comment provided by engineer. + + + Subscription errors + Błędy subskrypcji + No comment provided by engineer. + + + Subscriptions ignored + Subskrypcje zignorowane + No comment provided by engineer. + Support SimpleX Chat Wspieraj SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System System @@ -5282,11 +7371,20 @@ Błąd: %@ Uwierzytelnianie systemu No comment provided by engineer. + + TCP connection + Połączenie TCP + No comment provided by engineer. + TCP connection timeout Limit czasu połączenia TCP No comment provided by engineer. + + TCP port for messaging + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5302,11 +7400,20 @@ Błąd: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + Ogon + No comment provided by engineer. + Take picture Zrób zdjęcie No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + No comment provided by engineer. + Tap button Naciśnij przycisk @@ -5342,16 +7449,20 @@ Błąd: %@ Dotknij, aby zeskanować No comment provided by engineer. - - Tap to start a new chat - Dotknij, aby rozpocząć nowy czat - No comment provided by engineer. + + Temporary file error + Tymczasowy błąd pliku + file error alert title Test failed at step %@. Test nie powiódł się na etapie %@. server test failure + + Test notifications + No comment provided by engineer. + Test server Przetestuj serwer @@ -5365,7 +7476,7 @@ Błąd: %@ Tests failed! Testy nie powiodły się! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5382,11 +7493,6 @@ Błąd: %@ Podziękowania dla użytkowników - wkład za pośrednictwem Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - Pierwsza platforma bez żadnych identyfikatorów użytkowników – z założenia prywatna. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5399,6 +7505,15 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Aplikacja może powiadamiać Cię, gdy otrzymujesz wiadomości lub prośby o kontakt — otwórz ustawienia, aby włączyć. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + Aplikacja zapyta o potwierdzenie pobierania od nieznanych serwerów plików (poza .onion). + No comment provided by engineer. + The attempt to change database passphrase was not completed. Próba zmiany hasła bazy danych nie została zakończona. @@ -5409,6 +7524,10 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Kod, który zeskanowałeś nie jest kodem QR linku SimpleX. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + No comment provided by engineer. + The connection you accepted will be cancelled! Zaakceptowane przez Ciebie połączenie zostanie anulowane! @@ -5429,6 +7548,11 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Szyfrowanie działa, a nowe uzgodnienie szyfrowania nie jest wymagane. Może to spowodować błędy w połączeniu! No comment provided by engineer. + + The future of messaging + Następna generacja prywatnych wiadomości + No comment provided by engineer. + The hash of the previous message is different. Hash poprzedniej wiadomości jest inny. @@ -5444,9 +7568,14 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Wiadomość zostanie oznaczona jako moderowana dla wszystkich członków. No comment provided by engineer. - - The next generation of private messaging - Następna generacja prywatnych wiadomości + + The messages will be deleted for all members. + Wiadomości zostaną usunięte dla wszystkich członków. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. + Wiadomości zostaną oznaczone jako moderowane dla wszystkich członków. No comment provided by engineer. @@ -5454,9 +7583,12 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Stara baza danych nie została usunięta podczas migracji, można ją usunąć. No comment provided by engineer. - - The profile is only shared with your contacts. - Profil jest udostępniany tylko Twoim kontaktom. + + The same conditions will apply to operator **%@**. + No comment provided by engineer. + + + The second preset operator in the app! No comment provided by engineer. @@ -5474,14 +7606,27 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Serwery dla nowych połączeń bieżącego profilu czatu **%@**. No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. Tekst, który wkleiłeś nie jest linkiem SimpleX. No comment provided by engineer. - - Theme - Motyw + + The uploaded database archive will be permanently removed from the servers. + Przesłane archiwum bazy danych zostanie trwale usunięte z serwerów. + No comment provided by engineer. + + + Themes + Motywy + No comment provided by engineer. + + + These conditions will also apply for: **%@**. No comment provided by engineer. @@ -5504,6 +7649,10 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Tego działania nie można cofnąć - wiadomości wysłane i odebrane wcześniej niż wybrane zostaną usunięte. Może to potrwać kilka minut. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. Tego działania nie można cofnąć - Twój profil, kontakty, wiadomości i pliki zostaną nieodwracalnie utracone. @@ -5511,10 +7660,12 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom This chat is protected by end-to-end encryption. + Ten czat jest chroniony przez szyfrowanie end-to-end. E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + Ten czat jest chroniony przez kwantowo odporne szyfrowanie end-to-end. E2EE info chat item @@ -5547,11 +7698,29 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom To jest twój jednorazowy link! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + Ten link dostał użyty z innym urządzeniem mobilnym, proszę stworzyć nowy link na komputerze. + No comment provided by engineer. + + + This message was deleted or not received yet. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. To ustawienie dotyczy wiadomości Twojego bieżącego profilu czatu **%@**. No comment provided by engineer. + + Title + Tytuł + No comment provided by engineer. + To ask any questions and to receive updates: Aby zadać wszelkie pytania i otrzymywać aktualizacje: @@ -5572,9 +7741,8 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Aby nawiązać nowe połączenie No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Aby chronić prywatność, zamiast identyfikatorów użytkowników używanych przez wszystkie inne platformy, SimpleX ma identyfikatory dla kolejek wiadomości, oddzielne dla każdego z Twoich kontaktów. + + To protect against your link being replaced, you can compare contact security codes. No comment provided by engineer. @@ -5582,6 +7750,11 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Aby chronić strefę czasową, pliki obrazów/głosów używają UTC. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + Aby chronić Twój adres IP, prywatne trasowanie używa Twoich serwerów SMP, aby dostarczyć wiadomości. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5589,6 +7762,25 @@ You will be prompted to complete authentication before this feature is enabled.< Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Aby chronić prywatność, zamiast identyfikatorów użytkowników używanych przez wszystkie inne platformy, SimpleX ma identyfikatory dla kolejek wiadomości, oddzielne dla każdego z Twoich kontaktów. + No comment provided by engineer. + + + To receive + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + Aby nagrać rozmowę, proszę zezwolić na użycie Mikrofonu. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Aby nagrać wideo, proszę zezwolić na użycie Aparatu. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Aby nagrać wiadomość głosową należy udzielić zgody na użycie Mikrofonu. @@ -5599,26 +7791,58 @@ Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania.Aby ujawnić Twój ukryty profil, wprowadź pełne hasło w pole wyszukiwania na stronie **Twoich profili czatu**. No comment provided by engineer. + + To send + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. Aby obsługiwać natychmiastowe powiadomienia push, należy zmigrować bazę danych czatu. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. Aby zweryfikować szyfrowanie end-to-end z Twoim kontaktem porównaj (lub zeskanuj) kod na waszych urządzeniach. No comment provided by engineer. + + Toggle chat list: + Przełącz listę czatów: + No comment provided by engineer. + Toggle incognito when connecting. Przełącz incognito przy połączeniu. No comment provided by engineer. + + Token status: %@. + token status + + + Toolbar opacity + Nieprzezroczystość paska narzędzi + No comment provided by engineer. + + + Total + Łącznie + No comment provided by engineer. + Transport isolation Izolacja transportu No comment provided by engineer. + + Transport sessions + Sesje transportowe + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Próbowanie połączenia z serwerem używanym do odbierania wiadomości od tego kontaktu (błąd: %@). @@ -5674,10 +7898,9 @@ Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania.Odblokować członka? No comment provided by engineer. - - Unexpected error: %@ - Nieoczekiwany błąd: %@ - item status description + + Undelivered messages + No comment provided by engineer. Unexpected migration state @@ -5687,7 +7910,7 @@ Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania. Unfav. Nie ulub. - No comment provided by engineer. + swipe action Unhide @@ -5724,6 +7947,11 @@ Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania.Nieznany błąd No comment provided by engineer. + + Unknown servers! + Nieznane serwery! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. O ile nie korzystasz z interfejsu połączeń systemu iOS, włącz tryb Nie przeszkadzać, aby uniknąć przerywania. @@ -5759,11 +7987,15 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Unmute Wyłącz wyciszenie - No comment provided by engineer. + notification label action Unread Nieprzeczytane + swipe action + + + Unsupported connection link No comment provided by engineer. @@ -5776,11 +8008,6 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Aktualizuj No comment provided by engineer. - - Update .onion hosts setting? - Zaktualizować ustawienie hostów .onion? - No comment provided by engineer. - Update database passphrase Aktualizuj hasło do bazy danych @@ -5791,9 +8018,13 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Zaktualizować ustawienia sieci? No comment provided by engineer. - - Update transport isolation mode? - Zaktualizować tryb izolacji transportu? + + Update settings? + Zaktualizować ustawienia? + No comment provided by engineer. + + + Updated conditions No comment provided by engineer. @@ -5801,18 +8032,19 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Aktualizacja ustawień spowoduje ponowne połączenie klienta ze wszystkimi serwerami. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - Aktualizacja tych ustawień spowoduje ponowne połączenie klienta ze wszystkimi serwerami. - No comment provided by engineer. - Upgrade and open chat Zaktualizuj i otwórz czat No comment provided by engineer. + + Upload errors + Błędy przesłania + No comment provided by engineer. + Upload failed + Wgrywanie nie udane No comment provided by engineer. @@ -5820,8 +8052,23 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Prześlij plik server test step + + Uploaded + Przesłane + No comment provided by engineer. + + + Uploaded files + Przesłane pliki + No comment provided by engineer. + Uploading archive + Wgrywanie archiwum + No comment provided by engineer. + + + Use %@ No comment provided by engineer. @@ -5829,11 +8076,24 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Użyj hostów .onion No comment provided by engineer. + + Use SOCKS proxy + Użyj proxy SOCKS + No comment provided by engineer. + Use SimpleX Chat servers? Użyć serwerów SimpleX Chat? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat Użyj czatu @@ -5844,6 +8104,14 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Użyj obecnego profilu No comment provided by engineer. + + Use for files + No comment provided by engineer. + + + Use for messages + No comment provided by engineer. + Use for new connections Użyj dla nowych połączeń @@ -5869,23 +8137,51 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Używać tylko lokalnych powiadomień? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + Używaj prywatnego trasowania z nieznanymi serwerami, gdy adres IP nie jest chroniony. + No comment provided by engineer. + + + Use private routing with unknown servers. + Używaj prywatnego trasowania z nieznanymi serwerami. + No comment provided by engineer. + Use server Użyj serwera No comment provided by engineer. + + Use servers + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. + Używaj aplikacji podczas połączenia. No comment provided by engineer. - - User profile - Profil użytkownika + + Use the app with one hand. + Korzystaj z aplikacji jedną ręką. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - Używanie hostów .onion wymaga kompatybilnego dostawcy VPN. + + Use web port + No comment provided by engineer. + + + User selection + Wybór użytkownika + No comment provided by engineer. + + + Username + Nazwa użytkownika No comment provided by engineer. @@ -5915,10 +8211,12 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Verify database passphrase + Zweryfikuj hasło bazy danych No comment provided by engineer. Verify passphrase + Zweryfikuj hasło No comment provided by engineer. @@ -5956,11 +8254,19 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Filmy i pliki do 1gb No comment provided by engineer. + + View conditions + No comment provided by engineer. + View security code Pokaż kod bezpieczeństwa No comment provided by engineer. + + View updated conditions + No comment provided by engineer. + Visible history Widoczna historia @@ -5976,11 +8282,16 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Wiadomości głosowe są zabronione na tym czacie. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. Wiadomości głosowe są zabronione w tej grupie. No comment provided by engineer. + + Voice messages not allowed + Wiadomości głosowe są niedozwolone + No comment provided by engineer. + Voice messages prohibited! Wiadomości głosowe zabronione! @@ -6011,8 +8322,19 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Oczekiwanie na film No comment provided by engineer. + + Wallpaper accent + Akcent tapety + No comment provided by engineer. + + + Wallpaper background + Tło tapety + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures + Ostrzeżenie: rozpoczęcie czatu na wielu urządzeniach nie jest wspierane i spowoduje niepowodzenia dostarczania wiadomości No comment provided by engineer. @@ -6037,6 +8359,7 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Welcome message is too long + Wiadomość powitalna jest zbyt długa No comment provided by engineer. @@ -6049,9 +8372,13 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Gdy dostępny No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Kiedy ludzie proszą o połączenie, możesz je zaakceptować lub odrzucić. + + When connecting audio and video calls. + Podczas łączenia połączeń audio i wideo. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. No comment provided by engineer. @@ -6059,6 +8386,21 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Gdy udostępnisz komuś profil incognito, będzie on używany w grupach, do których Cię zaprosi. No comment provided by engineer. + + WiFi + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + Zostanie włączone w czatach bezpośrednich! + No comment provided by engineer. + + + Wired ethernet + Połączenie ethernet (po kablu) + No comment provided by engineer. + With encrypted files and media. Z zaszyfrowanymi plikami i multimediami. @@ -6074,28 +8416,44 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Ze zmniejszonym zużyciem baterii. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + Bez Tor lub VPN, Twój adres IP będzie widoczny do serwerów plików. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + Bez Tor lub VPN, Twój adres IP będzie widoczny dla tych przekaźników XFTP: %@. + alert message + Wrong database passphrase Nieprawidłowe hasło bazy danych No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + Zły klucz lub nieznane połączenie - najprawdopodobniej to połączenie jest usunięte. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + Zły klucz lub nieznany adres fragmentu pliku - najprawdopodobniej plik został usunięty. + file error text + Wrong passphrase! Nieprawidłowe hasło! No comment provided by engineer. - - XFTP servers - Serwery XFTP - No comment provided by engineer. - - - You - Ty + + XFTP server + Serwer XFTP No comment provided by engineer. You **must not** use the same database on two devices. + **Nie możesz** używać tej samej bazy na dwóch urządzeniach. No comment provided by engineer. @@ -6118,6 +8476,10 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Jesteś już połączony z %@. No comment provided by engineer. + + You are already connected with %@. + No comment provided by engineer. + You are already connecting to %@. Już się łączysz z %@. @@ -6165,11 +8527,25 @@ Powtórzyć prośbę dołączenia? Jesteś zaproszony do grupy No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + Nie jesteś połączony z tymi serwerami. Prywatne trasowanie jest używane do dostarczania do nich wiadomości. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. Możesz przyjmować połączenia z ekranu blokady, bez uwierzytelniania urządzenia i aplikacji. No comment provided by engineer. + + You can change it in Appearance settings. + Możesz to zmienić w ustawieniach wyglądu. + No comment provided by engineer. + + + You can configure servers via settings. + No comment provided by engineer. + You can create it later Możesz go utworzyć później @@ -6187,6 +8563,7 @@ Powtórzyć prośbę dołączenia? You can give another try. + Możesz spróbować ponownie. No comment provided by engineer. @@ -6199,11 +8576,20 @@ Powtórzyć prośbę dołączenia? Możesz ustawić go jako widoczny dla swoich kontaktów SimpleX w Ustawieniach. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ Możesz teraz wysyłać wiadomości do %@ notification body + + You can send messages to %@ from Archived contacts. + Możesz wysyłać wiadomości do %@ ze zarchiwizowanych kontaktów. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + No comment provided by engineer. + You can set lock screen notification preview via settings. Podgląd powiadomień na ekranie blokady można ustawić w ustawieniach. @@ -6219,16 +8605,16 @@ Powtórzyć prośbę dołączenia? Możesz udostępnić ten adres Twoim kontaktom, aby umożliwić im połączenie z **%@**. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - Możesz udostępnić swój adres jako link lub jako kod QR - każdy będzie mógł się z Tobą połączyć. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app Możesz rozpocząć czat poprzez Ustawienia aplikacji / Baza danych lub poprzez ponowne uruchomienie aplikacji No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + Nadal możesz przeglądać rozmowę z %@ na liście czatów. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. Możesz włączyć blokadę SimpleX poprzez Ustawienia. @@ -6242,23 +8628,23 @@ Powtórzyć prośbę dołączenia? You can view invitation link again in connection details. Możesz zobaczyć link zaproszenia ponownie w szczegółach połączenia. - No comment provided by engineer. + alert message You can't send messages! Nie możesz wysyłać wiadomości! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Kontrolujesz przez który serwer(y) **odbierać** wiadomości, Twoje kontakty - serwery, których używasz do wysyłania im wiadomości. - No comment provided by engineer. - You could not be verified; please try again. Nie można zweryfikować użytkownika; proszę spróbować ponownie. No comment provided by engineer. + + You decide who can connect. + Ty decydujesz, kto może się połączyć. + No comment provided by engineer. + You have already requested connection via this address! Już prosiłeś o połączenie na ten adres! @@ -6271,11 +8657,6 @@ Repeat connection request? Powtórzyć prośbę połączenia? No comment provided by engineer. - - You have no chats - Nie masz czatów - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. Musisz wprowadzić hasło przy każdym uruchomieniu aplikacji - nie jest one przechowywane na urządzeniu. @@ -6296,11 +8677,26 @@ Powtórzyć prośbę połączenia? Dołączyłeś do tej grupy. Łączenie z zapraszającym członkiem grupy. No comment provided by engineer. + + You may migrate the exported database. + Możesz zmigrować wyeksportowaną bazy danych. + No comment provided by engineer. + + + You may save the exported archive. + Możesz zapisać wyeksportowane archiwum. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. Musisz używać najnowszej wersji bazy danych czatu TYLKO na jednym urządzeniu, w przeciwnym razie możesz przestać otrzymywać wiadomości od niektórych kontaktów. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + Aby móc dzwonić, musisz zezwolić kontaktowi na połączenia. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. Musisz zezwolić Twojemu kontaktowi na wysyłanie wiadomości głosowych, aby móc je wysyłać. @@ -6316,6 +8712,10 @@ Powtórzyć prośbę połączenia? Wysłałeś zaproszenie do grupy No comment provided by engineer. + + You should receive notifications. + token info + You will be connected to group when the group host's device is online, please wait or check later! Zostaniesz połączony do grupy, gdy urządzenie gospodarza grupy będzie online, proszę czekać lub sprawdzić później! @@ -6351,6 +8751,10 @@ Powtórzyć prośbę połączenia? Nadal będziesz otrzymywać połączenia i powiadomienia z wyciszonych profili, gdy są one aktywne. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Przestaniesz otrzymywać wiadomości od tej grupy. Historia czatu zostanie zachowana. @@ -6371,31 +8775,16 @@ Powtórzyć prośbę połączenia? Używasz profilu incognito dla tej grupy - aby zapobiec udostępnianiu głównego profilu zapraszanie kontaktów jest zabronione No comment provided by engineer. - - Your %@ servers - Twoje serwery %@ - No comment provided by engineer. - Your ICE servers Twoje serwery ICE No comment provided by engineer. - - Your SMP servers - Twoje serwery SMP - No comment provided by engineer. - Your SimpleX address Twój adres SimpleX No comment provided by engineer. - - Your XFTP servers - Twoje serwery XFTP - No comment provided by engineer. - Your calls Twoje połączenia @@ -6411,16 +8800,19 @@ Powtórzyć prośbę połączenia? Baza danych czatu nie jest szyfrowana - ustaw hasło, aby ją zaszyfrować. No comment provided by engineer. + + Your chat preferences + Twoje preferencje czatu + alert title + Your chat profiles Twoje profile czatu No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Twój kontakt musi być online, aby połączenie zostało zakończone. -Możesz anulować to połączenie i usunąć kontakt (i spróbować później z nowym linkiem). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Twoje połączenie zostało przeniesione do %@, ale podczas przekierowywania do profilu wystąpił nieoczekiwany błąd. No comment provided by engineer. @@ -6438,6 +8830,11 @@ Możesz anulować to połączenie i usunąć kontakt (i spróbować później z Twoje kontakty pozostaną połączone. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Twoje poświadczenia mogą zostać wysłane niezaszyfrowane. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Twoja obecna baza danych czatu zostanie usunięta i zastąpiona zaimportowaną. @@ -6468,33 +8865,36 @@ Możesz anulować to połączenie i usunąć kontakt (i spróbować później z Twój profil **%@** zostanie udostępniony. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Twój profil jest przechowywany na urządzeniu i udostępniany tylko Twoim kontaktom. -Serwery SimpleX nie mogą zobaczyć Twojego profilu. + + Your profile is stored on your device and only shared with your contacts. + Profil jest udostępniany tylko Twoim kontaktom. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Twój profil, kontakty i dostarczone wiadomości są przechowywane na Twoim urządzeniu. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Twój profil jest przechowywany na urządzeniu i udostępniany tylko Twoim kontaktom. Serwery SimpleX nie mogą zobaczyć Twojego profilu. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Twój profil został zmieniony. Jeśli go zapiszesz, zaktualizowany profil zostanie wysłany do wszystkich kontaktów. + alert message + Your random profile Twój losowy profil No comment provided by engineer. - - Your server - Twój serwer - No comment provided by engineer. - Your server address Twój adres serwera No comment provided by engineer. + + Your servers + Twoje serwery + No comment provided by engineer. + Your settings Twoje ustawienia @@ -6535,11 +8935,20 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. zaakceptowane połączenie call status + + accepted invitation + chat list item title + admin administrator member role + + admins + administratorzy + feature role + agreeing encryption for %@… uzgadnianie szyfrowania dla %@… @@ -6550,6 +8959,11 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. uzgadnianie szyfrowania… chat item text + + all members + wszyscy członkowie + feature role + always zawsze @@ -6560,6 +8974,15 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. i %lld innych wydarzeń No comment provided by engineer. + + archived report + No comment provided by engineer. + + + attempts + próby + No comment provided by engineer. + audio call (not e2e encrypted) połączenie audio (nie szyfrowane e2e) @@ -6593,13 +9016,19 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. blocked by admin zablokowany przez admina - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold pogrubiona No comment provided by engineer. + + call + zadzwoń + No comment provided by engineer. + call error błąd połączenia @@ -6703,7 +9132,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. connecting… łączenie… - chat list item title + No comment provided by engineer. connection established @@ -6750,10 +9179,16 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. dni time unit + + decryption errors + błąd odszyfrowywania + No comment provided by engineer. + default (%@) domyślne (%@) - pref value + delete after time +pref value default (no) @@ -6800,6 +9235,11 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. zduplikowana wiadomość integrity error chat item + + duplicates + duplikaty + No comment provided by engineer. + e2e encrypted zaszyfrowany e2e @@ -6875,9 +9315,14 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. błąd No comment provided by engineer. - - event happened - nowe wydarzenie + + expired + wygasły + No comment provided by engineer. + + + forwarded + przekazane dalej No comment provided by engineer. @@ -6905,6 +9350,11 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. iOS Keychain będzie używany do bezpiecznego przechowywania hasła po ponownym uruchomieniu aplikacji lub zmianie hasła - pozwoli to na otrzymywanie powiadomień push. No comment provided by engineer. + + inactive + nieaktywny + No comment provided by engineer. + incognito via contact address link incognito poprzez link adresu kontaktowego @@ -6945,6 +9395,11 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. zaproszenie do grupy %@ group name + + invite + zaproś + No comment provided by engineer. + invited zaproszony @@ -7000,6 +9455,11 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. połączony rcv group event chat item + + message + wiadomość + No comment provided by engineer. + message received wiadomość otrzymana @@ -7025,6 +9485,10 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. moderowany przez %@ marked deleted chat item preview text + + moderator + member role + months miesiące @@ -7033,7 +9497,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. never nigdy - No comment provided by engineer. + delete after time new message @@ -7064,8 +9528,8 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. off wyłączony enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7082,18 +9546,42 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. włączone group pref value + + other + inne + No comment provided by engineer. + + + other errors + inne błędy + No comment provided by engineer. + owner właściciel member role + + owners + właściciele + feature role + peer-to-peer peer-to-peer No comment provided by engineer. + + pending + No comment provided by engineer. + + + pending approval + No comment provided by engineer. + quantum resistant e2e encryption + kwantowo odporne szyfrowanie e2e chat item text @@ -7106,6 +9594,10 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. otrzymano potwierdzenie… No comment provided by engineer. + + rejected + No comment provided by engineer. + rejected call odrzucone połączenie @@ -7136,6 +9628,25 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. usunął cię rcv group event chat item + + requested to connect + chat list item title + + + saved + zapisane + No comment provided by engineer. + + + saved from %@ + zapisane od %@ + No comment provided by engineer. + + + search + szukaj + No comment provided by engineer. + sec sek @@ -7161,6 +9672,15 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. wyślij wiadomość bezpośrednią No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + Informacje kolejki serwera: %1$@ + +ostatnia otrzymana wiadomość: %2$@ + queue info + set new contact address ustaw nowy adres kontaktu @@ -7173,6 +9693,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. standard end-to-end encryption + standardowe szyfrowanie end-to-end chat item text @@ -7200,11 +9721,21 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. nieznany connection info + + unknown servers + nieznane przekaźniki + No comment provided by engineer. + unknown status nieznany status No comment provided by engineer. + + unprotected + niezabezpieczony + No comment provided by engineer. + updated group profile zaktualizowano profil grupy @@ -7245,6 +9776,11 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. przez przekaźnik No comment provided by engineer. + + video + wideo + No comment provided by engineer. + video call (not e2e encrypted) połączenie wideo (bez szyfrowania e2e) @@ -7270,11 +9806,21 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. tygodnie time unit + + when IP hidden + gdy IP ukryty + No comment provided by engineer. + yes tak pref value + + you + Ty + No comment provided by engineer. + you are invited to group jesteś zaproszony do grupy @@ -7349,7 +9895,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.
- +
@@ -7386,7 +9932,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.
- +
@@ -7406,4 +9952,245 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.
+ +
+ +
+ + + %d new events + notification body + + + From %d chat(s) + notification body + + + From: %@ + notification body + + + New events + notification + + + New messages + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright © 2024 SimpleX Chat. Wszelkie prawa zastrzeżone. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + Aplikacja zablokowana! + No comment provided by engineer. + + + Cancel + Anuluj + No comment provided by engineer. + + + Cannot access keychain to save database password + Nie można uzyskać dostępu do pęku kluczy aby zapisać hasło do bazy danych + No comment provided by engineer. + + + Cannot forward message + Nie można przekazać wiadomości + No comment provided by engineer. + + + Comment + Komentarz + No comment provided by engineer. + + + Currently maximum supported file size is %@. + Obecnie maksymalny obsługiwany rozmiar pliku to %@. + No comment provided by engineer. + + + Database downgrade required + Wymagane obniżenie wersji bazy danych + No comment provided by engineer. + + + Database encrypted! + Baza danych zaszyfrowana! + No comment provided by engineer. + + + Database error + Błąd bazy danych + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + Hasło bazy danych jest inne niż zapisane w pęku kluczy. + No comment provided by engineer. + + + Database passphrase is required to open chat. + Hasło do bazy danych jest wymagane do otwarcia czatu. + No comment provided by engineer. + + + Database upgrade required + Wymagana aktualizacja bazy danych + No comment provided by engineer. + + + Error preparing file + Błąd przygotowania pliku + No comment provided by engineer. + + + Error preparing message + Błąd przygotowania wiadomości + No comment provided by engineer. + + + Error: %@ + Błąd: %@ + No comment provided by engineer. + + + File error + Błąd pliku + No comment provided by engineer. + + + Incompatible database version + Niekompatybilna wersja bazy danych + No comment provided by engineer. + + + Invalid migration confirmation + Nieprawidłowe potwierdzenie migracji + No comment provided by engineer. + + + Keychain error + Błąd pęku kluczy + No comment provided by engineer. + + + Large file! + Duży plik! + No comment provided by engineer. + + + No active profile + Brak aktywnego profilu + No comment provided by engineer. + + + Ok + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + Otwórz aplikację aby obniżyć wersję bazy danych. + No comment provided by engineer. + + + Open the app to upgrade the database. + Otwórz aplikację aby zaktualizować bazę danych. + No comment provided by engineer. + + + Passphrase + Hasło + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Proszę utworzyć profil w aplikacji SimpleX + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Wybrane preferencje czatu zabraniają tej wiadomości. + No comment provided by engineer. + + + Sending a message takes longer than expected. + Wysłanie wiadomości trwa dłużej niż oczekiwano. + No comment provided by engineer. + + + Sending message… + Wysyłanie wiadomości… + No comment provided by engineer. + + + Share + Udostępnij + No comment provided by engineer. + + + Slow network? + Wolna sieć? + No comment provided by engineer. + + + Unknown database error: %@ + Nieznany błąd bazy danych: %@ + No comment provided by engineer. + + + Unsupported format + Niewspierany format + No comment provided by engineer. + + + Wait + Czekaj + No comment provided by engineer. + + + Wrong database passphrase + Nieprawidłowe hasło bazy danych + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + Możesz zezwolić na udostępnianie w ustawieniach Prywatność i bezpieczeństwo / Blokada SimpleX. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/contents.json b/apps/ios/SimpleX Localizations/pl.xcloc/contents.json index 22043b831d..c79fba1c1e 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/pl.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "pl", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/pt-BR.xcloc/Localized Contents/pt-BR.xliff b/apps/ios/SimpleX Localizations/pt-BR.xcloc/Localized Contents/pt-BR.xliff index b4fa69449f..bbb6c7d22a 100644 --- a/apps/ios/SimpleX Localizations/pt-BR.xcloc/Localized Contents/pt-BR.xliff +++ b/apps/ios/SimpleX Localizations/pt-BR.xcloc/Localized Contents/pt-BR.xliff @@ -187,23 +187,18 @@ ) No comment provided by engineer.
- - **Add new contact**: to create your one-time QR Code or link for your contact. - **Adicionar novo contato**: para criar seu QR Code ou link único para seu contato. - No comment provided by engineer. - **Create link / QR code** for your contact to use. **Crie um link / QR code** para seu contato usar. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **Mais privado**: verifique as novas mensagens a cada 20 minutos. O token do dispositivo é compartilhado com o servidor do SimpleX Chat, mas não quantos contatos ou mensagens você tem. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **Mais privado**: não use o servidor de notificações do SimpleX Chat, verifique as mensagens periodicamente em segundo plano (depende da frequência com que você usa o aplicativo). No comment provided by engineer. @@ -217,8 +212,8 @@ **Observação**: NÃO será possível recuperar ou alterar a frase secreta se você a perder. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Recomendado**: o token do dispositivo e as notificações são enviados para o servidor de notificações do SimpleX Chat, mas não o conteúdo, o tamanho ou o remetente da mensagem. No comment provided by engineer. @@ -374,9 +369,9 @@ Adicione servidores escaneando o QR code. No comment provided by engineer. - - Add server… - Adicionar servidor… + + Add server + Adicionar servidor No comment provided by engineer. @@ -1209,8 +1204,8 @@ Mensagens diretas chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited. Mensagens diretas entre membros são proibidas neste grupo. No comment provided by engineer. @@ -1229,8 +1224,8 @@ Mensagens temporárias são proibidas nesse chat. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. Mensagens que temporárias são proibidas neste grupo. No comment provided by engineer. @@ -1643,18 +1638,18 @@ Os membros do grupo podem excluir mensagens enviadas de forma irreversível. No comment provided by engineer. - - Group members can send direct messages. + + Members can send direct messages. Os membros do grupo podem enviar DMs. No comment provided by engineer. - - Group members can send disappearing messages. + + Members can send disappearing messages. Os membros do grupo podem enviar mensagens que desaparecem. No comment provided by engineer. - - Group members can send voice messages. + + Members can send voice messages. Os membros do grupo podem enviar mensagens de voz. No comment provided by engineer. @@ -1761,8 +1756,8 @@ A imagem será recebida quando seu contato estiver online, aguarde ou verifique mais tarde! No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Imune a spam e abuso No comment provided by engineer. @@ -1878,8 +1873,8 @@ A exclusão irreversível de mensagens é proibida neste chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. A exclusão irreversível de mensagens é proibida neste grupo. No comment provided by engineer. @@ -2209,8 +2204,8 @@ We will be adding server redundancy to prevent lost messages. Hosts Onion não serão usados. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only client devices store user profiles, contacts, groups, and messages. No comment provided by engineer. @@ -2267,8 +2262,8 @@ We will be adding server redundancy to prevent lost messages. Abrir console de chat authentication reason - - Open-source protocol and code – anybody can run the servers. + + Anybody can host servers. Protocolo de código aberto – qualquer um pode executar os servidores. No comment provided by engineer. @@ -2306,8 +2301,8 @@ We will be adding server redundancy to prevent lost messages. Cole o link que você recebeu na caixa abaixo para conectar com o seu contato. No comment provided by engineer. - - People can connect to you only via the links you share. + + You decide who can connect. Pessoas podem se conectar com você somente via links compartilhados. No comment provided by engineer. @@ -2961,8 +2956,8 @@ We will be adding server redundancy to prevent lost messages. Thank you for installing SimpleX Chat! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. + + No user identifiers. A 1ª plataforma sem nenhum identificador de usuário – privada por design. No comment provided by engineer. @@ -2998,8 +2993,8 @@ We will be adding server redundancy to prevent lost messages. The microphone does not work when the app is in the background. No comment provided by engineer. - - The next generation of private messaging + + The future of messaging A próxima geração de mensageiros privados No comment provided by engineer. @@ -3007,8 +3002,8 @@ We will be adding server redundancy to prevent lost messages. The old database was not removed during the migration, it can be deleted. No comment provided by engineer. - - The profile is only shared with your contacts. + + Your profile is stored on your device and only shared with your contacts. O perfil é compartilhado apenas com seus contatos. No comment provided by engineer. @@ -3071,8 +3066,8 @@ We will be adding server redundancy to prevent lost messages. To prevent the call interruption, enable Do Not Disturb mode. No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. No comment provided by engineer. @@ -3274,8 +3269,8 @@ Para se conectar, peça ao seu contato para criar outro link de conexão e verif Mensagens de voz são proibidas neste chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. Mensagens de voz são proibidas neste grupo. No comment provided by engineer. @@ -3402,10 +3397,6 @@ Para se conectar, peça ao seu contato para criar outro link de conexão e verif Você pode usar markdown para formatar mensagens: No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - You could not be verified; please try again. Você não pôde ser verificado; por favor, tente novamente. @@ -5234,6 +5225,426 @@ Isso pode acontecer por causa de algum bug ou quando a conexão está comprometi %1$@ em %2$@: copied message info, <sender> at <time> + + Allow your contacts to irreversibly delete sent messages. (24 hours) + Permitir que seus contatos deletem mensagens enviadas de maneira irreversível. (24 horas) + + + %@ downloaded + baixado + + + %@ uploaded + transferido + + + A new random profile will be shared. + Um novo perfil aleatório será compartilhado. + + + Camera not available + Câmera indisponível + + + Admins can block a member for all. + Administradores podem bloquear um membro para todos. + + + Allow to irreversibly delete sent messages. (24 hours) + Permitir que mensagens enviadas sejam deletadas de maneira irreversível. (24 horas) + + + Apply + Aplicar + + + Accent + Esquema + + + Accept connection request? + Aceitar solicitação de conexão? + + + Active connections + Conexões ativas + + + Add contact + Adicionar contato + + + Additional accent + Esquema adicional + + + All new messages from %@ will be hidden! + Todas as novas mensagens de %@ serão ocultas! + + + All profiles + Todos perfis + + + Allow calls? + Permitir chamadas? + + + Archive contacts to chat later. + Arquivar contatos para conversar depois. + + + Blur media + Censurar mídia + + + Calls prohibited! + Chamadas proibidas! + + + Can't call contact + Não foi possível ligar para o contato + + + %lld messages marked deleted + mensagens deletadas + + + 0 sec + 0 seg + + + %lld messages blocked + mensagens bloqueadas + + + %lld messages blocked by admin + mensagens bloqueadas pelo administrador + + + **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Nota**: usar o mesmo banco de dados em dois dispositivos irá quebrar a desencriptação das mensagens de suas conexões como uma medida de segurança. + + + - more stable message delivery. +- a bit better groups. +- and more! + - entrega de mensagens mais estável. +- grupos melhorados. +- e muito mais! + + + All messages will be deleted - this cannot be undone! + Todas as mensagens serão deletadas - isto não pode ser desfeito! + + + Allow to send files and media. + Permitir o envio de arquivos e mídia. + + + Allow to send SimpleX links. + Permitir envio de links SimpleX. + + + Block for all + Bloquear para todos + + + Block member + Bloquear membro + + + Blocked by admin + Bloqueado por um administrador + + + Block group members + Bloquear membros de grupo + + + Block member for all? + Bloquear membro para todos? + + + Block member? + Bloquear membro? + + + Both you and your contact can irreversibly delete sent messages. (24 hours) + Você e seu contato podem apagar mensagens enviadas de maneira irreversível. (24 horas) + + + Can't call member + Não foi possível ligar para este membro + + + Can't message member + Não foi possível enviar mensagem para este membro + + + Cancel migration + Cancelar migração + + + Abort + Abortar + + + Abort changing address + Abortar troca de endereço + + + Abort changing address? + Abortar troca de endereço? + + + - optionally notify deleted contacts. +- profile names with spaces. +- and more! + - notificar contatos apagados de maneira opcional. +- nome de perfil com espaços. +- e muito mais! + + + Allow sharing + Permitir compartilhamento + + + Block + Bloquear + + + Additional accent 2 + Esquema adicional 2 + + + Address change will be aborted. Old receiving address will be used. + Alteração de endereço será abortada. O endereço antigo será utilizado. + + + Advanced settings + Configurações avançadas + + + All data is kept private on your device. + Toda informação é privada em seu dispositivo. + + + All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Todos os seus contatos, conversas e arquivos serão encriptados e enviados em pedaços para nós XFTP. + + + Allow irreversible message deletion only if your contact allows it to you. (24 hours) + Permitir deletar mensagens de maneira irreversível apenas se seu contato permitir para você. (24 horas) + + + Already connecting! + Já está conectando! + + + Already joining the group! + Já está entrando no grupo! + + + Always use private routing. + Sempre use rotas privadas. + + + Apply to + Aplicar em + + + Archiving database + Arquivando banco de dados + + + Black + Preto + + + Cannot forward message + Não é possível encaminhar mensagem + + + (new) + (novo) + + + (this device v%@) + este dispositivo + + + **Create 1-time link**: to create and share a new invitation link. + **Adicionar contato**: criar um novo link de convite ou conectar via um link que você recebeu. + + + **Create group**: to create a new group. + **Criar grupo**: criar um novo grupo. + + + **Warning**: the archive will be removed. + **Aviso**: o arquivo será removido. + + + A few more things + E mais algumas coisas + + + Archived contacts + Contatos arquivados + + + Cellular + Rede móvel + + + %d file(s) failed to download. + %d arquivo(s) falharam ao ser baixados. + + + %d file(s) were deleted. + %d arquivo(s) foram apagados. + + + %d messages not forwarded + %d mensagens não encaminhadas + + + Bad desktop address + Endereço de desktop incorreto + + + Blur for better privacy. + Borrar para melhor privacidade. + + + %d file(s) are still being downloaded. + %d arquivo(s) ainda estão sendo baixados. + + + %d file(s) were not downloaded. + %d arquivo(s) não foram baixados. + + + Chat colors + Cores do chat + + + %lld group events + %lld eventos do grupo + + + %lld messages moderated by %@ + %lld mensagens moderadas por %@ + + + %1$@, %2$@ + %1$@, %2$@ + + + %lld new interface languages + %lld novos idiomas de interface + + + Better networking + Melhores redes + + + Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + Búlgaro, Finlandês, Tailandês e Ucraniano - obrigado aos usuários e [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + + + Better groups + Melhores grupos + + + Capacity exceeded - recipient did not receive previously sent messages. + Capacidade excedida - o destinatário não recebeu as mensagens enviadas anteriormente. + + + Chat migrated! + Conversa migrada! + + + Auto-accept settings + Aceitar automaticamente configurações + + + App encrypts new local files (except videos). + O aplicativo criptografa novos arquivos locais (exceto videos). + + + App session + Sessão do aplicativo + + + Acknowledged + Reconhecido + + + Acknowledgement errors + Erros conhecidos + + + Chat list + Lista de conversas + + + Chat database exported + Banco de dados da conversa exportado + + + Chat preferences were changed. + As preferências de bate-papo foram alteradas. + + + Chat theme + Tema da conversa + + + Better calls + Chamadas melhores + + + Better user experience + Melhor experiência do usuário + + + Allow downgrade + Permitir redução + + + Additional secondary + Secundária adicional + + + App data migration + Migração de dados do aplicativo + + + Archive and upload + Arquivar e enviar + + + Background + Fundo + + + Better message dates. + Datas de mensagens melhores. + + + Better notifications + Notificações melhores + + + Better security ✅ + Melhor segurança ✅ + + + Chat profile + Perfil da conversa + diff --git a/apps/ios/SimpleX Localizations/pt.xcloc/Localized Contents/pt.xliff b/apps/ios/SimpleX Localizations/pt.xcloc/Localized Contents/pt.xliff index 71c523ca4e..bc8bf79da1 100644 --- a/apps/ios/SimpleX Localizations/pt.xcloc/Localized Contents/pt.xliff +++ b/apps/ios/SimpleX Localizations/pt.xcloc/Localized Contents/pt.xliff @@ -5,9 +5,11 @@ - + + + No comment provided by engineer. @@ -15,140 +17,174 @@ Available in v5.1 No comment provided by engineer. - + + No comment provided by engineer. - + + No comment provided by engineer. - + + No comment provided by engineer. - + ( + ( No comment provided by engineer. - + (can be copied) + .(pode ser copiado) No comment provided by engineer. - + !1 colored! + !1 colorido! No comment provided by engineer. - + #secret# + #secreto# No comment provided by engineer. - + %@ + %@ No comment provided by engineer. - + %@ %@ + %@ %@ No comment provided by engineer. - + %@ / %@ + %@ / %@ No comment provided by engineer. - + %@ is connected! + %@ está conectado! notification title - + %@ is not verified + %@ não foi verificado No comment provided by engineer. - + %@ is verified + %@ foi verificado No comment provided by engineer. - + %@ servers + %@ servidores No comment provided by engineer. - + %@ wants to connect! + %@ quer se conectar! notification title - + %d days + %d dias message ttl - + %d hours + %d horas message ttl - + %d min + %d minuto message ttl - + %d months + %d meses message ttl - + %d sec + %d segundo message ttl - + %d skipped message(s) + %d mensagem(s) ignorada(s) integrity error chat item - + %lld + %lld No comment provided by engineer. - + %lld %@ + %lld %@ No comment provided by engineer. - + %lld contact(s) selected + %lld contato(s) selecionado(s) No comment provided by engineer. - + %lld file(s) with total size of %@ + %lld arquivo(s) com tamanho total de %@ No comment provided by engineer. - + %lld members + %lld membros No comment provided by engineer. - + %lld minutes + %lld minutos No comment provided by engineer. - + %lld second(s) + %lld segundo(s) No comment provided by engineer. - + %lld seconds + %lld segundos No comment provided by engineer. - + %lldd + %lldd No comment provided by engineer. - + %lldh + %lldh No comment provided by engineer. - + %lldk + %lldk No comment provided by engineer. - + %lldm + %lldm No comment provided by engineer. - + %lldmth + %lldmth No comment provided by engineer. @@ -159,64 +195,70 @@ Available in v5.1 %lldw No comment provided by engineer. - + %u messages failed to decrypt. + %u mensagens não foram descriptografadas. No comment provided by engineer. - + %u messages skipped. + %u mensagens ignoradas. No comment provided by engineer. - + ( + ( No comment provided by engineer. ) No comment provided by engineer. - - **Add new contact**: to create your one-time QR Code or link for your contact. - No comment provided by engineer. - **Create link / QR code** for your contact to use. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. + **Mais privado**: verifique novas mensagens a cada 20 minutos. O token do dispositivo é compartilhado com o servidor SimpleX Chat, mas não com quantos contatos ou mensagens você possui. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. + **Totalmente privado**: não use o servidor de notificações do SimpleX Chat, verifique as mensagens periodicamente em segundo plano (depende da frequência com que você usa o aplicativo). No comment provided by engineer. **Paste received link** or open it in the browser and tap **Open in mobile app**. No comment provided by engineer. - + **Please note**: you will NOT be able to recover or change passphrase if you lose it. + **Atenção**: Você NÃO poderá recuperar ou alterar a senha caso a perca. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. + **Recomendado**: O token do dispositivo e as notificações são enviados ao servidor de notificação do SimpleX Chat, mas não o conteúdo, o tamanho da mensagem ou de quem ela é. No comment provided by engineer. **Scan QR code**: to connect to your contact in person or via video call. No comment provided by engineer. - + **Warning**: Instant push notifications require passphrase saved in Keychain. + **Aviso**: As notificações push instantâneas exigem uma senha salva nas Chaves. No comment provided by engineer. - + **e2e encrypted** audio call + ** Criptografado e2e** chamada de áudio No comment provided by engineer. - + **e2e encrypted** video call + **Criptografado e2e** chamada de vídeo No comment provided by engineer. @@ -325,8 +367,8 @@ Available in v5.1 Add servers by scanning QR codes. No comment provided by engineer. - - Add server… + + Add server No comment provided by engineer. @@ -353,144 +395,175 @@ Available in v5.1 All group members will remain connected. No comment provided by engineer. - + All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. + Todas as mensagens serão apagadas – isso não pode ser desfeito! As mensagens serão apagadas SOMENTE para você. No comment provided by engineer. All your contacts will remain connected No comment provided by engineer. - + Allow + Permitir No comment provided by engineer. - + Allow calls only if your contact allows them. + Permita chamadas somente se seu contato permitir. No comment provided by engineer. - + Allow disappearing messages only if your contact allows it to you. + Permita o desaparecimento de mensagens somente se o seu contato permitir. No comment provided by engineer. Allow irreversible message deletion only if your contact allows it to you. No comment provided by engineer. - + Allow sending direct messages to members. + Permitir o envio de mensagens diretas aos membros. No comment provided by engineer. - + Allow sending disappearing messages. + Permitir o envio de mensagens que desaparecem. No comment provided by engineer. Allow to irreversibly delete sent messages. No comment provided by engineer. - + Allow to send voice messages. + Permitir enviar mensagens de voz. No comment provided by engineer. - + Allow voice messages only if your contact allows them. + Permita mensagens de voz apenas se o seu contato permitir. No comment provided by engineer. - + Allow voice messages? + Permitir mensagens de voz? No comment provided by engineer. - + Allow your contacts to call you. + Permita que seus contatos liguem para você. No comment provided by engineer. Allow your contacts to irreversibly delete sent messages. No comment provided by engineer. - + Allow your contacts to send disappearing messages. + Permita que seus contatos enviem mensagens que desaparecem. No comment provided by engineer. - + Allow your contacts to send voice messages. + Permita que seus contatos enviem mensagens de voz. No comment provided by engineer. - + Already connected? + Já conectado? No comment provided by engineer. - + Always use relay + Sempre usar retransmissão No comment provided by engineer. - + Answer call + Atender chamada No comment provided by engineer. - + App build: %@ + Versão do Aplicativo: %@ No comment provided by engineer. - + App icon + Ícone do Aplicativo No comment provided by engineer. - + App passcode + Senha do Aplicativo No comment provided by engineer. - + App version + Versão do Aplicativo No comment provided by engineer. - + App version: v%@ + Versão do Aplicativo: v%@ No comment provided by engineer. - + Appearance + Aparência No comment provided by engineer. - + Attach + Anexar No comment provided by engineer. - + Audio & video calls + Chamadas de áudio e vídeo No comment provided by engineer. - + Audio and video calls + Chamadas de áudio e vídeo No comment provided by engineer. - + Audio/video calls + Chamadas de áudio/vídeo chat feature - + Audio/video calls are prohibited. + Chamadas de áudio/vídeo são proibidas. No comment provided by engineer. - + Authentication cancelled + Autenticação cancelada PIN entry - + Authentication failed + Falha na autenticação No comment provided by engineer. - + Authentication is required before the call is connected, but you may miss calls. + A autenticação é necessária antes que a chamada seja conectada, mas você pode perder chamadas. No comment provided by engineer. - + Authentication unavailable + Autenticação indisponível No comment provided by engineer. - + Auto-accept contact requests + Aceitar solicitações de contato automaticamente No comment provided by engineer. @@ -545,160 +618,197 @@ Available in v5.1 Can't delete user profile! No comment provided by engineer. - + Can't invite contact! + Não é possível convidar contato! No comment provided by engineer. - + Can't invite contacts! + Não é possível convidar contatos! No comment provided by engineer. - + Cancel + Cancelar No comment provided by engineer. - + Cannot access keychain to save database password + Não é possível acessar as chaves para salvar a senha do banco de dados No comment provided by engineer. - + Cannot receive file + Não é possível receber o arquivo No comment provided by engineer. - + Change + Mudar No comment provided by engineer. Change passcode No comment provided by engineer. - + Change database passphrase? + Alterar a senha do banco de dados? No comment provided by engineer. - + Change lock mode + Alterar modo de bloqueio authentication reason - + Change member role? + Alterar a função do membro? No comment provided by engineer. - + Change passcode + Alterar senha authentication reason - + Change receiving address + Alterar endereço de recebimento No comment provided by engineer. - + Change receiving address? + Alterar endereço de recebimento? No comment provided by engineer. - + Change role + Mudar função No comment provided by engineer. - + Chat archive + Arquivar conversa No comment provided by engineer. - + Chat console + Console da conversa No comment provided by engineer. - + Chat database + Banco de Dados da conversa No comment provided by engineer. - + Chat database deleted + Banco de Dados da conversa apagado No comment provided by engineer. - + Chat database imported + Banco de Dados da conversa importado No comment provided by engineer. - + Chat is running + A conversa está em execução No comment provided by engineer. Chat is stopped No comment provided by engineer. - + Chat preferences + Preferências da conversa No comment provided by engineer. - + Chats + Conversas No comment provided by engineer. - + Check server address and try again. + Verifique o endereço do servidor e tente novamente. No comment provided by engineer. - + Chinese and Spanish interface + Interface em Chinês e Espanhol No comment provided by engineer. - + Choose file + Escolher arquivo No comment provided by engineer. - + Choose from library + Escolha da biblioteca No comment provided by engineer. - + Clear + Limpar No comment provided by engineer. - + Clear conversation + Limpar conversa No comment provided by engineer. - + Clear conversation? + Limpar conversa? No comment provided by engineer. - + Clear verification + Limpar verificação No comment provided by engineer. - + Colors + Cores No comment provided by engineer. - + Compare file + Comparar arquivo server test step - + Compare security codes with your contacts. + Compare os códigos de segurança com seus contatos. No comment provided by engineer. - + Configure ICE servers + Configurar servidores ICE No comment provided by engineer. - + Confirm + Confirme No comment provided by engineer. - + Confirm Passcode + Confirmar senha No comment provided by engineer. - + Confirm database upgrades + Confirmar atualizações do banco de dados No comment provided by engineer. - + Confirm new passphrase… + Confirme a nova senha… No comment provided by engineer. @@ -1117,8 +1227,8 @@ Available in v5.1 Direct messages chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited. No comment provided by engineer. @@ -1133,8 +1243,8 @@ Available in v5.1 Disappearing messages are prohibited in this chat. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. No comment provided by engineer. @@ -1569,16 +1679,16 @@ Available in v5.1 Group members can irreversibly delete sent messages. No comment provided by engineer. - - Group members can send direct messages. + + Members can send direct messages. No comment provided by engineer. - - Group members can send disappearing messages. + + Members can send disappearing messages. No comment provided by engineer. - - Group members can send voice messages. + + Members can send voice messages. No comment provided by engineer. @@ -1697,8 +1807,8 @@ Available in v5.1 Immediately No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam No comment provided by engineer. @@ -1810,8 +1920,8 @@ Available in v5.1 Irreversible message deletion is prohibited in this chat. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. No comment provided by engineer. @@ -2005,8 +2115,8 @@ Available in v5.1 Migration is completed No comment provided by engineer. - - Migrations: %@ + + Migrations: No comment provided by engineer. @@ -2163,8 +2273,8 @@ Available in v5.1 Onion hosts will not be used. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only client devices store user profiles, contacts, groups, and messages. No comment provided by engineer. @@ -2223,8 +2333,8 @@ Available in v5.1 Open user profiles authentication reason - - Open-source protocol and code – anybody can run the servers. + + Anybody can host servers. No comment provided by engineer. @@ -2279,8 +2389,8 @@ Available in v5.1 Paste the link you received into the box below to connect with your contact. No comment provided by engineer. - - People can connect to you only via the links you share. + + You decide who can connect. No comment provided by engineer. @@ -2983,8 +3093,8 @@ Available in v5.1 Thanks to the users – contribute via Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. + + No user identifiers. No comment provided by engineer. @@ -3028,16 +3138,16 @@ It can happen because of some bug or when the connection is compromised.The message will be marked as moderated for all members. No comment provided by engineer. - - The next generation of private messaging + + The future of messaging No comment provided by engineer. The old database was not removed during the migration, it can be deleted. No comment provided by engineer. - - The profile is only shared with your contacts. + + Your profile is stored on your device and only shared with your contacts. No comment provided by engineer. @@ -3100,8 +3210,8 @@ It can happen because of some bug or when the connection is compromised.To make a new connection No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. No comment provided by engineer. @@ -3326,8 +3436,8 @@ To connect, please ask your contact to create another connection link and check Voice messages are prohibited in this chat. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. No comment provided by engineer. @@ -3467,10 +3577,6 @@ SimpleX Lock must be enabled. You can't send messages! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - No comment provided by engineer. - You could not be verified; please try again. No comment provided by engineer. @@ -4162,6 +4268,307 @@ SimpleX servers cannot see your profile. \~strike~ No comment provided by engineer. + + ## In reply to + ## Em resposta a + copied message info + + + %@ uploaded + %@ enviado + No comment provided by engineer. + + + %d weeks + %d semanas + time interval + + + %lld messages blocked by admin + %lld mensagens bloqueadas pelo administrador + No comment provided by engineer. + + + %lld new interface languages + %lld novas interface de idiomas + No comment provided by engineer. + + + **Create 1-time link**: to create and share a new invitation link. + **Adicionar contato**: para criar um novo link de convite ou conectar-se por meio de um link que você recebeu. + No comment provided by engineer. + + + **Create group**: to create a new group. + **Criar grupo**: para criar um novo grupo. + No comment provided by engineer. + + + **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Observação**: usar o mesmo banco de dados em dois dispositivos interromperá a descriptografia de mensagens de suas conexões, como proteção de segurança. + No comment provided by engineer. + + + ## History + ## Histórico + copied message info + + + **Warning**: the archive will be removed. + **Atenção**: O arquivo será removido. + No comment provided by engineer. + + + %@ and %@ + %@ e %@ + No comment provided by engineer. + + + %@ and %@ connected + %@ e %@ conectado + No comment provided by engineer. + + + %1$@ at %2$@: + %1$@ em %2$@: + copied message info, <sender> at <time> + + + %@ connected + %@ conectado + No comment provided by engineer. + + + %@, %@ and %lld members + %@, %@ e %lld membros + No comment provided by engineer. + + + %@, %@ and %lld other members connected + %@, %@ e %lld outros membros conectados + No comment provided by engineer. + + + %lld group events + %lld eventos de grupo + No comment provided by engineer. + + + %lld messages marked deleted + %lld mensagens marcadas como excluídas + No comment provided by engineer. + + + %lld messages blocked + %lld mensagens bloqueadas + No comment provided by engineer. + + + %lld messages moderated by %@ + %lld mensagens moderadas por %@ + No comment provided by engineer. + + + (this device v%@) + (este dispositivo v%@) + No comment provided by engineer. + + + All data is erased when it is entered. + Todos os dados são apagados quando são inseridos. + No comment provided by engineer. + + + Allow irreversible message deletion only if your contact allows it to you. (24 hours) + Permita a exclusão irreversível de mensagens somente se o seu contato permitir. (24 horas) + No comment provided by engineer. + + + Apply + Aplicar + No comment provided by engineer. + + + Archive and upload + Arquivar e fazer envio + No comment provided by engineer. + + + All new messages from %@ will be hidden! + Todas as novas mensagens de %@ ficarão ocultas! + No comment provided by engineer. + + + Already connecting! + Já conectando! + No comment provided by engineer. + + + App encrypts new local files (except videos). + O aplicativo criptografa novos arquivos locais (exceto vídeos). + No comment provided by engineer. + + + App passcode is replaced with self-destruct passcode. + A senha do Aplicativo é substituída pela senha de autodestruição. + No comment provided by engineer. + + + All your contacts will remain connected. + Todos os seus contatos permanecerão conectados. + No comment provided by engineer. + + + All your contacts will remain connected. Profile update will be sent to your contacts. + Todos os seus contatos permanecerão conectados. A atualização do perfil será enviada para seus contatos. + No comment provided by engineer. + + + Allow to send files and media. + Permitir o envio de arquivos e mídia. + No comment provided by engineer. + + + App data migration + Migração de dados de aplicativos + No comment provided by engineer. + + + Archiving database + Arquivando banco de dados + No comment provided by engineer. + + + All messages will be deleted - this cannot be undone! + Todas as mensagens serão apagadas – isso não pode ser desfeito! + No comment provided by engineer. + + + All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Todos os seus contatos, conversas e arquivos serão criptografados com segurança e enviados em partes para retransmissões XFTP configuradas. + No comment provided by engineer. + + + Allow message reactions only if your contact allows them. + Permita reações às mensagens somente se o seu contato permitir. + No comment provided by engineer. + + + Allow message reactions. + Permitir reações às mensagens. + No comment provided by engineer. + + + Allow your contacts to irreversibly delete sent messages. (24 hours) + Permita que seus contatos apaguem irreversivelmente as mensagens enviadas. (24 horas) + No comment provided by engineer. + + + Already joining the group! + Entrando no grupo! + No comment provided by engineer. + + + An empty chat profile with the provided name is created, and the app opens as usual. + Um perfil de conversa vazio com o nome fornecido é criado e o aplicativo abre normalmente. + No comment provided by engineer. + + + Auto-accept + Aceitar automaticamente + No comment provided by engineer. + + + Allow your contacts adding message reactions. + Permita que seus contatos adicionem reações às mensagens. + No comment provided by engineer. + + + Allow to irreversibly delete sent messages. (24 hours) + Permitir apagar irreversivelmente as mensagens enviadas. (24 horas) + No comment provided by engineer. + + + Choose _Migrate from another device_ on the new device and scan QR code. + Escolha _Migrar de outro dispositivo_ no novo dispositivo e leia o código QR. + No comment provided by engineer. + + + Cancel migration + Cancelar migração + No comment provided by engineer. + + + Camera not available + Câmera não disponível + No comment provided by engineer. + + + Change self-destruct passcode + Alterar a senha de autodestruição + authentication reason + set passcode view + + + Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat. + A conversa foi interrompida. Se você já usou esse banco de dados em outro dispositivo, deverá transferi-lo de volta antes de iniciar a conversa. + No comment provided by engineer. + + + Chat migrated! + Conversa migrada! + No comment provided by engineer. + + + Clear private notes? + Limpar notas privadas? + No comment provided by engineer. + + + Confirm network settings + Confirme as configurações de rede + No comment provided by engineer. + + + Confirm that you remember database passphrase to migrate it. + Confirme se você se lembra da senha do banco de dados para migrá-lo. + No comment provided by engineer. + + + Change self-destruct mode + Alterar modo de autodestruição + authentication reason + + + Confirm upload + Confirmar envio + No comment provided by engineer. + + + %@ downloaded + %@ baixado + No comment provided by engineer. + + + # %@ + # %@ + copied message info title, # <title> + + + %@: + %@: + copied message info + + + %@ (current) + %@(atual) + No comment provided by engineer. + + + %@ (current): + %@ (atual): + copied message info + diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff index 2b0bf95f45..419fa75375 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (можно скопировать) @@ -109,6 +82,7 @@ %@ downloaded + %@ загружено No comment provided by engineer. @@ -126,6 +100,11 @@ %@ подтверждён No comment provided by engineer. + + %@ server + %@ сервер + No comment provided by engineer. + %@ servers %@ серверы @@ -133,6 +112,7 @@ %@ uploaded + %@ загружено No comment provided by engineer. @@ -140,6 +120,11 @@ %@ хочет соединиться! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ и %lld членов группы @@ -147,7 +132,7 @@ %@, %@ and %lld other members connected - %@, %@ и %lld других членов соединены + установлено соединение с %@, %@ и %lld другими членами группы No comment provided by engineer. @@ -160,11 +145,36 @@ %d дней time interval + + %d file(s) are still being downloaded. + %d файл(ов) загружаются. + forward confirmation reason + + + %d file(s) failed to download. + %d файл(ов) не удалось загрузить. + forward confirmation reason + + + %d file(s) were deleted. + %d файлов было удалено. + forward confirmation reason + + + %d file(s) were not downloaded. + %d файлов не было загружено. + forward confirmation reason + %d hours %d ч. time interval + + %d messages not forwarded + %d сообщений не переслано + alert title + %d min %d мин @@ -180,6 +190,11 @@ %d сек time interval + + %d seconds(s) + %d секунд + delete after time + %d skipped message(s) %d пропущенных сообщение(й) @@ -217,7 +232,7 @@ %lld members - Членов группы: %lld + %lld членов No comment provided by engineer. @@ -250,11 +265,6 @@ %lld новых языков интерфейса No comment provided by engineer. - - %lld second(s) - %lld секунд - No comment provided by engineer. - %lld seconds %lld секунд @@ -305,11 +315,6 @@ %u сообщений пропущено. No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) (новое) @@ -320,19 +325,9 @@ (это устройство v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - **Добавить контакт**: создать новую ссылку-приглашение или подключиться через полученную ссылку. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Добавить новый контакт**: чтобы создать одноразовый QR код или ссылку для Вашего контакта. + + **Create 1-time link**: to create and share a new invitation link. + **Добавить контакт**: создать и поделиться новой ссылкой-приглашением. No comment provided by engineer. @@ -340,18 +335,19 @@ **Создать группу**: создать новую группу. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. - **Более конфиденциально**: проверять новые сообщения каждые 20 минут. Токен устройства будет отправлен на сервер уведомлений SimpleX Chat, но у сервера не будет информации о количестве контактов и сообщений. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. + **Более конфиденциально**: проверять новые сообщения каждые 20 минут. Только токен устройства будет отправлен на сервер уведомлений SimpleX Chat, но у сервера не будет информации о количестве контактов и какой либо информации о сообщениях. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). - **Самый конфиденциальный**: не использовать сервер уведомлений SimpleX Chat, проверять сообщения периодически в фоновом режиме (зависит от того насколько часто Вы используете приложение). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. + **Самый конфиденциальный**: не использовать сервер уведомлений SimpleX Chat. Сообщения проверяются в фоновом режиме, когда система позволяет, в зависимости от того, как часто Вы используете приложение. No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Обратите внимание**: использование одной и той же базы данных на двух устройствах нарушит расшифровку сообщений от ваших контактов, как свойство защиты соединений. No comment provided by engineer. @@ -359,11 +355,16 @@ **Внимание**: Вы не сможете восстановить или поменять пароль, если Вы его потеряете. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Рекомендовано**: токен устройства и уведомления отправляются на сервер SimpleX Chat, но сервер не получает сами сообщения, их размер или от кого они. No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + **Сканировать / Вставить ссылку**: чтобы соединиться через полученную ссылку. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **Внимание**: для работы мгновенных уведомлений пароль должен быть сохранен в Keychain. @@ -371,6 +372,7 @@ **Warning**: the archive will be removed. + **Внимание**: архив будет удален. No comment provided by engineer. @@ -388,11 +390,6 @@ \*жирный* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -429,11 +426,6 @@ - история редактирования. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 сек @@ -447,7 +439,8 @@ 1 day 1 день - time interval + delete after time +time interval 1 hour @@ -462,12 +455,29 @@ 1 month 1 месяц - time interval + delete after time +time interval 1 week 1 неделю - time interval + delete after time +time interval + + + 1 year + 1 год + delete after time + + + 1-time link + Одноразовая ссылка + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + Одноразовая ссылка может быть использована *только с одним контактом* - поделитесь при встрече или через любой мессенджер. + No comment provided by engineer. 5 minutes @@ -484,11 +494,6 @@ 30 секунд No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -519,8 +524,8 @@ A separate TCP connection will be used **for each contact and group member**. **Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail. - Отдельное TCP-соединение (и авторизация SOCKS) будет использоваться **для каждого контакта и члена группы**. -**Обратите внимание**: если у Вас много контактов, потребление батареи и трафика может быть значительно выше, и некоторые соединения могут не работать. + Будет использовано отдельное TCP соединение **для каждого контакта и члена группы**. +**Примечание**: Чем больше подключений, тем быстрее разряжается батарея и расходуется трафик, а некоторые соединения могут отваливаться. No comment provided by engineer. @@ -538,31 +543,32 @@ Прекратить изменение адреса? No comment provided by engineer. - - About SimpleX - О SimpleX - No comment provided by engineer. - About SimpleX Chat Информация о SimpleX Chat No comment provided by engineer. - - About SimpleX address - Об адресе SimpleX + + About operators + Об операторах No comment provided by engineer. - - Accent color - Основной цвет + + Accent + Акцент No comment provided by engineer. Accept Принять accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Принять условия + No comment provided by engineer. Accept connection request? @@ -577,21 +583,47 @@ Accept incognito Принять инкогнито - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + Принятые условия + No comment provided by engineer. + + + Acknowledged + Подтверждено + No comment provided by engineer. + + + Acknowledgement errors + Ошибки подтверждения + No comment provided by engineer. + + + Active + Активный + token status text + + + Active connections + Активные соединения + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Добавьте адрес в свой профиль, чтобы Ваши контакты могли поделиться им. Профиль будет отправлен Вашим контактам. No comment provided by engineer. - - Add contact - Добавить контакт + + Add friends + Добавить друзей No comment provided by engineer. - - Add preset servers - Добавить серверы по умолчанию + + Add list + Добавить список No comment provided by engineer. @@ -599,14 +631,19 @@ Добавить профиль No comment provided by engineer. + + Add server + Добавить сервер + No comment provided by engineer. + Add servers by scanning QR codes. Добавить серверы через QR код. No comment provided by engineer. - - Add server… - Добавить сервер… + + Add team members + Добавить сотрудников No comment provided by engineer. @@ -614,11 +651,46 @@ Добавить на другое устройство No comment provided by engineer. + + Add to list + Добавить в список + No comment provided by engineer. + Add welcome message Добавить приветственное сообщение No comment provided by engineer. + + Add your team members to the conversations. + Добавьте сотрудников в разговор. + No comment provided by engineer. + + + Added media & file servers + Дополнительные серверы файлов и медиа + No comment provided by engineer. + + + Added message servers + Дополнительные серверы сообщений + No comment provided by engineer. + + + Additional accent + Дополнительный акцент + No comment provided by engineer. + + + Additional accent 2 + Дополнительный акцент 2 + No comment provided by engineer. + + + Additional secondary + Вторичный 2 + No comment provided by engineer. + Address Адрес @@ -629,8 +701,19 @@ Изменение адреса будет прекращено. Будет использоваться старый адрес. No comment provided by engineer. + + Address or 1-time link? + Адрес или одноразовая ссылка? + No comment provided by engineer. + + + Address settings + Настройки адреса + No comment provided by engineer. + Admins can block a member for all. + Админы могут заблокировать члена группы. No comment provided by engineer. @@ -643,6 +726,16 @@ Настройки сети No comment provided by engineer. + + Advanced settings + Настройки сети + No comment provided by engineer. + + + All + Все + No comment provided by engineer. + All app data is deleted. Все данные приложения будут удалены. @@ -653,14 +746,29 @@ Все чаты и сообщения будут удалены - это нельзя отменить! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + Все чаты будут удалены из списка %@, и список удален. + alert message + All data is erased when it is entered. Все данные удаляются при его вводе. No comment provided by engineer. + + All data is kept private on your device. + Все данные хранятся только на вашем устройстве. + No comment provided by engineer. + All group members will remain connected. - Все члены группы, которые соединились через эту ссылку, останутся в группе. + Все члены группы останутся соединены. + No comment provided by engineer. + + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + Все сообщения и файлы отправляются с **end-to-end шифрованием**, с постквантовой безопасностью в прямых разговорах. No comment provided by engineer. @@ -678,6 +786,20 @@ Все новые сообщения от %@ будут скрыты! No comment provided by engineer. + + All profiles + Все профили + profile dropdown + + + All reports will be archived for you. + Все сообщения о нарушениях будут заархивированы для вас. + No comment provided by engineer. + + + All servers + No comment provided by engineer. + All your contacts will remain connected. Все контакты, которые соединились через этот адрес, сохранятся. @@ -690,6 +812,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Все ваши контакты, разговоры и файлы будут надежно зашифрованы и загружены на выбранные XFTP серверы. No comment provided by engineer. @@ -702,11 +825,21 @@ Разрешить звонки, только если их разрешает Ваш контакт. No comment provided by engineer. + + Allow calls? + Разрешить звонки? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Разрешить исчезающие сообщения, только если Ваш контакт разрешает их Вам. No comment provided by engineer. + + Allow downgrade + Разрешить прямую доставку + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Разрешить необратимое удаление сообщений, только если Ваш контакт разрешает это Вам. (24 часа) @@ -724,7 +857,7 @@ Allow sending direct messages to members. - Разрешить посылать прямые сообщения членам группы. + Разрешить личные сообщения членам группы. No comment provided by engineer. @@ -732,11 +865,26 @@ Разрешить посылать исчезающие сообщения. No comment provided by engineer. + + Allow sharing + Разрешить поделиться + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Разрешить необратимо удалять отправленные сообщения. (24 часа) No comment provided by engineer. + + Allow to report messsages to moderators. + Разрешить отправлять сообщения о нарушениях модераторам. + No comment provided by engineer. + + + Allow to send SimpleX links. + Разрешить отправлять ссылки SimpleX. + No comment provided by engineer. + Allow to send files and media. Разрешить посылать файлы и медиа. @@ -797,6 +945,11 @@ Вступление в группу уже начато! No comment provided by engineer. + + Always use private routing. + Всегда использовать конфиденциальную доставку. + No comment provided by engineer. + Always use relay Всегда соединяться через relay @@ -807,11 +960,21 @@ Будет создан пустой профиль чата с указанным именем, и приложение откроется в обычном режиме. No comment provided by engineer. + + Another reason + Другая причина + report reason + Answer call Принять звонок No comment provided by engineer. + + Anybody can host servers. + Кто угодно может запустить сервер. + No comment provided by engineer. + App build: %@ Сборка приложения: %@ @@ -819,6 +982,7 @@ App data migration + Миграция данных No comment provided by engineer. @@ -826,6 +990,11 @@ Приложение шифрует новые локальные файлы (кроме видео). No comment provided by engineer. + + App group: + Группа приложения: + No comment provided by engineer. + App icon Иконка @@ -841,6 +1010,11 @@ Код доступа в приложение будет заменен кодом самоуничтожения. No comment provided by engineer. + + App session + Сессия приложения + No comment provided by engineer. + App version Версия приложения @@ -858,14 +1032,62 @@ Apply + Применить + No comment provided by engineer. + + + Apply to + Применить к + No comment provided by engineer. + + + Archive + Архивировать + No comment provided by engineer. + + + Archive %lld reports? + Архивировать %lld сообщений о нарушениях? + No comment provided by engineer. + + + Archive all reports? + Архивировать все сообщения о нарушениях? No comment provided by engineer. Archive and upload + Архивировать и загрузить + No comment provided by engineer. + + + Archive contacts to chat later. + Архивируйте контакты чтобы продолжить переписку. + No comment provided by engineer. + + + Archive report + Архивировать сообщение о нарушении + No comment provided by engineer. + + + Archive report? + Архивировать сообщение о нарушении? + No comment provided by engineer. + + + Archive reports + Архивировать сообщения о нарушениях + swipe action + + + Archived contacts + Архивированные контакты No comment provided by engineer. Archiving database + Подготовка архива No comment provided by engineer. @@ -928,11 +1150,21 @@ Автоприем изображений No comment provided by engineer. + + Auto-accept settings + Настройки автоприема + alert title + Back Назад No comment provided by engineer. + + Background + Фон + No comment provided by engineer. + Bad desktop address Неверный адрес компьютера @@ -948,16 +1180,61 @@ Ошибка хэш сообщения No comment provided by engineer. + + Better calls + Улучшенные звонки + No comment provided by engineer. + Better groups Улучшенные группы No comment provided by engineer. + + Better groups performance + Улучшенная производительность групп + No comment provided by engineer. + + + Better message dates. + Улучшенные даты сообщений. + No comment provided by engineer. + Better messages Улучшенные сообщения No comment provided by engineer. + + Better networking + Улучшенные сетевые функции + No comment provided by engineer. + + + Better notifications + Улучшенные уведомления + No comment provided by engineer. + + + Better privacy and security + Улучшенная конфиденциальность и безопасность + No comment provided by engineer. + + + Better security ✅ + Улучшенная безопасность ✅ + No comment provided by engineer. + + + Better user experience + Улучшенный интерфейс + No comment provided by engineer. + + + Black + Черная + No comment provided by engineer. + Block Заблокировать @@ -970,7 +1247,7 @@ Block group members - Блокируйте членов группы + Заблокировать членов группы No comment provided by engineer. @@ -980,7 +1257,7 @@ Block member for all? - Заблокировать члена для всех? + Заблокировать для всех? No comment provided by engineer. @@ -993,6 +1270,16 @@ Заблокирован администратором No comment provided by engineer. + + Blur for better privacy. + Размыть для конфиденциальности. + No comment provided by engineer. + + + Blur media + Размытие изображений + No comment provided by engineer. + Both you and your contact can add message reactions. И Вы, и Ваш контакт можете добавлять реакции на сообщения. @@ -1023,11 +1310,35 @@ Болгарский, финский, тайский и украинский - благодаря пользователям и [Weblate] (https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + Бизнес адрес + No comment provided by engineer. + + + Business chats + Бизнес разговоры + No comment provided by engineer. + + + Businesses + Бизнесы + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). По профилю чата или [по соединению](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (БЕТА). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + Используя SimpleX Chat, Вы согласны: +- отправлять только законные сообщения в публичных группах. +- уважать других пользователей – не отправлять спам. + No comment provided by engineer. + Call already ended! Звонок уже завершен! @@ -1038,11 +1349,26 @@ Звонки No comment provided by engineer. + + Calls prohibited! + Звонки запрещены! + No comment provided by engineer. + Camera not available Камера недоступна No comment provided by engineer. + + Can't call contact + Не удается позвонить контакту + No comment provided by engineer. + + + Can't call member + Не удаётся позвонить члену группы + No comment provided by engineer. + Can't invite contact! Нельзя пригласить контакт! @@ -1053,13 +1379,20 @@ Нельзя пригласить контакты! No comment provided by engineer. + + Can't message member + Не удаётся отправить сообщение члену группы + No comment provided by engineer. + Cancel Отменить - No comment provided by engineer. + alert action +alert button Cancel migration + Отменить миграцию No comment provided by engineer. @@ -1067,9 +1400,24 @@ Ошибка доступа к Keychain при сохранении пароля No comment provided by engineer. + + Cannot forward message + Невозможно переслать сообщение + No comment provided by engineer. + Cannot receive file Невозможно получить файл + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Превышено количество сообщений - предыдущие сообщения не доставлены. + snd error text + + + Cellular + Мобильная сеть No comment provided by engineer. @@ -1077,6 +1425,16 @@ Поменять No comment provided by engineer. + + Change automatic message deletion? + Измененить автоматическое удаление сообщений? + alert title + + + Change chat profiles + Поменять профили + authentication reason + Change database passphrase? Поменять пароль базы данных? @@ -1121,11 +1479,26 @@ Change self-destruct passcode Изменить код самоуничтожения authentication reason - set passcode view +set passcode view - - Chat archive - Архив чата + + Chat + Разговор + No comment provided by engineer. + + + Chat already exists + Разговор уже существует + No comment provided by engineer. + + + Chat already exists! + Разговор уже существует! + No comment provided by engineer. + + + Chat colors + Цвета чата No comment provided by engineer. @@ -1143,6 +1516,11 @@ Данные чата удалены No comment provided by engineer. + + Chat database exported + Данные чата экспортированы + No comment provided by engineer. + Chat database imported Архив чата импортирован @@ -1163,8 +1541,14 @@ Чат остановлен. Если вы уже использовали эту базу данных на другом устройстве, перенесите ее обратно до запуска чата. No comment provided by engineer. + + Chat list + Список чатов + No comment provided by engineer. + Chat migrated! + Чат мигрирован! No comment provided by engineer. @@ -1172,15 +1556,50 @@ Предпочтения No comment provided by engineer. + + Chat preferences were changed. + Настройки чата были изменены. + alert message + + + Chat profile + Профиль чата + No comment provided by engineer. + + + Chat theme + Тема чата + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + Разговор будет удален для всех участников - это действие нельзя отменить! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + Разговор будет удален для Вас - это действие нельзя отменить! + No comment provided by engineer. + Chats Чаты No comment provided by engineer. + + Check messages every 20 min. + Проверять сообщения каждые 20 минут. + No comment provided by engineer. + + + Check messages when allowed. + Проверять сообщения по возможности. + No comment provided by engineer. + Check server address and try again. Проверьте адрес сервера и попробуйте снова. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1189,6 +1608,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + Выберите _Мигрировать с другого устройства_ на новом устройстве и сосканируйте QR код. No comment provided by engineer. @@ -1201,10 +1621,25 @@ Выбрать из библиотеки No comment provided by engineer. + + Chunks deleted + Блоков удалено + No comment provided by engineer. + + + Chunks downloaded + Блоков принято + No comment provided by engineer. + + + Chunks uploaded + Блоков загружено + No comment provided by engineer. + Clear Очистить - No comment provided by engineer. + swipe action Clear conversation @@ -1216,6 +1651,16 @@ Очистить разговор? No comment provided by engineer. + + Clear group? + Очистить группу? + No comment provided by engineer. + + + Clear or delete group? + Очистить или удалить группу? + No comment provided by engineer. + Clear private notes? Очистить личные заметки? @@ -1226,11 +1671,21 @@ Сбросить подтверждение No comment provided by engineer. - - Colors - Цвета + + Color chats with the new themes. + Добавьте цвета к чатам в настройках. No comment provided by engineer. + + Color mode + Режим цветов + No comment provided by engineer. + + + Community guidelines violation + Нарушение правил группы + report reason + Compare file Сравнение файла @@ -1241,11 +1696,56 @@ Сравните код безопасности с Вашими контактами. No comment provided by engineer. + + Completed + Готово + No comment provided by engineer. + + + Conditions accepted on: %@. + Условия приняты: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + Условия приняты для оператора(ов): **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + Условия уже приняты для следующих оператора(ов): **%@**. + No comment provided by engineer. + + + Conditions of use + Условия использования + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + Условия будут приняты для оператора(ов): **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + Условия будут приняты: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + Условия будут автоматически приняты для включенных операторов: %@. + No comment provided by engineer. + Configure ICE servers Настройка ICE серверов No comment provided by engineer. + + Configure server operators + Настроить операторов серверов + No comment provided by engineer. + Confirm Подтвердить @@ -1256,13 +1756,24 @@ Подтвердить Код No comment provided by engineer. + + Confirm contact deletion? + Потвердить удаление контакта? + No comment provided by engineer. + Confirm database upgrades Подтвердить обновление базы данных No comment provided by engineer. + + Confirm files from unknown servers. + Подтверждать файлы с неизвестных серверов. + No comment provided by engineer. + Confirm network settings + Подтвердите настройки сети No comment provided by engineer. @@ -1277,12 +1788,19 @@ Confirm that you remember database passphrase to migrate it. + Подтвердите, что Вы помните пароль базы данных для ее миграции. No comment provided by engineer. Confirm upload + Подтвердить загрузку No comment provided by engineer. + + Confirmed + Подтвержденный + token status text + Connect Соединиться @@ -1303,6 +1821,11 @@ Подключиться к компьютеру No comment provided by engineer. + + Connect to your friends faster. + Соединяйтесь с друзьями быстрее. + No comment provided by engineer. + Connect to yourself? Соединиться с самим собой? @@ -1342,16 +1865,31 @@ This is your own one-time link! Соединиться с %@ No comment provided by engineer. + + Connected + Соединено + No comment provided by engineer. + Connected desktop Подключенный компьютер No comment provided by engineer. + + Connected servers + Подключенные серверы + No comment provided by engineer. + Connected to desktop Компьютер подключен No comment provided by engineer. + + Connecting + Соединяется + No comment provided by engineer. + Connecting to server… Устанавливается соединение с сервером… @@ -1362,6 +1900,11 @@ This is your own one-time link! Устанавливается соединение с сервером… (ошибка: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + Контакт соединяется, подождите или проверьте позже! + No comment provided by engineer. + Connecting to desktop Подключение к компьютеру @@ -1372,6 +1915,16 @@ This is your own one-time link! Соединение No comment provided by engineer. + + Connection and servers status. + Состояние соединения и серверов. + No comment provided by engineer. + + + Connection blocked + Соединение заблокировано + No comment provided by engineer. + Connection error Ошибка соединения @@ -1382,11 +1935,38 @@ This is your own one-time link! Ошибка соединения (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + Соединение заблокировано сервером оператора: +%@ + No comment provided by engineer. + + + Connection not ready. + Соединение не готово. + No comment provided by engineer. + + + Connection notifications + Уведомления по соединениям + No comment provided by engineer. + Connection request sent! Запрос на соединение отправлен! No comment provided by engineer. + + Connection requires encryption renegotiation. + Соединение требует повторного согласования шифрования. + No comment provided by engineer. + + + Connection security + Безопасность соединения + No comment provided by engineer. + Connection terminated Подключение прервано @@ -1397,6 +1977,16 @@ This is your own one-time link! Превышено время соединения No comment provided by engineer. + + Connection with desktop stopped + Соединение с компьютером остановлено + No comment provided by engineer. + + + Connections + Соединения + No comment provided by engineer. + Contact allows Контакт разрешает @@ -1407,6 +1997,11 @@ This is your own one-time link! Существующий контакт No comment provided by engineer. + + Contact deleted! + Контакт удален! + No comment provided by engineer. + Contact hidden: Контакт скрыт: @@ -1417,9 +2012,9 @@ This is your own one-time link! Соединение с контактом установлено notification - - Contact is not connected yet! - Соединение еще не установлено! + + Contact is deleted. + Контакт удален. No comment provided by engineer. @@ -1432,6 +2027,11 @@ This is your own one-time link! Предпочтения контакта No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + Контакт будет удален — это нельзя отменить! + No comment provided by engineer. + Contacts Контакты @@ -1442,21 +2042,41 @@ This is your own one-time link! Контакты могут помечать сообщения для удаления; Вы сможете просмотреть их. No comment provided by engineer. + + Content violates conditions of use + Содержание нарушает условия использования + blocking reason + Continue Продолжить No comment provided by engineer. + + Conversation deleted! + Разговор удален! + No comment provided by engineer. + Copy Скопировать - chat item action + No comment provided by engineer. + + + Copy error + Ошибка копирования + No comment provided by engineer. Core version: v%@ Версия ядра: v%@ No comment provided by engineer. + + Corner + Угол + No comment provided by engineer. + Correct name to %@? Исправить имя на %@? @@ -1467,6 +2087,11 @@ This is your own one-time link! Создать No comment provided by engineer. + + Create 1-time link + Создать одноразовую ссылку + No comment provided by engineer. + Create SimpleX address Создать адрес SimpleX @@ -1477,11 +2102,6 @@ This is your own one-time link! Создайте группу, используя случайный профиль. No comment provided by engineer. - - Create an address to let people connect with you. - Создайте адрес, чтобы можно было соединиться с вами. - No comment provided by engineer. - Create file Создание файла @@ -1502,6 +2122,11 @@ This is your own one-time link! Создать ссылку No comment provided by engineer. + + Create list + Создать список + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 Создайте новый профиль в [приложении для компьютера](https://simplex.chat/downloads/). 💻 @@ -1527,6 +2152,11 @@ This is your own one-time link! Создать профиль No comment provided by engineer. + + Created + Создано + No comment provided by engineer. + Created at Создано @@ -1537,13 +2167,9 @@ This is your own one-time link! Создано: %@ copied message info - - Created on %@ - Дата создания %@ - No comment provided by engineer. - Creating archive link + Создание ссылки на архив No comment provided by engineer. @@ -1556,11 +2182,21 @@ This is your own one-time link! Текущий Код No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + Текст условий использования не может быть показан, вы можете посмотреть их через ссылку: + No comment provided by engineer. + Current passphrase… Текущий пароль… No comment provided by engineer. + + Current profile + Текущий профиль + No comment provided by engineer. + Currently maximum supported file size is %@. Максимальный размер файла - %@. @@ -1571,11 +2207,26 @@ This is your own one-time link! Пользовательское время No comment provided by engineer. + + Customizable message shape. + Настраиваемая форма сообщений. + No comment provided by engineer. + + + Customize theme + Настроить тему + No comment provided by engineer. + Dark Тёмная No comment provided by engineer. + + Dark mode colors + Цвета тёмного режима + No comment provided by engineer. + Database ID ID базы данных @@ -1674,6 +2325,11 @@ This is your own one-time link! Данные чата будут мигрированы при перезапуске No comment provided by engineer. + + Debug delivery + Отладка доставки + No comment provided by engineer. + Decentralized Децентрализованный @@ -1687,18 +2343,19 @@ This is your own one-time link! Delete Удалить - chat item action + alert action +swipe action + + + Delete %lld messages of members? + Удалить %lld сообщений членов группы? + No comment provided by engineer. Delete %lld messages? Удалить %lld сообщений? No comment provided by engineer. - - Delete Contact - Удалить контакт - No comment provided by engineer. - Delete address Удалить адрес @@ -1724,14 +2381,14 @@ This is your own one-time link! Удалить и уведомить контакт No comment provided by engineer. - - Delete archive - Удалить архив + + Delete chat + Удалить разговор No comment provided by engineer. - - Delete chat archive? - Удалить архив чата? + + Delete chat messages from your device. + Удалить сообщения с вашего устройства. No comment provided by engineer. @@ -1744,6 +2401,11 @@ This is your own one-time link! Удалить профиль? No comment provided by engineer. + + Delete chat? + Удалить разговор? + No comment provided by engineer. + Delete connection Удалить соединение @@ -1754,11 +2416,9 @@ This is your own one-time link! Удалить контакт No comment provided by engineer. - - Delete contact? -This cannot be undone! - Удалить контакт? -Это не может быть отменено! + + Delete contact? + Удалить контакт? No comment provided by engineer. @@ -1768,6 +2428,7 @@ This cannot be undone! Delete database from this device + Удалить базу данных с этого устройства No comment provided by engineer. @@ -1820,6 +2481,11 @@ This cannot be undone! Удалить ссылку? No comment provided by engineer. + + Delete list? + Удалить список? + alert title + Delete member message? Удалить сообщение участника? @@ -1833,7 +2499,7 @@ This cannot be undone! Delete messages Удалить сообщения - No comment provided by engineer. + alert button Delete messages after @@ -1850,9 +2516,9 @@ This cannot be undone! Удалить предыдущую версию данных? No comment provided by engineer. - - Delete pending connection - Удалить соединение + + Delete or moderate up to 200 messages. + Удаляйте или модерируйте до 200 сообщений. No comment provided by engineer. @@ -1870,11 +2536,31 @@ This cannot be undone! Удаление очереди server test step + + Delete report + Удалить сообщение о нарушении + No comment provided by engineer. + + + Delete up to 20 messages at once. + Удаляйте до 20 сообщений за раз. + No comment provided by engineer. + Delete user profile? Удалить профиль пользователя? No comment provided by engineer. + + Delete without notification + Удалить без уведомления + No comment provided by engineer. + + + Deleted + Удалено + No comment provided by engineer. + Deleted at Удалено @@ -1885,6 +2571,16 @@ This cannot be undone! Удалено: %@ copied message info + + Deletion errors + Ошибки удаления + No comment provided by engineer. + + + Delivered even when Apple drops them. + Доставляются даже тогда, когда Apple их теряет. + No comment provided by engineer. + Delivery Доставка @@ -1920,11 +2616,41 @@ This cannot be undone! Компьютеры No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + Адрес сервера назначения %@ несовместим с настройками пересылающего сервера %@. + No comment provided by engineer. + + + Destination server error: %@ + Ошибка сервера получателя: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + Версия сервера назначения %@ несовместима с пересылающим сервером %@. + No comment provided by engineer. + + + Detailed statistics + Подробная статистика + No comment provided by engineer. + + + Details + Подробности + No comment provided by engineer. + Develop Для разработчиков No comment provided by engineer. + + Developer options + Опции разработчика + No comment provided by engineer. + Developer tools Инструменты разработчика @@ -1955,9 +2681,13 @@ This cannot be undone! Прямые сообщения chat feature - - Direct messages between members are prohibited in this group. - Прямые сообщения между членами группы запрещены. + + Direct messages between members are prohibited in this chat. + Личные сообщения запрещены в этой группе. + No comment provided by engineer. + + + Direct messages between members are prohibited. No comment provided by engineer. @@ -1970,11 +2700,26 @@ This cannot be undone! Отключить блокировку SimpleX authentication reason + + Disable automatic message deletion? + Отключить автоматическое удаление сообщений? + alert title + + + Disable delete messages + Отключить удаление сообщений + alert button + Disable for all Выключить для всех No comment provided by engineer. + + Disabled + Выключено + No comment provided by engineer. + Disappearing message Исчезающее сообщение @@ -1990,8 +2735,8 @@ This cannot be undone! Исчезающие сообщения запрещены в этом чате. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. Исчезающие сообщения запрещены в этой группе. No comment provided by engineer. @@ -2025,11 +2770,21 @@ This cannot be undone! Обнаружение по локальной сети No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + Не отправлять сообщения напрямую, даже если сервер получателя не поддерживает конфиденциальную доставку. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. Не используйте SimpleX для экстренных звонков. No comment provided by engineer. + + Do NOT use private routing. + Не использовать конфиденциальную доставку. + No comment provided by engineer. + Do it later Отложить @@ -2037,7 +2792,16 @@ This cannot be undone! Do not send history to new members. - Не отправлять историю новым членам. + No comment provided by engineer. + + + Do not use credentials with proxy. + Не использовать учетные данные с прокси. + No comment provided by engineer. + + + Documents: + Документы: No comment provided by engineer. @@ -2050,18 +2814,40 @@ This cannot be undone! Не включать No comment provided by engineer. + + Don't miss important messages. + Не пропустите важные сообщения. + No comment provided by engineer. + Don't show again Не показывать No comment provided by engineer. + + Done + Готово + No comment provided by engineer. + Downgrade and open chat Откатить версию и открыть чат No comment provided by engineer. + + Download + Загрузить + alert button +chat item action + + + Download errors + Ошибки приема + No comment provided by engineer. + Download failed + Ошибка загрузки No comment provided by engineer. @@ -2069,12 +2855,29 @@ This cannot be undone! Загрузка файла server test step + + Download files + Загрузить файлы + alert action + + + Downloaded + Принято + No comment provided by engineer. + + + Downloaded files + Принятые файлы + No comment provided by engineer. + Downloading archive + Загрузка архива No comment provided by engineer. Downloading link details + Загрузка ссылки архива No comment provided by engineer. @@ -2087,6 +2890,11 @@ This cannot be undone! Длительность No comment provided by engineer. + + E2E encrypted notifications. + E2E зашифрованные нотификации. + No comment provided by engineer. + Edit Редактировать @@ -2107,6 +2915,10 @@ This cannot be undone! Включить (кроме исключений) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + No comment provided by engineer. + Enable SimpleX Lock Включить блокировку SimpleX @@ -2120,7 +2932,7 @@ This cannot be undone! Enable automatic message deletion? Включить автоматическое удаление сообщений? - No comment provided by engineer. + alert title Enable camera access @@ -2134,6 +2946,7 @@ This cannot be undone! Enable in direct chats (BETA)! + Включите для контактов (BETA)! No comment provided by engineer. @@ -2166,6 +2979,16 @@ This cannot be undone! Включить код самоуничтожения set passcode view + + Enabled + Включено + No comment provided by engineer. + + + Enabled for + Включено для + No comment provided by engineer. + Encrypt Зашифровать @@ -2236,6 +3059,11 @@ This cannot be undone! Ошибка нового соглашения о шифровании. No comment provided by engineer. + + Encryption renegotiation in progress. + Выполняется повторное согласование шифрования. + No comment provided by engineer. + Enter Passcode Введите Код @@ -2253,6 +3081,7 @@ This cannot be undone! Enter passphrase + Введите пароль No comment provided by engineer. @@ -2300,30 +3129,35 @@ This cannot be undone! Ошибка при прекращении изменения адреса No comment provided by engineer. + + Error accepting conditions + Ошибка приема условий + alert title + Error accepting contact request Ошибка при принятии запроса на соединение No comment provided by engineer. - - Error accessing database file - Ошибка при доступе к данным чата - No comment provided by engineer. - Error adding member(s) - Ошибка при добавлении членов группы No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + Ошибка добавления сервера + alert title Error changing address Ошибка при изменении адреса No comment provided by engineer. + + Error changing connection profile + Ошибка при изменении профиля соединения + No comment provided by engineer. + Error changing role Ошибка при изменении роли @@ -2334,6 +3168,21 @@ This cannot be undone! Ошибка при изменении настройки No comment provided by engineer. + + Error changing to incognito! + Ошибка при смене на Инкогнито! + No comment provided by engineer. + + + Error checking token status + Ошибка проверки статуса токена + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + Ошибка подключения к пересылающему серверу %@. Попробуйте позже. + No comment provided by engineer. + Error creating address Ошибка при создании адреса @@ -2349,9 +3198,13 @@ This cannot be undone! Ошибка при создании ссылки группы No comment provided by engineer. + + Error creating list + Ошибка создания списка + alert title + Error creating member contact - Ошибка создания контакта с членом группы No comment provided by engineer. @@ -2364,6 +3217,11 @@ This cannot be undone! Ошибка создания профиля! No comment provided by engineer. + + Error creating report + Ошибка создания сообщения о нарушении + No comment provided by engineer. + Error decrypting file Ошибка расшифровки файла @@ -2384,11 +3242,6 @@ This cannot be undone! Ошибка при удалении соединения No comment provided by engineer. - - Error deleting contact - Ошибка при удалении контакта - No comment provided by engineer. - Error deleting database Ошибка при удалении данных чата @@ -2411,6 +3264,7 @@ This cannot be undone! Error downloading the archive + Ошибка загрузки архива No comment provided by engineer. @@ -2433,6 +3287,11 @@ This cannot be undone! Ошибка при экспорте архива чата No comment provided by engineer. + + Error exporting theme: %@ + Ошибка экспорта темы: %@ + No comment provided by engineer. + Error importing chat database Ошибка при импорте архива чата @@ -2443,9 +3302,14 @@ This cannot be undone! Ошибка при вступлении в группу No comment provided by engineer. - - Error loading %@ servers - Ошибка загрузки %@ серверов + + Error loading servers + Ошибка загрузки серверов + alert title + + + Error migrating settings + Ошибка миграции настроек No comment provided by engineer. @@ -2456,16 +3320,35 @@ This cannot be undone! Error receiving file Ошибка при получении файла + alert title + + + Error reconnecting server + Ошибка переподключения к серверу No comment provided by engineer. + + Error reconnecting servers + Ошибка переподключения к серверам + No comment provided by engineer. + + + Error registering for notifications + Ошибка регистрации для уведомлений + alert title + Error removing member - Ошибка при удалении члена группы No comment provided by engineer. - - Error saving %@ servers - Ошибка при сохранении %@ серверов + + Error reordering lists + Ошибка сортировки списков + alert title + + + Error resetting statistics + Ошибка сброса статистики No comment provided by engineer. @@ -2473,6 +3356,11 @@ This cannot be undone! Ошибка при сохранении ICE серверов No comment provided by engineer. + + Error saving chat list + Ошибка сохранения списка чатов + alert title + Error saving group profile Ошибка при сохранении профиля группы @@ -2488,8 +3376,14 @@ This cannot be undone! Ошибка сохранения пароля в Keychain No comment provided by engineer. + + Error saving servers + Ошибка сохранения серверов + alert title + Error saving settings + Ошибка сохранения настроек when migrating @@ -2509,7 +3403,6 @@ This cannot be undone! Error sending member contact invitation - Ошибка отправки приглашения члену группы No comment provided by engineer. @@ -2532,16 +3425,26 @@ This cannot be undone! Ошибка при остановке чата No comment provided by engineer. + + Error switching profile + Ошибка переключения профиля + No comment provided by engineer. + Error switching profile! Ошибка выбора профиля! - No comment provided by engineer. + alertTitle Error synchronizing connection Ошибка синхронизации соединения No comment provided by engineer. + + Error testing server connection + Ошибка проверки соединения с сервером + No comment provided by engineer. + Error updating group link Ошибка обновления ссылки группы @@ -2552,6 +3455,11 @@ This cannot be undone! Ошибка при обновлении сообщения No comment provided by engineer. + + Error updating server + Ошибка сохранения сервера + alert title + Error updating settings Ошибка при сохранении настроек сети @@ -2564,10 +3472,12 @@ This cannot be undone! Error uploading the archive + Ошибка загрузки архива No comment provided by engineer. Error verifying passphrase: + Ошибка подтверждения пароля: No comment provided by engineer. @@ -2578,7 +3488,9 @@ This cannot be undone! Error: %@ Ошибка: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2590,6 +3502,16 @@ This cannot be undone! Ошибка: данные чата не найдены No comment provided by engineer. + + Errors + Ошибки + No comment provided by engineer. + + + Errors in servers configuration. + Ошибки в настройках серверов. + servers error + Even when disabled in the conversation. Даже когда они выключены в разговоре. @@ -2605,6 +3527,11 @@ This cannot be undone! Раскрыть chat item action + + Expired + Истекший + token status text + Export database Экспорт архива чата @@ -2615,6 +3542,11 @@ This cannot be undone! Ошибка при экспорте: No comment provided by engineer. + + Export theme + Экспорт темы + No comment provided by engineer. + Exported database archive. Архив чата экспортирован. @@ -2622,6 +3554,7 @@ This cannot be undone! Exported file doesn't exist + Экспортированный файл не существует No comment provided by engineer. @@ -2639,16 +3572,70 @@ This cannot be undone! Быстрые и не нужно ждать, когда отправитель онлайн! No comment provided by engineer. + + Faster deletion of groups. + Ускорено удаление групп. + No comment provided by engineer. + Faster joining and more reliable messages. Быстрое вступление и надежная доставка сообщений. No comment provided by engineer. + + Faster sending messages. + Ускорена отправка сообщений. + No comment provided by engineer. + Favorite Избранный + swipe action + + + Favorites + Избранное No comment provided by engineer. + + File error + Ошибка файла + file error alert title + + + File errors: +%@ + Ошибки файлов: +%@ + alert message + + + File is blocked by server operator: +%@. + Файл заблокирован оператором сервера: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + Файл не найден - скорее всего, файл был удален или отменен. + file error text + + + File server error: %@ + Ошибка сервера файлов: %@ + file error text + + + File status + Статус файла + No comment provided by engineer. + + + File status: %@ + Статус файла: %@ + copied message info + File will be deleted from servers. Файл будет удалён с серверов. @@ -2669,6 +3656,11 @@ This cannot be undone! Файл: %@ No comment provided by engineer. + + Files + Файлы + No comment provided by engineer. + Files & media Файлы и медиа @@ -2679,11 +3671,16 @@ This cannot be undone! Файлы и медиа chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. Файлы и медиа запрещены в этой группе. No comment provided by engineer. + + Files and media not allowed + Файлы и медиа не разрешены + No comment provided by engineer. + Files and media prohibited! Файлы и медиа запрещены! @@ -2696,10 +3693,12 @@ This cannot be undone! Finalize migration + Завершить миграцию No comment provided by engineer. Finalize migration on another device. + Завершите миграцию на другом устройстве. No comment provided by engineer. @@ -2739,14 +3738,117 @@ This cannot be undone! Fix not supported by group member - Починка не поддерживается членом группы No comment provided by engineer. + + For all moderators + Для всех модераторов + No comment provided by engineer. + + + For chat profile %@: + Для профиля чата %@: + servers error + For console Для консоли No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + Например, если Ваш контакт получает сообщения через сервер SimpleX Chat, Ваше приложение доставит их через сервер Flux. + No comment provided by engineer. + + + For me + Для меня + No comment provided by engineer. + + + For private routing + Для доставки сообщений + No comment provided by engineer. + + + For social media + Для социальных сетей + No comment provided by engineer. + + + Forward + Переслать + chat item action + + + Forward %d message(s)? + Переслать %d сообщение(й)? + alert title + + + Forward and save messages + Переслать и сохранить сообщение + No comment provided by engineer. + + + Forward messages + Переслать сообщения + alert action + + + Forward messages without files? + Переслать сообщения без файлов? + alert message + + + Forward up to 20 messages at once. + Пересылайте до 20 сообщений за раз. + No comment provided by engineer. + + + Forwarded + Переслано + No comment provided by engineer. + + + Forwarded from + Переслано из + No comment provided by engineer. + + + Forwarding %lld messages + Пересылка %lld сообщений + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + Пересылающий сервер %@ не смог подключиться к серверу назначения %@. Попробуйте позже. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + Адрес пересылающего сервера несовместим с настройками сети: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + Версия пересылающего сервера несовместима с настройками сети: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + Пересылающий сервер: %1$@ +Ошибка сервера получателя: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + Пересылающий сервер: %1$@ +Ошибка: %2$@ + snd error text + Found desktop Компьютер найден @@ -2767,14 +3869,8 @@ This cannot be undone! Полное имя (не обязательно) No comment provided by engineer. - - Full name: - Полное имя: - No comment provided by engineer. - Fully decentralized – visible only to members. - Группа полностью децентрализована – она видна только членам. No comment provided by engineer. @@ -2792,6 +3888,21 @@ This cannot be undone! ГИФ файлы и стикеры No comment provided by engineer. + + Get notified when mentioned. + Уведомления, когда Вас упомянули. + No comment provided by engineer. + + + Good afternoon! + Добрый день! + message preview + + + Good morning! + Доброе утро! + message preview + Group Группа @@ -2819,7 +3930,7 @@ This cannot be undone! Group image - Аватар группы + Картинка группы No comment provided by engineer. @@ -2847,36 +3958,6 @@ This cannot be undone! Ссылки групп No comment provided by engineer. - - Group members can add message reactions. - Члены группы могут добавлять реакции на сообщения. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Члены группы могут необратимо удалять отправленные сообщения. (24 часа) - No comment provided by engineer. - - - Group members can send direct messages. - Члены группы могут посылать прямые сообщения. - No comment provided by engineer. - - - Group members can send disappearing messages. - Члены группы могут посылать исчезающие сообщения. - No comment provided by engineer. - - - Group members can send files and media. - Члены группы могут слать файлы и медиа. - No comment provided by engineer. - - - Group members can send voice messages. - Члены группы могут отправлять голосовые сообщения. - No comment provided by engineer. - Group message: Групповое сообщение: @@ -2899,7 +3980,6 @@ This cannot be undone! Group profile is stored on members' devices, not on the servers. - Профиль группы хранится на устройствах членов, а не на серверах. No comment provided by engineer. @@ -2909,7 +3989,6 @@ This cannot be undone! Group will be deleted for all members - this cannot be undone! - Группа будет удалена для всех членов - это действие нельзя отменить! No comment provided by engineer. @@ -2917,11 +3996,21 @@ This cannot be undone! Группа будет удалена для Вас - это действие нельзя отменить! No comment provided by engineer. + + Groups + Группы + No comment provided by engineer. + Help Помощь No comment provided by engineer. + + Help admins moderating their groups. + Помогайте администраторам модерировать их группы. + No comment provided by engineer. + Hidden Скрытое @@ -2964,7 +4053,6 @@ This cannot be undone! History is not sent to new members. - История не отправляется новым членам. No comment provided by engineer. @@ -2972,10 +4060,20 @@ This cannot be undone! Как SimpleX работает No comment provided by engineer. + + How it affects privacy + Как это влияет на конфиденциальность + No comment provided by engineer. + + + How it helps privacy + Как это улучшает конфиденциальность + No comment provided by engineer. + How it works Как это работает - No comment provided by engineer. + alert button How to @@ -2984,7 +4082,7 @@ This cannot be undone! How to use it - Как использовать + Про адрес No comment provided by engineer. @@ -2994,6 +4092,7 @@ This cannot be undone! Hungarian interface + Венгерский интерфейс No comment provided by engineer. @@ -3001,6 +4100,11 @@ This cannot be undone! ICE серверы (один на строке) No comment provided by engineer. + + IP address + IP адрес + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Если Вы не можете встретиться лично, покажите QR-код во время видеозвонка или поделитесь ссылкой. @@ -3041,8 +4145,8 @@ This cannot be undone! Сразу No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Защищен от спама No comment provided by engineer. @@ -3063,10 +4167,23 @@ This cannot be undone! Import failed + Ошибка импорта + No comment provided by engineer. + + + Import theme + Импорт темы No comment provided by engineer. Importing archive + Импорт архива + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Улучшенная доставка, меньше трафик. No comment provided by engineer. @@ -3086,6 +4203,7 @@ This cannot be undone! In order to continue, chat should be stopped. + Чтобы продолжить, чат должен быть остановлен. No comment provided by engineer. @@ -3093,6 +4211,21 @@ This cannot be undone! В ответ на No comment provided by engineer. + + In-call sounds + Звуки во время звонков + No comment provided by engineer. + + + Inappropriate content + Неприемлемый контент + report reason + + + Inappropriate profile + Неприемлемый профиль + report reason + Incognito Инкогнито @@ -3163,6 +4296,11 @@ This cannot be undone! [SimpleX Chat для терминала](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Мгновенно + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3170,16 +4308,41 @@ This cannot be undone! No comment provided by engineer. - - Instantly - Мгновенно - No comment provided by engineer. - Interface Интерфейс No comment provided by engineer. + + Interface colors + Цвета интерфейса + No comment provided by engineer. + + + Invalid + Недействительный + token status text + + + Invalid (bad token) + Недействительный (плохой токен) + token status text + + + Invalid (expired) + Недействительный (истекший) + token status text + + + Invalid (unregistered) + Недействительный (незарегистрированный) + token status text + + + Invalid (wrong topic) + Недействительный (плохой заголовок) + token status text + Invalid QR code Неверный QR код @@ -3202,6 +4365,7 @@ This cannot be undone! Invalid migration confirmation + Ошибка подтверждения миграции No comment provided by engineer. @@ -3217,7 +4381,7 @@ This cannot be undone! Invalid server address! Ошибка в адресе сервера! - No comment provided by engineer. + alert title Invalid status @@ -3236,7 +4400,11 @@ This cannot be undone! Invite members - Пригласить членов группы + No comment provided by engineer. + + + Invite to chat + Пригласить в разговор No comment provided by engineer. @@ -3254,8 +4422,8 @@ This cannot be undone! Необратимое удаление сообщений запрещено в этом чате. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. Необратимое удаление сообщений запрещено в этой группе. No comment provided by engineer. @@ -3280,6 +4448,11 @@ This cannot be undone! 3. Соединение компроментировано. No comment provided by engineer. + + It protects your IP address and connections. + Защищает ваш IP адрес и соединения. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Возможно, Вы уже соединились через эту ссылку. Если это не так, то это ошибка (%@). @@ -3298,7 +4471,7 @@ This cannot be undone! Join Вступить - No comment provided by engineer. + swipe action Join group @@ -3340,6 +4513,11 @@ This is your link for group %@! Keep Оставить + alert action + + + Keep conversation + Оставить разговор No comment provided by engineer. @@ -3350,7 +4528,7 @@ This is your link for group %@! Keep unused invitation? Оставить неиспользованное приглашение? - No comment provided by engineer. + alert title Keep your connections @@ -3385,6 +4563,16 @@ This is your link for group %@! Leave Выйти + swipe action + + + Leave chat + Покинуть разговор + No comment provided by engineer. + + + Leave chat? + Покинуть разговор? No comment provided by engineer. @@ -3427,6 +4615,21 @@ This is your link for group %@! Связанные компьютеры No comment provided by engineer. + + List + Список + swipe action + + + List name and emoji should be different for all lists. + Название списка и эмодзи должны быть разными для всех списков. + No comment provided by engineer. + + + List name... + Имя списка... + No comment provided by engineer. + Live message! Живое сообщение! @@ -3437,11 +4640,6 @@ This is your link for group %@! "Живые" сообщения No comment provided by engineer. - - Local - Локальные - No comment provided by engineer. - Local name Локальное имя @@ -3462,11 +4660,6 @@ This is your link for group %@! Режим блокировки No comment provided by engineer. - - Make a private connection - Добавьте контакт - No comment provided by engineer. - Make one message disappear Одно исчезающее сообщение @@ -3477,21 +4670,11 @@ This is your link for group %@! Сделайте профиль скрытым! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Пожалуйста, проверьте, что адреса %@ серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Пожалуйста, проверьте, что адреса WebRTC ICE серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Много пользователей спросили: *как SimpleX доставляет сообщения без идентификаторов пользователей?* - No comment provided by engineer. - Mark deleted for everyone Пометить как удаленное для всех @@ -3517,24 +4700,89 @@ This is your link for group %@! Макс. 30 секунд, доставляются мгновенно. No comment provided by engineer. + + Media & file servers + Серверы файлов и медиа + No comment provided by engineer. + + + Medium + Среднее + blur media + Member - Член группы + No comment provided by engineer. + + + Member inactive + item status text + + + Member reports + Сообщения о нарушениях + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + Роль участника будет изменена на "%@". Все участники разговора получат уведомление. No comment provided by engineer. Member role will be changed to "%@". All group members will be notified. - Роль члена группы будет изменена на "%@". Все члены группы получат сообщение. No comment provided by engineer. Member role will be changed to "%@". The member will receive a new invitation. - Роль члена группы будет изменена на "%@". Будет отправлено новое приглашение. + No comment provided by engineer. + + + Member will be removed from chat - this cannot be undone! No comment provided by engineer. Member will be removed from group - this cannot be undone! - Член группы будет удален - это действие нельзя отменить! + No comment provided by engineer. + + + Members can add message reactions. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + No comment provided by engineer. + + + Members can report messsages to moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + No comment provided by engineer. + + + Members can send direct messages. + No comment provided by engineer. + + + Members can send disappearing messages. + No comment provided by engineer. + + + Members can send files and media. + No comment provided by engineer. + + + Members can send voice messages. + No comment provided by engineer. + + + Mention members 👋 + No comment provided by engineer. + + + Menus + Меню No comment provided by engineer. @@ -3547,11 +4795,30 @@ This is your link for group %@! Отчеты о доставке сообщений! No comment provided by engineer. + + Message delivery warning + Предупреждение доставки сообщения + item status text + Message draft Черновик сообщения No comment provided by engineer. + + Message forwarded + Сообщение переслано + item status text + + + Message may be delivered later if member becomes active. + item status description + + + Message queue info + Информация об очереди сообщений + No comment provided by engineer. + Message reactions Реакции на сообщения @@ -3562,11 +4829,41 @@ This is your link for group %@! Реакции на сообщения в этом чате запрещены. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. Реакции на сообщения запрещены в этой группе. No comment provided by engineer. + + Message reception + Прием сообщений + No comment provided by engineer. + + + Message servers + Серверы сообщений + No comment provided by engineer. + + + Message shape + Форма сообщений + No comment provided by engineer. + + + Message source remains private. + Источник сообщения остаётся конфиденциальным. + No comment provided by engineer. + + + Message status + Статус сообщения + No comment provided by engineer. + + + Message status: %@ + Статус сообщения: %@ + copied message info + Message text Текст сообщения @@ -3574,6 +4871,7 @@ This is your link for group %@! Message too large + Сообщение слишком большое No comment provided by engineer. @@ -3591,36 +4889,64 @@ This is your link for group %@! Сообщения от %@ будут показаны! No comment provided by engineer. + + Messages in this chat will never be deleted. + Сообщения в этом чате никогда не будут удалены. + alert message + + + Messages received + Получено сообщений + No comment provided by engineer. + + + Messages sent + Сообщений отправлено + No comment provided by engineer. + + + Messages were deleted after you selected them. + Сообщения были удалены после того, как вы их выбрали. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + Сообщения, файлы и звонки защищены **end-to-end шифрованием** с прямой секретностью (PFS), правдоподобным отрицанием и восстановлением от взлома. No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + Сообщения, файлы и звонки защищены **квантово-устойчивым end-to-end шифрованием** с прямой секретностью (PFS), правдоподобным отрицанием и восстановлением от взлома. No comment provided by engineer. Migrate device + Мигрировать устройство No comment provided by engineer. Migrate from another device + Миграция с другого устройства No comment provided by engineer. Migrate here + Мигрировать сюда No comment provided by engineer. Migrate to another device + Мигрировать на другое устройство No comment provided by engineer. Migrate to another device via QR code. + Мигрируйте на другое устройство через QR код. No comment provided by engineer. Migrating + Миграция No comment provided by engineer. @@ -3630,6 +4956,7 @@ This is your link for group %@! Migration complete + Миграция завершена No comment provided by engineer. @@ -3647,9 +4974,9 @@ This is your link for group %@! Перемещение данных завершено No comment provided by engineer. - - Migrations: %@ - Миграции: %@ + + Migrations: + Миграции: No comment provided by engineer. @@ -3667,21 +4994,31 @@ This is your link for group %@! Модерировано: %@ copied message info + + More + Больше + swipe action + More improvements are coming soon! Дополнительные улучшения скоро! No comment provided by engineer. + + More reliable network connection. + Более надежное соединение с сетью. + No comment provided by engineer. + + + More reliable notifications + Более надежные уведомления + No comment provided by engineer. + Most likely this connection is deleted. Скорее всего, соединение удалено. item status description - - Most likely this contact has deleted the connection with you. - Скорее всего, этот контакт удалил соединение с Вами. - No comment provided by engineer. - Multiple chat profiles Много профилей чата @@ -3690,7 +5027,12 @@ This is your link for group %@! Mute Без звука - No comment provided by engineer. + notification label action + + + Mute all + Все без звука + notification label action Muted when inactive! @@ -3700,13 +5042,38 @@ This is your link for group %@! Name Имя - No comment provided by engineer. + swipe action Network & servers Сеть & серверы No comment provided by engineer. + + Network connection + Интернет-соединение + No comment provided by engineer. + + + Network decentralization + Децентрализация сети + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + Ошибка сети - сообщение не было отправлено после многократных попыток. + snd error text + + + Network management + Статус сети + No comment provided by engineer. + + + Network operator + Оператор сети + No comment provided by engineer. + Network settings Настройки сети @@ -3717,16 +5084,36 @@ This is your link for group %@! Состояние сети No comment provided by engineer. + + New + Новый + token status text + New Passcode Новый Код No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Новые учетные данные SOCKS будут использоваться при каждом запуске приложения. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Новые учетные данные SOCKS будут использоваться для каждого сервера. + No comment provided by engineer. + New chat Новый чат No comment provided by engineer. + + New chat experience 🎉 + Новый интерфейс 🎉 + No comment provided by engineer. + New contact request Новый запрос на соединение @@ -3737,11 +5124,6 @@ This is your link for group %@! Новый контакт: notification - - New database archive - Новый архив чата - No comment provided by engineer. - New desktop app! Приложение для компьютера! @@ -3752,14 +5134,23 @@ This is your link for group %@! Новое имя No comment provided by engineer. + + New events + Новые события + notification + New in %@ Новое в %@ No comment provided by engineer. + + New media options + Новые медиа-опции + No comment provided by engineer. + New member role - Роль члена группы No comment provided by engineer. @@ -3772,6 +5163,11 @@ This is your link for group %@! Новый пароль… No comment provided by engineer. + + New server + Новый сервер + No comment provided by engineer. + No Нет @@ -3782,6 +5178,21 @@ This is your link for group %@! Нет кода доступа Authentication unavailable + + No chats + Нет чатов + No comment provided by engineer. + + + No chats found + Чаты не найдены + No comment provided by engineer. + + + No chats in list %@ + Нет чатов в списке %@ + No comment provided by engineer. + No contacts selected Контакты не выбраны @@ -3802,6 +5213,11 @@ This is your link for group %@! Отсутствует токен устройства! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + Прямого соединения пока нет, сообщение переслано или будет переслано админом. + item status description + No filtered chats Нет отфильтрованных разговоров @@ -3817,21 +5233,111 @@ This is your link for group %@! Нет истории No comment provided by engineer. + + No info, try to reload + Нет информации, попробуйте перезагрузить + No comment provided by engineer. + + + No media & file servers. + Нет серверов файлов и медиа. + servers error + + + No message + Нет сообщения + No comment provided by engineer. + + + No message servers. + Нет серверов сообщений. + servers error + + + No network connection + Нет интернет-соединения + No comment provided by engineer. + + + No permission to record speech + Нет разрешения на запись речи + No comment provided by engineer. + + + No permission to record video + Нет разрешения на запись видео + No comment provided by engineer. + No permission to record voice message Нет разрешения для записи голосового сообщения No comment provided by engineer. + + No push server + Без сервера нотификаций + No comment provided by engineer. + No received or sent files Нет полученных или отправленных файлов No comment provided by engineer. + + No servers for private message routing. + Нет серверов для доставки сообщений. + servers error + + + No servers to receive files. + Нет серверов для приема файлов. + servers error + + + No servers to receive messages. + Нет серверов для приема сообщений. + servers error + + + No servers to send files. + Нет серверов для отправки файлов. + servers error + + + No token! + Нет токена! + alert title + + + No unread chats + Нет непрочитанных чатов + No comment provided by engineer. + + + No user identifiers. + Без идентификаторов пользователей. + No comment provided by engineer. + Not compatible! Несовместимая версия! No comment provided by engineer. + + Notes + Заметки + No comment provided by engineer. + + + Nothing selected + Ничего не выбрано + No comment provided by engineer. + + + Nothing to forward! + Нет сообщений, которые можно переслать! + alert title + Notifications Уведомления @@ -3842,13 +5348,25 @@ This is your link for group %@! Уведомления выключены No comment provided by engineer. + + Notifications error + Ошибка уведомлений + alert title + + + Notifications privacy + Конфиденциальность уведомлений + No comment provided by engineer. + + + Notifications status + Статус уведомлений + alert title + Now admins can: - delete members' messages. - disable members ("observer" role) - Теперь админы могут: -- удалять сообщения членов. -- приостанавливать членов (роль "наблюдатель") No comment provided by engineer. @@ -3859,36 +5377,35 @@ This is your link for group %@! Off Выключено - No comment provided by engineer. + blur media Ok Ок - No comment provided by engineer. + alert button Old database Предыдущая версия данных чата No comment provided by engineer. - - Old database archive - Старый архив чата - No comment provided by engineer. - One-time invitation link Одноразовая ссылка No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Подключаться только к onion хостам. Требуется включенный VPN. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Подключаться только к **onion** хостам. +Требуется совместимый VPN. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Onion хосты используются, если возможно. Требуется включенный VPN. + + Onion hosts will be used when available. +Requires compatible VPN. + Onion хосты используются, если возможно. +Требуется совместимый VPN. No comment provided by engineer. @@ -3896,9 +5413,19 @@ This is your link for group %@! Onion хосты не используются. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. - Только пользовательские устройства хранят контакты, группы и сообщения, которые отправляются **с двухуровневым end-to-end шифрованием**. + + Only chat owners can change preferences. + Только владельцы разговора могут поменять предпочтения. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. + Только пользовательские устройства хранят контакты, группы и сообщения. + No comment provided by engineer. + + + Only delete conversation + Удалить только разговор No comment provided by engineer. @@ -3916,6 +5443,16 @@ This is your link for group %@! Только владельцы группы могут разрешить голосовые сообщения. No comment provided by engineer. + + Only sender and moderators see it + Только отправитель и модераторы видят это + No comment provided by engineer. + + + Only you and moderators see it + Только вы и модераторы видят это + No comment provided by engineer. + Only you can add message reactions. Только Вы можете добавлять реакции на сообщения. @@ -3969,13 +5506,18 @@ This is your link for group %@! Open Открыть - No comment provided by engineer. + alert action Open Settings Открыть Настройки No comment provided by engineer. + + Open changes + Открыть изменения + No comment provided by engineer. + Open chat Открыть чат @@ -3986,32 +5528,48 @@ This is your link for group %@! Открыть консоль authentication reason + + Open conditions + Открыть условия + No comment provided by engineer. + Open group Открыть группу No comment provided by engineer. + + Open link? + alert title + Open migration to another device + Открытие миграции на другое устройство authentication reason - - Open user profiles - Открыть профили пользователя - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Открытый протокол и код - кто угодно может запустить сервер. - No comment provided by engineer. - Opening app… Приложение отрывается… No comment provided by engineer. + + Operator + Оператор + No comment provided by engineer. + + + Operator server + Сервер оператора + alert title + + + Or import archive file + Или импортировать файл архива + No comment provided by engineer. + Or paste archive link + Или вставьте ссылку архива No comment provided by engineer. @@ -4021,6 +5579,7 @@ This is your link for group %@! Or securely share this file link + Или передайте эту ссылку No comment provided by engineer. @@ -4028,6 +5587,28 @@ This is your link for group %@! Или покажите этот код No comment provided by engineer. + + Or to share privately + Или поделиться конфиденциально + No comment provided by engineer. + + + Organize chats into lists + Организуйте чаты в списки + No comment provided by engineer. + + + Other + Другaя сеть + No comment provided by engineer. + + + Other file errors: +%@ + Другие ошибки файлов: +%@ + alert message + PING count Количество PING @@ -4063,6 +5644,11 @@ This is your link for group %@! Код доступа установлен! No comment provided by engineer. + + Password + Пароль + No comment provided by engineer. + Password to show Пароль чтобы раскрыть @@ -4070,7 +5656,6 @@ This is your link for group %@! Past member %@ - Бывший член %@ past/unknown group member @@ -4093,13 +5678,13 @@ This is your link for group %@! Вставьте полученную ссылку No comment provided by engineer. - - People can connect to you only via the links you share. - С Вами можно соединиться только через созданные Вами ссылки. + + Pending + Ожидает No comment provided by engineer. - - Periodically + + Periodic Периодически No comment provided by engineer. @@ -4110,6 +5695,17 @@ This is your link for group %@! Picture-in-picture calls + Звонки с картинкой-в-картинке + No comment provided by engineer. + + + Play from the chat list. + Открыть из списка чатов. + No comment provided by engineer. + + + Please ask your contact to enable calls. + Попросите Вашего контакта разрешить звонки. No comment provided by engineer. @@ -4117,6 +5713,13 @@ This is your link for group %@! Попросите у Вашего контакта разрешить отправку голосовых сообщений. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + Пожалуйста, проверьте, что мобильный и компьютер находятся в одной и той же локальной сети, и что брандмауэр компьютера разрешает подключение. +Пожалуйста, поделитесь любыми другими ошибками с разработчиками. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Пожалуйста, проверьте, что Вы использовали правильную ссылку или попросите, чтобы Ваш контакт отправил Вам другую ссылку. @@ -4134,6 +5737,7 @@ This is your link for group %@! Please confirm that network settings are correct for this device. + Пожалуйста, подтвердите, что настройки сети верны для этого устройства. No comment provided by engineer. @@ -4183,60 +5787,121 @@ Error: %@ Пожалуйста, надежно сохраните пароль, Вы НЕ сможете его поменять, если потеряете. No comment provided by engineer. + + Please try to disable and re-enable notfications. + Попробуйте выключить и снова включить уведомления. + token info + + + Please wait for token activation to complete. + Пожалуйста, дождитесь завершения активации токена. + token info + + + Please wait for token to be registered. + Пожалуйста, дождитесь регистрации токена. + token info + Polish interface Польский интерфейс No comment provided by engineer. + + Port + Порт + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Возможно, хэш сертификата в адресе сервера неверный server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Сохранить последний черновик, вместе с вложениями. No comment provided by engineer. - - Preset server - Сервер по умолчанию - No comment provided by engineer. - Preset server address Адрес сервера по умолчанию No comment provided by engineer. + + Preset servers + Серверы по умолчанию + No comment provided by engineer. + Preview Просмотр No comment provided by engineer. + + Previously connected servers + Ранее подключенные серверы + No comment provided by engineer. + Privacy & security Конфиденциальность No comment provided by engineer. + + Privacy for your customers. + Конфиденциальность для ваших покупателей. + No comment provided by engineer. + + + Privacy policy and conditions of use. + Политика конфиденциальности и условия использования. + No comment provided by engineer. + Privacy redefined Более конфиденциальный No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + Частные разговоры, группы и Ваши контакты недоступны для операторов серверов. + No comment provided by engineer. + Private filenames Защищенные имена файлов No comment provided by engineer. + + Private media file names. + Конфиденциальные названия медиафайлов. + No comment provided by engineer. + + + Private message routing + Конфиденциальная доставка сообщений + No comment provided by engineer. + + + Private message routing 🚀 + Конфиденциальная доставка 🚀 + No comment provided by engineer. + Private notes Личные заметки name of notes to self + + Private routing + Конфиденциальная доставка + No comment provided by engineer. + + + Private routing error + Ошибка конфиденциальной доставки + No comment provided by engineer. + Profile and server connections Профиль и соединения на сервере @@ -4244,17 +5909,12 @@ Error: %@ Profile image - Аватар + Картинка профиля No comment provided by engineer. - - Profile name - Имя профиля - No comment provided by engineer. - - - Profile name: - Имя профиля: + + Profile images + Картинки профилей No comment provided by engineer. @@ -4262,10 +5922,15 @@ Error: %@ Пароль профиля No comment provided by engineer. + + Profile theme + Тема профиля + No comment provided by engineer. + Profile update will be sent to your contacts. Обновлённый профиль будет отправлен Вашим контактам. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4287,9 +5952,18 @@ Error: %@ Запретить реакции на сообщения. No comment provided by engineer. + + Prohibit reporting messages to moderators. + Запретить жаловаться модераторам группы. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + Запретить отправку ссылок SimpleX. + No comment provided by engineer. + Prohibit sending direct messages to members. - Запретить посылать прямые сообщения членам группы. No comment provided by engineer. @@ -4307,11 +5981,23 @@ Error: %@ Запретить отправлять голосовые сообщений. No comment provided by engineer. + + Protect IP address + Защитить IP адрес + No comment provided by engineer. + Protect app screen Защитить экран приложения No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + Защитите ваш IP адрес от серверов сообщений, выбранных Вашими контактами. +Включите в настройках *Сети и серверов*. + No comment provided by engineer. + Protect your chat profiles with a password! Защитите Ваши профили чата паролем! @@ -4327,6 +6013,21 @@ Error: %@ Таймаут протокола на KB No comment provided by engineer. + + Proxied + Проксировано + No comment provided by engineer. + + + Proxied servers + Проксированные серверы + No comment provided by engineer. + + + Proxy requires password + Прокси требует пароль + No comment provided by engineer. + Push notifications Доставка уведомлений @@ -4334,10 +6035,12 @@ Error: %@ Push server + Сервер уведомлений No comment provided by engineer. Quantum resistant encryption + Квантово-устойчивое шифрование No comment provided by engineer. @@ -4345,6 +6048,11 @@ Error: %@ Оценить приложение No comment provided by engineer. + + Reachable chat toolbar + Доступная панель чата + No comment provided by engineer. + React… Реакция… @@ -4353,33 +6061,28 @@ Error: %@ Read Прочитано - No comment provided by engineer. + swipe action Read more Узнать больше No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Узнать больше в [Руководстве пользователя](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). Дополнительная информация в [Руководстве пользователя](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Узнать больше в [Руководстве пользователя](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). Узнать больше в [Руководстве пользователя](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - - Read more in our GitHub repository. - Узнайте больше из нашего GitHub репозитория. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). Узнайте больше из нашего [GitHub репозитория](https://github.com/simplex-chat/simplex-chat#readme). @@ -4390,6 +6093,11 @@ Error: %@ Отчёты о доставке выключены No comment provided by engineer. + + Receive errors + Ошибки приема + No comment provided by engineer. + Received at Получено @@ -4410,6 +6118,21 @@ Error: %@ Полученное сообщение message info title + + Received messages + Полученные сообщения + No comment provided by engineer. + + + Received reply + Полученный ответ + No comment provided by engineer. + + + Received total + Всего получено + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. Адрес получения сообщений будет перемещён на другой сервер. Изменение адреса завершится после того как отправитель будет онлайн. @@ -4430,16 +6153,46 @@ Error: %@ История сообщений и улучшенный [каталог групп](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + Получатели не видят от кого это сообщение. + No comment provided by engineer. + Recipients see updates as you type them. Получатели видят их в то время как Вы их набираете. No comment provided by engineer. + + Reconnect + Переподключить + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Повторно подключите все серверы, чтобы принудительно доставить сообщения. Используется дополнительный трафик. No comment provided by engineer. + + Reconnect all servers + Переподключить все серверы + No comment provided by engineer. + + + Reconnect all servers? + Переподключить все серверы? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + Переподключить сервер для устранения неполадок доставки сообщений. Это использует дополнительный трафик. + No comment provided by engineer. + + + Reconnect server? + Переподключить сервер? + No comment provided by engineer. + Reconnect servers? Переподключить серверы? @@ -4460,10 +6213,26 @@ Error: %@ Уменьшенное потребление батареи No comment provided by engineer. + + Register + Зарегистрировать + No comment provided by engineer. + + + Register notification token? + Зарегистрировать токен уведомлений? + token info + + + Registered + Зарегистрирован + token status text + Reject Отклонить - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4490,14 +6259,22 @@ Error: %@ Удалить No comment provided by engineer. + + Remove archive? + Удалить архив? + No comment provided by engineer. + + + Remove image + Удалить изображение + No comment provided by engineer. + Remove member - Удалить члена группы No comment provided by engineer. Remove member? - Удалить члена группы? No comment provided by engineer. @@ -4527,10 +6304,12 @@ Error: %@ Repeat download + Повторить загрузку No comment provided by engineer. Repeat import + Повторить импорт No comment provided by engineer. @@ -4540,6 +6319,7 @@ Error: %@ Repeat upload + Повторить загрузку No comment provided by engineer. @@ -4547,6 +6327,56 @@ Error: %@ Ответить chat item action + + Report + Пожаловаться + chat item action + + + Report content: only group moderators will see it. + Пожаловаться на сообщение: увидят только модераторы группы. + report reason + + + Report member profile: only group moderators will see it. + Пожаловаться на профиль: увидят только модераторы группы. + report reason + + + Report other: only group moderators will see it. + Пожаловаться: увидят только модераторы группы. + report reason + + + Report reason? + Причина сообщения? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + Пожаловаться на спам: увидят только модераторы группы. + report reason + + + Report violation: only group moderators will see it. + Пожаловаться на нарушение: увидят только модераторы группы. + report reason + + + Report: %@ + Сообщение о нарушении: %@ + report in notification + + + Reporting messages to moderators is prohibited. + Сообщения о нарушениях запрещены в этой группе. + No comment provided by engineer. + + + Reports + Сообщения о нарушениях + No comment provided by engineer. + Required Обязательно @@ -4557,16 +6387,41 @@ Error: %@ Сбросить No comment provided by engineer. + + Reset all hints + Сбросить все подсказки + No comment provided by engineer. + + + Reset all statistics + Сбросить всю статистику + No comment provided by engineer. + + + Reset all statistics? + Сбросить всю статистику? + No comment provided by engineer. + Reset colors Сбросить цвета No comment provided by engineer. + + Reset to app theme + Сбросить на тему приложения + No comment provided by engineer. + Reset to defaults Сбросить настройки No comment provided by engineer. + + Reset to user theme + Сбросить на тему пользователя + No comment provided by engineer. + Restart the app to create a new chat profile Перезапустите приложение, чтобы создать новый профиль. @@ -4607,9 +6462,9 @@ Error: %@ Показать chat item action - - Revert - Отменить изменения + + Review conditions + Посмотреть условия No comment provided by engineer. @@ -4637,33 +6492,49 @@ Error: %@ Запустить chat No comment provided by engineer. - - SMP servers - SMP серверы + + SMP server + SMP сервер + No comment provided by engineer. + + + SOCKS proxy + SOCKS прокси + No comment provided by engineer. + + + Safely receive files + Получайте файлы безопасно No comment provided by engineer. Safer groups + Более безопасные группы No comment provided by engineer. Save Сохранить - chat item action + alert button +chat item action Save (and notify contacts) Сохранить (и уведомить контакты) - No comment provided by engineer. + alert button Save and notify contact Сохранить и уведомить контакт - No comment provided by engineer. + alert button Save and notify group members - Сохранить и уведомить членов группы + No comment provided by engineer. + + + Save and reconnect + Сохранить и переподключиться No comment provided by engineer. @@ -4671,21 +6542,16 @@ Error: %@ Сохранить сообщение и обновить группу No comment provided by engineer. - - Save archive - Сохранить архив - No comment provided by engineer. - - - Save auto-accept settings - Сохранить настройки автоприема - No comment provided by engineer. - Save group profile Сохранить профиль группы No comment provided by engineer. + + Save list + Сохранить список + No comment provided by engineer. + Save passphrase and open chat Сохранить пароль и открыть чат @@ -4699,7 +6565,7 @@ Error: %@ Save preferences? Сохранить предпочтения? - No comment provided by engineer. + alert title Save profile password @@ -4714,28 +6580,53 @@ Error: %@ Save servers? Сохранить серверы? - No comment provided by engineer. - - - Save settings? - Сохранить настройки? - No comment provided by engineer. + alert title Save welcome message? Сохранить приветственное сообщение? No comment provided by engineer. + + Save your profile? + Сохранить ваш профиль? + alert title + + + Saved + Сохранено + No comment provided by engineer. + Saved WebRTC ICE servers will be removed Сохраненные WebRTC ICE серверы будут удалены No comment provided by engineer. + + Saved from + Сохранено из + No comment provided by engineer. + Saved message Сохраненное сообщение message info title + + Saving %lld messages + Сохранение %lld сообщений + No comment provided by engineer. + + + Scale + Масштаб + No comment provided by engineer. + + + Scan / Paste link + Сканировать / Вставить ссылку + No comment provided by engineer. + Scan QR code Сканировать QR код @@ -4776,11 +6667,21 @@ Error: %@ Искать или вставьте ссылку SimpleX No comment provided by engineer. + + Secondary + Вторичный + No comment provided by engineer. + Secure queue Защита очереди server test step + + Secured + Защищено + No comment provided by engineer. + Security assessment Аудит безопасности @@ -4794,6 +6695,21 @@ Error: %@ Select Выбрать + chat item action + + + Select chat profile + Выберите профиль чата + No comment provided by engineer. + + + Selected %lld + Выбрано %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Выбранные настройки чата запрещают это сообщение. No comment provided by engineer. @@ -4831,11 +6747,6 @@ Error: %@ Отправка отчётов о доставке No comment provided by engineer. - - Send direct message - Отправить сообщение - No comment provided by engineer. - Send direct message to connect Отправьте сообщение чтобы соединиться @@ -4846,6 +6757,11 @@ Error: %@ Отправить исчезающее сообщение No comment provided by engineer. + + Send errors + Ошибки отправки + No comment provided by engineer. + Send link previews Отправлять картинки ссылок @@ -4856,14 +6772,29 @@ Error: %@ Отправить живое сообщение No comment provided by engineer. + + Send message to enable calls. + Отправьте сообщение, чтобы включить звонки. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + Отправлять сообщения напрямую, когда IP адрес защищен, и Ваш сервер или сервер получателя не поддерживает конфиденциальную доставку. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + Отправлять сообщения напрямую, когда Ваш сервер или сервер получателя не поддерживает конфиденциальную доставку. + No comment provided by engineer. + Send notifications Отправлять уведомления No comment provided by engineer. - - Send notifications: - Отправлять уведомления: + + Send private reports + Вы можете сообщить о нарушениях No comment provided by engineer. @@ -4883,13 +6814,12 @@ Error: %@ Send up to 100 last messages to new members. - Отправить до 100 последних сообщений новым членам. No comment provided by engineer. Sender cancelled file transfer. Отправитель отменил передачу файла. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4946,6 +6876,11 @@ Error: %@ Отправлено: %@ copied message info + + Sent directly + Отправлено напрямую + No comment provided by engineer. + Sent file event Отправка файла @@ -4956,11 +6891,71 @@ Error: %@ Отправленное сообщение message info title + + Sent messages + Отправленные сообщения + No comment provided by engineer. + Sent messages will be deleted after set time. Отправленные сообщения будут удалены через заданное время. No comment provided by engineer. + + Sent reply + Отправленный ответ + No comment provided by engineer. + + + Sent total + Всего отправлено + No comment provided by engineer. + + + Sent via proxy + Отправлено через прокси + No comment provided by engineer. + + + Server + Сервер + No comment provided by engineer. + + + Server added to operator %@. + Сервер добавлен к оператору %@. + alert message + + + Server address + Адрес сервера + No comment provided by engineer. + + + Server address is incompatible with network settings. + Адрес сервера несовместим с настройками сети. + srv error text. + + + Server address is incompatible with network settings: %@. + Адрес сервера несовместим с сетевыми настройками: %@. + No comment provided by engineer. + + + Server operator changed. + Оператор серверов изменен. + alert title + + + Server operators + Операторы серверов + No comment provided by engineer. + + + Server protocol changed. + Протокол сервера изменен. + alert title + Server requires authorization to create queues, check password Сервер требует авторизации для создания очередей, проверьте пароль @@ -4976,11 +6971,36 @@ Error: %@ Ошибка теста сервера! No comment provided by engineer. + + Server type + Тип сервера + No comment provided by engineer. + + + Server version is incompatible with network settings. + Версия сервера несовместима с настройками сети. + srv error text + + + Server version is incompatible with your app: %@. + Версия сервера несовместима с вашим приложением: %@. + No comment provided by engineer. + Servers Серверы No comment provided by engineer. + + Servers info + Информация о серверах + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + Статистика серверов будет сброшена - это нельзя отменить! + No comment provided by engineer. + Session code Код сессии @@ -4991,11 +7011,21 @@ Error: %@ Установить 1 день No comment provided by engineer. + + Set chat name… + Имя чата… + No comment provided by engineer. + Set contact name… Имя контакта… No comment provided by engineer. + + Set default theme + Установить тему по умолчанию + No comment provided by engineer. + Set group preferences Предпочтения группы @@ -5006,6 +7036,11 @@ Error: %@ Установите код вместо системной аутентификации. No comment provided by engineer. + + Set message expiration in chats. + Установите срок хранения сообщений в чатах. + No comment provided by engineer. + Set passcode Установить код доступа @@ -5013,6 +7048,7 @@ Error: %@ Set passphrase + Установить пароль No comment provided by engineer. @@ -5022,7 +7058,6 @@ Error: %@ Set the message shown to new members! - Установить сообщение для новых членов группы! No comment provided by engineer. @@ -5035,24 +7070,55 @@ Error: %@ Настройки No comment provided by engineer. + + Settings were changed. + Настройки были изменены. + alert message + + + Shape profile images + Форма картинок профилей + No comment provided by engineer. + Share Поделиться - chat item action + alert action +chat item action Share 1-time link Поделиться одноразовой ссылкой No comment provided by engineer. + + Share 1-time link with a friend + Поделитесь одноразовой ссылкой с другом + No comment provided by engineer. + + + Share SimpleX address on social media. + Поделитесь SimpleX адресом в социальных сетях. + No comment provided by engineer. + Share address Поделиться адресом No comment provided by engineer. + + Share address publicly + Поделитесь адресом + No comment provided by engineer. + Share address with contacts? Поделиться адресом с контактами? + alert title + + + Share from other apps. + Поделитесь из других приложений. No comment provided by engineer. @@ -5060,18 +7126,33 @@ Error: %@ Поделиться ссылкой No comment provided by engineer. + + Share profile + Поделиться профилем + No comment provided by engineer. + Share this 1-time invite link Поделиться одноразовой ссылкой-приглашением No comment provided by engineer. + + Share to SimpleX + Поделиться в SimpleX + No comment provided by engineer. + Share with contacts Поделиться с контактами No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code + Показать QR код No comment provided by engineer. @@ -5089,21 +7170,46 @@ Error: %@ Показывать последние сообщения No comment provided by engineer. + + Show message status + Показать статус сообщения + No comment provided by engineer. + + + Show percentage + Показать процент + No comment provided by engineer. + Show preview Показывать уведомления No comment provided by engineer. + + Show → on messages sent via private routing. + Показать → на сообщениях доставленных конфиденциально. + No comment provided by engineer. + Show: Показать: No comment provided by engineer. + + SimpleX + SimpleX + No comment provided by engineer. + SimpleX Address Адрес SimpleX No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + SimpleX Chat и Flux заключили соглашение добавить серверы под управлением Flux в приложение. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. Безопасность SimpleX Chat была проверена Trail of Bits. @@ -5134,6 +7240,20 @@ Error: %@ Адрес SimpleX No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + Адрес SimpleX и одноразовые ссылки безопасно отправлять через любой мессенджер. + No comment provided by engineer. + + + SimpleX address or 1-time link? + Адрес SimpleX или одноразовая ссылка? + No comment provided by engineer. + + + SimpleX channel link + simplex link type + SimpleX contact address SimpleX ссылка-контакт @@ -5152,6 +7272,16 @@ Error: %@ SimpleX links SimpleX ссылки + chat feature + + + SimpleX links are prohibited. + Ссылки SimpleX запрещены в этой группе. + No comment provided by engineer. + + + SimpleX links not allowed + Ссылки SimpleX не разрешены No comment provided by engineer. @@ -5159,11 +7289,21 @@ Error: %@ SimpleX одноразовая ссылка simplex link type + + SimpleX protocols reviewed by Trail of Bits. + Аудит SimpleX протоколов от Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Упрощенный режим Инкогнито No comment provided by engineer. + + Size + Размер + No comment provided by engineer. + Skip Пропустить @@ -5179,16 +7319,54 @@ Error: %@ Маленькие группы (до 20) No comment provided by engineer. + + Soft + Слабое + blur media + + + Some app settings were not migrated. + Некоторые настройки приложения не были перенесены. + No comment provided by engineer. + + + Some file(s) were not exported: + Некоторые файл(ы) не были экспортированы: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. Во время импорта произошли некоторые ошибки - для получения более подробной информации вы можете обратиться к консоли. No comment provided by engineer. + + Some non-fatal errors occurred during import: + Во время импорта произошли некоторые ошибки: + No comment provided by engineer. + + + Some servers failed the test: +%@ + Серверы не прошли тест: +%@ + alert message + Somebody Контакт notification title + + Spam + Спам + blocking reason +report reason + + + Square, circle, or anything in between. + Квадрат, круг и все, что между ними. + No comment provided by engineer. + Start chat Запустить чат @@ -5204,6 +7382,16 @@ Error: %@ Запустить перемещение данных No comment provided by engineer. + + Starting from %@. + Начиная с %@. + No comment provided by engineer. + + + Statistics + Статистика + No comment provided by engineer. + Stop Остановить @@ -5216,11 +7404,7 @@ Error: %@ Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - Остановите чат, чтобы разблокировать операции с архивом чата + Остановить чат No comment provided by engineer. @@ -5251,27 +7435,63 @@ Error: %@ Stop sharing Прекратить делиться - No comment provided by engineer. + alert action Stop sharing address? Прекратить делиться адресом? - No comment provided by engineer. + alert title Stopping chat + Остановка чата No comment provided by engineer. + + Storage + Хранилище + No comment provided by engineer. + + + Strong + Сильное + blur media + Submit Продолжить No comment provided by engineer. + + Subscribed + Подписано + No comment provided by engineer. + + + Subscription errors + Ошибки подписки + No comment provided by engineer. + + + Subscriptions ignored + Подписок игнорировано + No comment provided by engineer. + Support SimpleX Chat Поддержать SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Переключайте звук и видео во время звонка. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Переключайте профиль чата для одноразовых приглашений. + No comment provided by engineer. + System Системная @@ -5282,11 +7502,21 @@ Error: %@ Системная аутентификация No comment provided by engineer. + + TCP connection + TCP-соединение + No comment provided by engineer. + TCP connection timeout Таймаут TCP соединения No comment provided by engineer. + + TCP port for messaging + TCP-порт для отправки сообщений + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5302,11 +7532,21 @@ Error: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + Хвост + No comment provided by engineer. + Take picture Сделать фото No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + Нажмите Создать адрес SimpleX в меню, чтобы создать его позже. + No comment provided by engineer. + Tap button Нажмите кнопку @@ -5342,16 +7582,21 @@ Error: %@ Нажмите, чтобы сканировать No comment provided by engineer. - - Tap to start a new chat - Нажмите, чтобы начать чат - No comment provided by engineer. + + Temporary file error + Временная ошибка файла + file error alert title Test failed at step %@. Ошибка теста на шаге %@. server test failure + + Test notifications + Протестировать уведомления + No comment provided by engineer. + Test server Тестировать сервер @@ -5365,7 +7610,7 @@ Error: %@ Tests failed! Ошибка тестов! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5382,11 +7627,6 @@ Error: %@ Благодаря пользователям – добавьте переводы через Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - Первая в мире платформа без идентификаторов пользователей. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5399,6 +7639,16 @@ It can happen because of some bug or when the connection is compromised.Приложение может посылать Вам уведомления о сообщениях и запросах на соединение - уведомления можно включить в Настройках. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + Приложение улучшает конфиденциальность используя разных операторов в каждом разговоре. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + Приложение будет запрашивать подтверждение загрузки с неизвестных серверов (за исключением .onion адресов). + No comment provided by engineer. + The attempt to change database passphrase was not completed. Попытка поменять пароль базы данных не была завершена. @@ -5409,6 +7659,11 @@ It can happen because of some bug or when the connection is compromised.Этот QR код не является SimpleX-ccылкой. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + Соединение достигло предела недоставленных сообщений. Возможно, Ваш контакт не в сети. + No comment provided by engineer. + The connection you accepted will be cancelled! Подтвержденное соединение будет отменено! @@ -5429,6 +7684,11 @@ It can happen because of some bug or when the connection is compromised.Шифрование работает, и новое соглашение не требуется. Это может привести к ошибкам соединения! No comment provided by engineer. + + The future of messaging + Будущее коммуникаций + No comment provided by engineer. + The hash of the previous message is different. Хэш предыдущего сообщения отличается. @@ -5436,17 +7696,18 @@ It can happen because of some bug or when the connection is compromised. The message will be deleted for all members. - Сообщение будет удалено для всех членов группы. No comment provided by engineer. The message will be marked as moderated for all members. - Сообщение будет помечено как удаленное для всех членов группы. No comment provided by engineer. - - The next generation of private messaging - Новое поколение приватных сообщений + + The messages will be deleted for all members. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. No comment provided by engineer. @@ -5454,9 +7715,14 @@ It can happen because of some bug or when the connection is compromised.Предыдущая версия данных чата не удалена при перемещении, её можно удалить. No comment provided by engineer. - - The profile is only shared with your contacts. - Профиль отправляется только Вашим контактам. + + The same conditions will apply to operator **%@**. + Те же самые условия будут приняты для оператора **%@**. + No comment provided by engineer. + + + The second preset operator in the app! + Второй оператор серверов в приложении! No comment provided by engineer. @@ -5474,14 +7740,29 @@ It can happen because of some bug or when the connection is compromised.Серверы для новых соединений Вашего текущего профиля чата **%@**. No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + Серверы для новых файлов Вашего текущего профиля **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. Вставленный текст не является SimpleX-ссылкой. No comment provided by engineer. - - Theme - Тема + + The uploaded database archive will be permanently removed from the servers. + Загруженный архив базы данных будет навсегда удален с серверов. + No comment provided by engineer. + + + Themes + Темы + No comment provided by engineer. + + + These conditions will also apply for: **%@**. + Эти условия также будут применены к: **%@**. No comment provided by engineer. @@ -5504,6 +7785,11 @@ It can happen because of some bug or when the connection is compromised.Это действие нельзя отменить — все сообщения, отправленные или полученные раньше чем выбрано, будут удалены. Это может занять несколько минут. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + Это действие нельзя отменить - сообщения в этом чате, отправленные или полученные раньше чем выбрано, будут удалены. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. Это действие нельзя отменить — Ваш профиль, контакты, сообщения и файлы будут безвозвратно утеряны. @@ -5511,10 +7797,12 @@ It can happen because of some bug or when the connection is compromised. This chat is protected by end-to-end encryption. + Чат защищен end-to-end шифрованием. E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + Чат защищен квантово-устойчивым end-to-end шифрованием. E2EE info chat item @@ -5529,7 +7817,6 @@ It can happen because of some bug or when the connection is compromised. This group has over %lld members, delivery receipts are not sent. - В группе более %lld членов, отчёты о доставке выключены. No comment provided by engineer. @@ -5547,11 +7834,30 @@ It can happen because of some bug or when the connection is compromised.Это ваша собственная одноразовая ссылка! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + Эта ссылка была использована на другом мобильном, пожалуйста, создайте новую ссылку на компьютере. + No comment provided by engineer. + + + This message was deleted or not received yet. + Это сообщение было удалено или еще не получено. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. Эта настройка применяется к сообщениям в Вашем текущем профиле чата **%@**. No comment provided by engineer. + + Title + Заголовок + No comment provided by engineer. + To ask any questions and to receive updates: Чтобы задать вопросы и получать уведомления о новых версиях, @@ -5572,9 +7878,9 @@ It can happen because of some bug or when the connection is compromised.Чтобы соединиться No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Чтобы защитить Вашу конфиденциальность, вместо ID пользователей, которые есть в других платформах, SimpleX использует ID для очередей сообщений, разные для каждого контакта. + + To protect against your link being replaced, you can compare contact security codes. + Чтобы защитить Вашу ссылку от замены, Вы можете сравнить код безопасности. No comment provided by engineer. @@ -5582,6 +7888,11 @@ It can happen because of some bug or when the connection is compromised.Чтобы защитить Ваш часовой пояс, файлы картинок и голосовых сообщений используют UTC. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + Чтобы защитить ваш IP адрес, приложение использует Ваши SMP серверы для конфиденциальной доставки сообщений. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5589,6 +7900,26 @@ You will be prompted to complete authentication before this feature is enabled.< Вам будет нужно пройти аутентификацию для включения блокировки. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Чтобы защитить Вашу конфиденциальность, SimpleX использует разные идентификаторы для каждого Вашeго контакта. + No comment provided by engineer. + + + To receive + Для получения + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + Для записи речи, пожалуйста, дайте разрешение на использование микрофона. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Для записи видео, пожалуйста, дайте разрешение на использование камеры. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Для записи голосового сообщения, пожалуйста разрешите доступ к микрофону. @@ -5599,26 +7930,61 @@ You will be prompted to complete authentication before this feature is enabled.< Чтобы показать Ваш скрытый профиль, введите его пароль в поле поиска на странице **Ваши профили чата**. No comment provided by engineer. + + To send + Для оправки + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. Для поддержки мгновенный доставки уведомлений данные чата должны быть перемещены. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + Чтобы использовать серверы оператора **%@**, примите условия использования. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. Чтобы подтвердить end-to-end шифрование с Вашим контактом сравните (или сканируйте) код безопасности на Ваших устройствах. No comment provided by engineer. + + Toggle chat list: + Переключите список чатов: + No comment provided by engineer. + Toggle incognito when connecting. Установите режим Инкогнито при соединении. No comment provided by engineer. + + Token status: %@. + Статус токена: %@. + token status + + + Toolbar opacity + Прозрачность тулбара + No comment provided by engineer. + + + Total + Всего + No comment provided by engineer. + Transport isolation Отдельные сессии для No comment provided by engineer. + + Transport sessions + Транспортные сессии + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Устанавливается соединение с сервером, через который Вы получаете сообщения от этого контакта (ошибка: %@). @@ -5661,23 +8027,20 @@ You will be prompted to complete authentication before this feature is enabled.< Unblock member - Разблокировать члена группы No comment provided by engineer. Unblock member for all? - Разблокировать члена для всех? No comment provided by engineer. Unblock member? - Разблокировать члена группы? No comment provided by engineer. - - Unexpected error: %@ - Неожиданная ошибка: %@ - item status description + + Undelivered messages + Недоставленные сообщения + No comment provided by engineer. Unexpected migration state @@ -5687,7 +8050,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unfav. Не избр. - No comment provided by engineer. + swipe action Unhide @@ -5724,6 +8087,11 @@ You will be prompted to complete authentication before this feature is enabled.< Неизвестная ошибка No comment provided by engineer. + + Unknown servers! + Неизвестные серверы! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. Если Вы не используете интерфейс iOS, включите режим Не отвлекать, чтобы звонок не прерывался. @@ -5759,16 +8127,19 @@ To connect, please ask your contact to create another connection link and check Unmute Уведомлять - No comment provided by engineer. + notification label action Unread Не прочитано + swipe action + + + Unsupported connection link No comment provided by engineer. Up to 100 last messages are sent to new members. - До 100 последних сообщений отправляются новым членам. No comment provided by engineer. @@ -5776,11 +8147,6 @@ To connect, please ask your contact to create another connection link and check Обновить No comment provided by engineer. - - Update .onion hosts setting? - Обновить настройки .onion хостов? - No comment provided by engineer. - Update database passphrase Поменять пароль @@ -5791,9 +8157,14 @@ To connect, please ask your contact to create another connection link and check Обновить настройки сети? No comment provided by engineer. - - Update transport isolation mode? - Обновить режим отдельных сессий? + + Update settings? + Обновить настройки? + No comment provided by engineer. + + + Updated conditions + Обновленные условия No comment provided by engineer. @@ -5801,18 +8172,19 @@ To connect, please ask your contact to create another connection link and check Обновление настроек приведет к сбросу и установке нового соединения со всеми серверами. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - Обновление этих настроек приведет к сбросу и установке нового соединения со всеми серверами. - No comment provided by engineer. - Upgrade and open chat Обновить и открыть чат No comment provided by engineer. + + Upload errors + Ошибки загрузки + No comment provided by engineer. + Upload failed + Ошибка загрузки No comment provided by engineer. @@ -5820,8 +8192,24 @@ To connect, please ask your contact to create another connection link and check Загрузка файла server test step + + Uploaded + Загружено + No comment provided by engineer. + + + Uploaded files + Отправленные файлы + No comment provided by engineer. + Uploading archive + Загрузка архива + No comment provided by engineer. + + + Use %@ + Использовать %@ No comment provided by engineer. @@ -5829,11 +8217,25 @@ To connect, please ask your contact to create another connection link and check Использовать .onion хосты No comment provided by engineer. + + Use SOCKS proxy + Использовать SOCKS прокси + No comment provided by engineer. + Use SimpleX Chat servers? Использовать серверы предосталенные SimpleX Chat? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + Использовать TCP-порт %@, когда порт не указан. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat Использовать чат @@ -5844,6 +8246,16 @@ To connect, please ask your contact to create another connection link and check Использовать активный профиль No comment provided by engineer. + + Use for files + Использовать для файлов + No comment provided by engineer. + + + Use for messages + Использовать для сообщений + No comment provided by engineer. + Use for new connections Использовать для новых соединений @@ -5869,23 +8281,53 @@ To connect, please ask your contact to create another connection link and check Использовать только локальные нотификации? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + Использовать конфиденциальную доставку с неизвестными серверами, когда IP адрес не защищен. + No comment provided by engineer. + + + Use private routing with unknown servers. + Использовать конфиденциальную доставку с неизвестными серверами. + No comment provided by engineer. + Use server Использовать сервер No comment provided by engineer. + + Use servers + Использовать серверы + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. + Используйте приложение во время звонка. No comment provided by engineer. - - User profile - Профиль чата + + Use the app with one hand. + Используйте приложение одной рукой. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - Для использования .onion хостов требуется совместимый VPN провайдер. + + Use web port + Использовать веб-порт + No comment provided by engineer. + + + User selection + Выбор пользователя + No comment provided by engineer. + + + Username + Имя пользователя No comment provided by engineer. @@ -5915,10 +8357,12 @@ To connect, please ask your contact to create another connection link and check Verify database passphrase + Проверка пароля базы данных No comment provided by engineer. Verify passphrase + Проверить пароль No comment provided by engineer. @@ -5956,11 +8400,21 @@ To connect, please ask your contact to create another connection link and check Видео и файлы до 1гб No comment provided by engineer. + + View conditions + Посмотреть условия + No comment provided by engineer. + View security code Показать код безопасности No comment provided by engineer. + + View updated conditions + Посмотреть измененные условия + No comment provided by engineer. + Visible history Доступ к истории @@ -5976,11 +8430,16 @@ To connect, please ask your contact to create another connection link and check Голосовые сообщения запрещены в этом чате. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. Голосовые сообщения запрещены в этой группе. No comment provided by engineer. + + Voice messages not allowed + Голосовые сообщения не разрешены + No comment provided by engineer. + Voice messages prohibited! Голосовые сообщения запрещены! @@ -6011,8 +8470,19 @@ To connect, please ask your contact to create another connection link and check Ожидание видео No comment provided by engineer. + + Wallpaper accent + Рисунок обоев + No comment provided by engineer. + + + Wallpaper background + Фон обоев + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures + Внимание: запуск чата на нескольких устройствах не поддерживается и приведет к сбоям доставки сообщений No comment provided by engineer. @@ -6037,6 +8507,7 @@ To connect, please ask your contact to create another connection link and check Welcome message is too long + Приветственное сообщение слишком длинное No comment provided by engineer. @@ -6049,9 +8520,14 @@ To connect, please ask your contact to create another connection link and check Когда возможно No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Когда Вы получите запрос на соединение, Вы можете принять или отклонить его. + + When connecting audio and video calls. + Во время соединения аудио и видео звонков. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. + Когда больше чем один оператор включен, ни один из них не видит метаданные, чтобы определить, кто соединен с кем. No comment provided by engineer. @@ -6059,6 +8535,21 @@ To connect, please ask your contact to create another connection link and check Когда Вы соединены с контактом инкогнито, тот же самый инкогнито профиль будет использоваться для групп с этим контактом. No comment provided by engineer. + + WiFi + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + Будет включено в прямых разговорах! + No comment provided by engineer. + + + Wired ethernet + Проводная сеть + No comment provided by engineer. + With encrypted files and media. С зашифрованными файлами и медиа. @@ -6074,28 +8565,44 @@ To connect, please ask your contact to create another connection link and check С уменьшенным потреблением батареи. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + Без Тора или ВПН, Ваш IP адрес будет доступен серверам файлов. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + Без Тора или ВПН, Ваш IP адрес будет доступен этим серверам файлов: %@. + alert message + Wrong database passphrase Неправильный пароль базы данных No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + Неверный ключ или неизвестное соединение - скорее всего, это соединение удалено. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + Неверный ключ или неизвестный адрес блока файла - скорее всего, файл удален. + file error text + Wrong passphrase! Неправильный пароль! No comment provided by engineer. - - XFTP servers - XFTP серверы - No comment provided by engineer. - - - You - Вы + + XFTP server + XFTP сервер No comment provided by engineer. You **must not** use the same database on two devices. + Вы **не должны** использовать одну и ту же базу данных на двух устройствах. No comment provided by engineer. @@ -6118,6 +8625,11 @@ To connect, please ask your contact to create another connection link and check Вы уже соединены с контактом %@. No comment provided by engineer. + + You are already connected with %@. + Вы уже соединены с %@. + No comment provided by engineer. + You are already connecting to %@. Вы уже соединяетесь с %@. @@ -6165,11 +8677,26 @@ Repeat join request? Вы приглашены в группу No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + Вы не подключены к этим серверам. Для доставки сообщений на них используется конфиденциальная доставка. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. Вы можете принимать звонки на экране блокировки, без аутентификации. No comment provided by engineer. + + You can change it in Appearance settings. + Вы можете изменить это в настройках Интерфейса. + No comment provided by engineer. + + + You can configure servers via settings. + Вы можете настроить серверы позже. + No comment provided by engineer. + You can create it later Вы можете создать его позже @@ -6187,6 +8714,7 @@ Repeat join request? You can give another try. + Вы можете попробовать еще раз. No comment provided by engineer. @@ -6199,11 +8727,21 @@ Repeat join request? Вы можете сделать его видимым для ваших контактов в SimpleX через Настройки. No comment provided by engineer. - - You can now send messages to %@ - Вы теперь можете отправлять сообщения %@ + + You can now chat with %@ + Вы теперь можете общаться с %@ notification body + + You can send messages to %@ from Archived contacts. + Вы можете отправлять сообщения %@ из Архивированных контактов. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + Вы можете установить имя соединения, чтобы запомнить кому Вы отправили ссылку. + No comment provided by engineer. + You can set lock screen notification preview via settings. Вы можете установить просмотр уведомлений на экране блокировки в настройках. @@ -6211,7 +8749,6 @@ Repeat join request? You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it. - Вы можете поделиться ссылкой или QR кодом - через них можно присоединиться к группе. Вы сможете удалить ссылку, сохранив членов группы, которые через нее соединились. No comment provided by engineer. @@ -6219,16 +8756,16 @@ Repeat join request? Вы можете поделиться этим адресом с Вашими контактами, чтобы они могли соединиться с **%@**. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - Вы можете использовать Ваш адрес как ссылку или как QR код - кто угодно сможет соединиться с Вами. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app Вы можете запустить чат через Настройки приложения или перезапустив приложение. No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + Вы по-прежнему можете просмотреть разговор с %@ в списке чатов. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. Вы можете включить Блокировку SimpleX через Настройки. @@ -6242,23 +8779,23 @@ Repeat join request? You can view invitation link again in connection details. Вы можете увидеть ссылку-приглашение снова открыв соединение. - No comment provided by engineer. + alert message You can't send messages! Вы не можете отправлять сообщения! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Вы определяете через какие серверы Вы **получаете сообщения**, Ваши контакты - серверы, которые Вы используете для отправки. - No comment provided by engineer. - You could not be verified; please try again. Верификация не удалась; пожалуйста, попробуйте ещё раз. No comment provided by engineer. + + You decide who can connect. + Вы определяете, кто может соединиться. + No comment provided by engineer. + You have already requested connection via this address! Вы уже запросили соединение через этот адрес! @@ -6271,11 +8808,6 @@ Repeat connection request? Повторить запрос? No comment provided by engineer. - - You have no chats - У Вас нет чатов - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. Пароль не сохранен на устройстве — Вы будете должны ввести его при каждом запуске чата. @@ -6293,7 +8825,16 @@ Repeat connection request? You joined this group. Connecting to inviting group member. - Вы вступили в эту группу. Устанавливается соединение с пригласившим членом группы. + No comment provided by engineer. + + + You may migrate the exported database. + Вы можете мигрировать экспортированную базу данных. + No comment provided by engineer. + + + You may save the exported archive. + Вы можете сохранить экспортированный архив. No comment provided by engineer. @@ -6301,6 +8842,11 @@ Repeat connection request? Вы должны всегда использовать самую новую версию данных чата, ТОЛЬКО на одном устройстве, иначе Вы можете перестать получать сообщения от каких то контактов. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + Чтобы включить звонки, разрешите их Вашему контакту. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. Чтобы включить отправку голосовых сообщений, разрешите их Вашему контакту. @@ -6316,6 +8862,11 @@ Repeat connection request? Вы отправили приглашение в группу No comment provided by engineer. + + You should receive notifications. + Вы должны получать уведомления. + token info + You will be connected to group when the group host's device is online, please wait or check later! Соединение с группой будет установлено, когда хост группы будет онлайн. Пожалуйста, подождите или проверьте позже! @@ -6343,7 +8894,6 @@ Repeat connection request? You will connect to all group members. - Вы соединитесь со всеми членами группы. No comment provided by engineer. @@ -6351,6 +8901,11 @@ Repeat connection request? Вы все равно получите звонки и уведомления в профилях без звука, когда они активные. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + Вы прекратите получать сообщения в этом разговоре. История будет сохранена. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Вы перестанете получать сообщения от этой группы. История чата будет сохранена. @@ -6371,31 +8926,16 @@ Repeat connection request? Вы используете инкогнито профиль для этой группы - чтобы предотвратить раскрытие Вашего основного профиля, приглашать контакты не разрешено No comment provided by engineer. - - Your %@ servers - Ваши %@ серверы - No comment provided by engineer. - Your ICE servers Ваши ICE серверы No comment provided by engineer. - - Your SMP servers - Ваши SMP серверы - No comment provided by engineer. - Your SimpleX address Ваш адрес SimpleX No comment provided by engineer. - - Your XFTP servers - Ваши XFTP серверы - No comment provided by engineer. - Your calls Ваши звонки @@ -6411,16 +8951,19 @@ Repeat connection request? База данных НЕ зашифрована. Установите пароль, чтобы защитить Ваши данные. No comment provided by engineer. + + Your chat preferences + Ваши настройки чата + alert title + Your chat profiles Ваши профили чата No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Ваш контакт должен быть в сети чтобы установить соединение. -Вы можете отменить соединение и удалить контакт (и попробовать позже с другой ссылкой). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Соединение было перемещено на %@, но при смене профиля произошла неожиданная ошибка. No comment provided by engineer. @@ -6438,6 +8981,11 @@ You can cancel this connection and remove the contact (and try later with a new Ваши контакты сохранятся. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Ваши учетные данные могут быть отправлены в незашифрованном виде. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Текущие данные Вашего чата будет УДАЛЕНЫ и ЗАМЕНЕНЫ импортированными. @@ -6468,33 +9016,36 @@ You can cancel this connection and remove the contact (and try later with a new Будет отправлен Ваш профиль **%@**. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Ваш профиль хранится на Вашем устройстве и отправляется только Вашим контактам. -SimpleX серверы не могут получить доступ к Вашему профилю. + + Your profile is stored on your device and only shared with your contacts. + Ваш профиль храниться на Вашем устройстве и отправляется только контактам. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Ваш профиль, контакты и доставленные сообщения хранятся на Вашем устройстве. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Ваш профиль хранится на Вашем устройстве и отправляется только Вашим контактам. SimpleX серверы не могут получить доступ к Вашему профилю. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Ваш профиль был изменен. Если вы сохраните его, обновленный профиль будет отправлен всем вашим контактам. + alert message + Your random profile Случайный профиль No comment provided by engineer. - - Your server - Ваш сервер - No comment provided by engineer. - Your server address Адрес Вашего сервера No comment provided by engineer. + + Your servers + Ваши серверы + No comment provided by engineer. + Your settings Настройки @@ -6535,11 +9086,21 @@ SimpleX серверы не могут получить доступ к Ваше принятый звонок call status + + accepted invitation + принятое приглашение + chat list item title + admin админ member role + + admins + админы + feature role + agreeing encryption for %@… шифрование согласовывается для %@… @@ -6550,6 +9111,10 @@ SimpleX серверы не могут получить доступ к Ваше шифрование согласовывается… chat item text + + all members + feature role + always всегда @@ -6560,6 +9125,16 @@ SimpleX серверы не могут получить доступ к Ваше и %lld других событий No comment provided by engineer. + + archived report + заархивированное сообщение о нарушении + No comment provided by engineer. + + + attempts + попытки + No comment provided by engineer. + audio call (not e2e encrypted) аудиозвонок (не e2e зашифрованный) @@ -6593,13 +9168,19 @@ SimpleX серверы не могут получить доступ к Ваше blocked by admin заблокировано администратором - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold жирный No comment provided by engineer. + + call + звонок + No comment provided by engineer. + call error ошибка звонка @@ -6703,7 +9284,7 @@ SimpleX серверы не могут получить доступ к Ваше connecting… соединяется… - chat list item title + No comment provided by engineer. connection established @@ -6750,10 +9331,16 @@ SimpleX серверы не могут получить доступ к Ваше дней time unit + + decryption errors + ошибки расшифровки + No comment provided by engineer. + default (%@) по умолчанию (%@) - pref value + delete after time +pref value default (no) @@ -6800,6 +9387,11 @@ SimpleX серверы не могут получить доступ к Ваше повторное сообщение integrity error chat item + + duplicates + дубликаты + No comment provided by engineer. + e2e encrypted e2e зашифровано @@ -6875,9 +9467,14 @@ SimpleX серверы не могут получить доступ к Ваше ошибка No comment provided by engineer. - - event happened - событие произошло + + expired + истекло + No comment provided by engineer. + + + forwarded + переслано No comment provided by engineer. @@ -6905,6 +9502,11 @@ SimpleX серверы не могут получить доступ к Ваше Пароль базы данных будет безопасно сохранен в iOS Keychain после запуска чата или изменения пароля - это позволит получать мгновенные уведомления. No comment provided by engineer. + + inactive + неактивен + No comment provided by engineer. + incognito via contact address link инкогнито через ссылку-контакт @@ -6945,6 +9547,11 @@ SimpleX серверы не могут получить доступ к Ваше приглашение в группу %@ group name + + invite + пригласить + No comment provided by engineer. + invited приглашен(а) @@ -6987,12 +9594,10 @@ SimpleX серверы не могут получить доступ к Ваше member - член группы member role member %1$@ changed to %2$@ - член %1$@ изменился на %2$@ profile update event chat item @@ -7000,6 +9605,11 @@ SimpleX серверы не могут получить доступ к Ваше соединен(а) rcv group event chat item + + message + написать + No comment provided by engineer. + message received получено сообщение @@ -7025,6 +9635,11 @@ SimpleX серверы не могут получить доступ к Ваше удалено %@ marked deleted chat item preview text + + moderator + модератор + member role + months месяцев @@ -7033,7 +9648,7 @@ SimpleX серверы не могут получить доступ к Ваше never никогда - No comment provided by engineer. + delete after time new message @@ -7064,8 +9679,8 @@ SimpleX серверы не могут получить доступ к Ваше off нет enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7082,18 +9697,44 @@ SimpleX серверы не могут получить доступ к Ваше да group pref value + + other + другое + No comment provided by engineer. + + + other errors + другие ошибки + No comment provided by engineer. + owner владелец member role + + owners + владельцы + feature role + peer-to-peer peer-to-peer No comment provided by engineer. + + pending + ожидает + No comment provided by engineer. + + + pending approval + ожидает утверждения + No comment provided by engineer. + quantum resistant e2e encryption + квантово-устойчивое e2e шифрование chat item text @@ -7106,6 +9747,11 @@ SimpleX серверы не могут получить доступ к Ваше получено подтверждение… No comment provided by engineer. + + rejected + отклонён + No comment provided by engineer. + rejected call отклонённый звонок @@ -7136,6 +9782,26 @@ SimpleX серверы не могут получить доступ к Ваше удалил(а) Вас из группы rcv group event chat item + + requested to connect + запрошено соединение + chat list item title + + + saved + сохранено + No comment provided by engineer. + + + saved from %@ + сохранено из %@ + No comment provided by engineer. + + + search + поиск + No comment provided by engineer. + sec сек @@ -7161,6 +9827,15 @@ SimpleX серверы не могут получить доступ к Ваше отправьте сообщение No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + информация сервера об очереди: %1$@ + +последнее полученное сообщение: %2$@ + queue info + set new contact address установлен новый адрес контакта @@ -7173,6 +9848,7 @@ SimpleX серверы не могут получить доступ к Ваше standard end-to-end encryption + стандартное end-to-end шифрование chat item text @@ -7200,11 +9876,21 @@ SimpleX серверы не могут получить доступ к Ваше неизвестно connection info + + unknown servers + неизвестные серверы + No comment provided by engineer. + unknown status неизвестный статус No comment provided by engineer. + + unprotected + незащищённый + No comment provided by engineer. + updated group profile обновил(а) профиль группы @@ -7245,6 +9931,11 @@ SimpleX серверы не могут получить доступ к Ваше через relay сервер No comment provided by engineer. + + video + видеозвонок + No comment provided by engineer. + video call (not e2e encrypted) видеозвонок (не e2e зашифрованный) @@ -7270,11 +9961,21 @@ SimpleX серверы не могут получить доступ к Ваше недель time unit + + when IP hidden + когда IP защищен + No comment provided by engineer. + yes да pref value + + you + Вы + No comment provided by engineer. + you are invited to group Вы приглашены в группу @@ -7349,7 +10050,7 @@ SimpleX серверы не могут получить доступ к Ваше
- +
@@ -7386,7 +10087,7 @@ SimpleX серверы не могут получить доступ к Ваше
- +
@@ -7406,4 +10107,249 @@ SimpleX серверы не могут получить доступ к Ваше
+ +
+ +
+ + + %d new events + %d новых сообщений + notification body + + + From %d chat(s) + notification body + + + From: %@ + От: %@ + notification body + + + New events + Новые события + notification + + + New messages + Новые сообщения + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright © 2024 SimpleX Chat. Все права защищены. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + Приложение заблокировано! + No comment provided by engineer. + + + Cancel + Отменить + No comment provided by engineer. + + + Cannot access keychain to save database password + Невозможно сохранить пароль в keychain + No comment provided by engineer. + + + Cannot forward message + Невозможно переслать сообщение + No comment provided by engineer. + + + Comment + Комментарий + No comment provided by engineer. + + + Currently maximum supported file size is %@. + В настоящее время максимальный поддерживаемый размер файла составляет %@. + No comment provided by engineer. + + + Database downgrade required + Требуется откат базы данных + No comment provided by engineer. + + + Database encrypted! + База данных зашифрована! + No comment provided by engineer. + + + Database error + Ошибка базы данных + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + Пароль базы данных отличается от сохраненного в keychain. + No comment provided by engineer. + + + Database passphrase is required to open chat. + Введите пароль базы данных, чтобы открыть чат. + No comment provided by engineer. + + + Database upgrade required + Требуется обновление базы данных + No comment provided by engineer. + + + Error preparing file + Ошибка подготовки файла + No comment provided by engineer. + + + Error preparing message + Ошибка подготовки сообщения + No comment provided by engineer. + + + Error: %@ + Ошибка: %@ + No comment provided by engineer. + + + File error + Ошибка файла + No comment provided by engineer. + + + Incompatible database version + Несовместимая версия базы данных + No comment provided by engineer. + + + Invalid migration confirmation + Ошибка подтверждения миграции + No comment provided by engineer. + + + Keychain error + Ошибка keychain + No comment provided by engineer. + + + Large file! + Большой файл! + No comment provided by engineer. + + + No active profile + Нет активного профиля + No comment provided by engineer. + + + Ok + Ок + No comment provided by engineer. + + + Open the app to downgrade the database. + Откройте приложение, чтобы откатить базу данных. + No comment provided by engineer. + + + Open the app to upgrade the database. + Откройте приложение, чтобы обновить базу данных. + No comment provided by engineer. + + + Passphrase + Пароль + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Пожалуйста, создайте профиль в приложении SimpleX. + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Выбранные настройки чата запрещают это сообщение. + No comment provided by engineer. + + + Sending a message takes longer than expected. + Отправка сообщения занимает дольше ожиданного. + No comment provided by engineer. + + + Sending message… + Отправка сообщения… + No comment provided by engineer. + + + Share + Поделиться + No comment provided by engineer. + + + Slow network? + Медленная сеть? + No comment provided by engineer. + + + Unknown database error: %@ + Неизвестная ошибка базы данных: %@ + No comment provided by engineer. + + + Unsupported format + Неподдерживаемый формат + No comment provided by engineer. + + + Wait + Подождать + No comment provided by engineer. + + + Wrong database passphrase + Неправильный пароль базы данных + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + Вы можете разрешить функцию Поделиться в настройках Конфиденциальности / Блокировка SimpleX. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/contents.json b/apps/ios/SimpleX Localizations/ru.xcloc/contents.json index 2d5d76dd8f..b49b25d653 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/ru.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "ru", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff index e1b0e4d3e4..671dd87d7d 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff +++ b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (สามารถคัดลอกได้) @@ -120,9 +93,12 @@ %@ ได้รับการตรวจสอบแล้ว No comment provided by engineer. + + %@ server + No comment provided by engineer. + %@ servers - %@ เซิร์ฟเวอร์ No comment provided by engineer. @@ -134,6 +110,10 @@ %@ อยากเชื่อมต่อ! notification title + + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members No comment provided by engineer. @@ -152,11 +132,31 @@ %d วัน time interval + + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d ชั่วโมง time interval + + %d messages not forwarded + alert title + %d min %d นาที @@ -172,6 +172,10 @@ %d วินาที time interval + + %d seconds(s) + delete after time + %d skipped message(s) %d ข้อความที่ถูกข้าม @@ -236,11 +240,6 @@ %lld new interface languages No comment provided by engineer. - - %lld second(s) - %lld วินาที - No comment provided by engineer. - %lld seconds %lld วินาที @@ -291,11 +290,6 @@ %u ข้อความที่ถูกข้าม No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) No comment provided by engineer. @@ -304,31 +298,21 @@ (this device v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **เพิ่มผู้ติดต่อใหม่**: เพื่อสร้างคิวอาร์โค้ดแบบใช้ครั้งเดียวหรือลิงก์สำหรับผู้ติดต่อของคุณ + + **Create 1-time link**: to create and share a new invitation link. No comment provided by engineer. **Create group**: to create a new group. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **เป็นส่วนตัวมากขึ้น**: ตรวจสอบข้อความใหม่ทุกๆ 20 นาที โทเค็นอุปกรณ์แชร์กับเซิร์ฟเวอร์ SimpleX Chat แต่ไม่ระบุจำนวนผู้ติดต่อหรือข้อความที่คุณมี No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **ส่วนตัวที่สุด**: ไม่ใช้เซิร์ฟเวอร์การแจ้งเตือนของ SimpleX Chat ตรวจสอบข้อความเป็นระยะในพื้นหลัง (ขึ้นอยู่กับความถี่ที่คุณใช้แอป) No comment provided by engineer. @@ -341,11 +325,15 @@ **โปรดทราบ**: คุณจะไม่สามารถกู้คืนหรือเปลี่ยนรหัสผ่านได้หากคุณทำรหัสผ่านหาย No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **แนะนำ**: โทเค็นอุปกรณ์และการแจ้งเตือนจะถูกส่งไปยังเซิร์ฟเวอร์การแจ้งเตือนของ SimpleX Chat แต่ไม่ใช่เนื้อหาข้อความ ขนาด หรือผู้ที่ส่ง No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **คำเตือน**: การแจ้งเตือนแบบพุชทันทีจำเป็นต้องบันทึกรหัสผ่านไว้ใน Keychain @@ -370,11 +358,6 @@ \*ตัวหนา* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -405,11 +388,6 @@ - ประวัติการแก้ไข No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec time to disappear @@ -422,7 +400,8 @@ 1 day 1 วัน - time interval + delete after time +time interval 1 hour @@ -437,12 +416,26 @@ 1 month 1 เดือน - time interval + delete after time +time interval 1 week 1 สัปดาห์ - time interval + delete after time +time interval + + + 1 year + delete after time + + + 1-time link + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + No comment provided by engineer. 5 minutes @@ -459,11 +452,6 @@ 30 วินาที No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -512,31 +500,29 @@ ยกเลิกการเปลี่ยนที่อยู่? No comment provided by engineer. - - About SimpleX - เกี่ยวกับ SimpleX - No comment provided by engineer. - About SimpleX Chat เกี่ยวกับ SimpleX Chat No comment provided by engineer. - - About SimpleX address - เกี่ยวกับที่อยู่ SimpleX + + About operators No comment provided by engineer. - - Accent color - สีเน้น + + Accent No comment provided by engineer. Accept รับ accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + No comment provided by engineer. Accept connection request? @@ -550,20 +536,40 @@ Accept incognito ยอมรับโหมดไม่ระบุตัวตน - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + No comment provided by engineer. + + + Acknowledged + No comment provided by engineer. + + + Acknowledgement errors + No comment provided by engineer. + + + Active + token status text + + + Active connections + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. เพิ่มที่อยู่ลงในโปรไฟล์ของคุณ เพื่อให้ผู้ติดต่อของคุณสามารถแชร์กับผู้อื่นได้ การอัปเดตโปรไฟล์จะถูกส่งไปยังผู้ติดต่อของคุณ No comment provided by engineer. - - Add contact + + Add friends No comment provided by engineer. - - Add preset servers - เพิ่มเซิร์ฟเวอร์ที่ตั้งไว้ล่วงหน้า + + Add list No comment provided by engineer. @@ -571,14 +577,18 @@ เพิ่มโปรไฟล์ No comment provided by engineer. + + Add server + เพิ่มเซิร์ฟเวอร์ + No comment provided by engineer. + Add servers by scanning QR codes. เพิ่มเซิร์ฟเวอร์โดยการสแกนรหัสคิวอาร์โค้ด No comment provided by engineer. - - Add server… - เพิ่มเซิร์ฟเวอร์… + + Add team members No comment provided by engineer. @@ -586,11 +596,39 @@ เพิ่มเข้าไปในอุปกรณ์อื่น No comment provided by engineer. + + Add to list + No comment provided by engineer. + Add welcome message เพิ่มข้อความต้อนรับ No comment provided by engineer. + + Add your team members to the conversations. + No comment provided by engineer. + + + Added media & file servers + No comment provided by engineer. + + + Added message servers + No comment provided by engineer. + + + Additional accent + No comment provided by engineer. + + + Additional accent 2 + No comment provided by engineer. + + + Additional secondary + No comment provided by engineer. + Address ที่อยู่ @@ -601,6 +639,14 @@ การเปลี่ยนแปลงที่อยู่จะถูกยกเลิก จะใช้ที่อยู่เก่าของผู้รับ No comment provided by engineer. + + Address or 1-time link? + No comment provided by engineer. + + + Address settings + No comment provided by engineer. + Admins can block a member for all. No comment provided by engineer. @@ -615,6 +661,14 @@ การตั้งค่าระบบเครือข่ายขั้นสูง No comment provided by engineer. + + Advanced settings + No comment provided by engineer. + + + All + No comment provided by engineer. + All app data is deleted. ข้อมูลแอปทั้งหมดถูกลบแล้ว. @@ -625,16 +679,28 @@ แชทและข้อความทั้งหมดจะถูกลบ - การดำเนินการนี้ไม่สามารถยกเลิกได้! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + alert message + All data is erased when it is entered. ข้อมูลทั้งหมดจะถูกลบเมื่อถูกป้อน No comment provided by engineer. + + All data is kept private on your device. + No comment provided by engineer. + All group members will remain connected. สมาชิกในกลุ่มทุกคนจะยังคงเชื่อมต่ออยู่. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! No comment provided by engineer. @@ -648,6 +714,18 @@ All new messages from %@ will be hidden! No comment provided by engineer. + + All profiles + profile dropdown + + + All reports will be archived for you. + No comment provided by engineer. + + + All servers + No comment provided by engineer. + All your contacts will remain connected. ผู้ติดต่อทั้งหมดของคุณจะยังคงเชื่อมต่ออยู่. @@ -672,11 +750,19 @@ อนุญาตการโทรเฉพาะเมื่อผู้ติดต่อของคุณอนุญาตเท่านั้น. No comment provided by engineer. + + Allow calls? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. อนุญาตให้ข้อความที่หายไปเฉพาะในกรณีที่ผู้ติดต่อของคุณอนุญาตเท่านั้น. No comment provided by engineer. + + Allow downgrade + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) อนุญาตให้ลบข้อความแบบถาวรเฉพาะในกรณีที่ผู้ติดต่อของคุณอนุญาตให้คุณเท่านั้น @@ -702,11 +788,23 @@ อนุญาตให้ส่งข้อความที่จะหายไปหลังปิดแชท (disappearing message) No comment provided by engineer. + + Allow sharing + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) อนุญาตให้ลบข้อความที่ส่งไปแล้วอย่างถาวร No comment provided by engineer. + + Allow to report messsages to moderators. + No comment provided by engineer. + + + Allow to send SimpleX links. + No comment provided by engineer. + Allow to send files and media. อนุญาตให้ส่งไฟล์และสื่อ @@ -765,6 +863,10 @@ Already joining the group! No comment provided by engineer. + + Always use private routing. + No comment provided by engineer. + Always use relay ใช้รีเลย์เสมอ @@ -775,11 +877,20 @@ โปรไฟล์แชทที่ว่างเปล่าพร้อมชื่อที่ให้ไว้ได้ถูกสร้างขึ้นและแอปจะเปิดตามปกติ No comment provided by engineer. + + Another reason + report reason + Answer call รับสาย No comment provided by engineer. + + Anybody can host servers. + โปรโตคอลและโค้ดโอเพ่นซอร์ส – ใคร ๆ ก็สามารถเปิดใช้เซิร์ฟเวอร์ได้ + No comment provided by engineer. + App build: %@ รุ่นแอป: %@ @@ -793,6 +904,10 @@ App encrypts new local files (except videos). No comment provided by engineer. + + App group: + No comment provided by engineer. + App icon ไอคอนแอป @@ -808,6 +923,10 @@ รหัสผ่านแอปจะถูกแทนที่ด้วยรหัสผ่านที่ทำลายตัวเอง No comment provided by engineer. + + App session + No comment provided by engineer. + App version เวอร์ชันแอป @@ -827,10 +946,46 @@ Apply No comment provided by engineer. + + Apply to + No comment provided by engineer. + + + Archive + No comment provided by engineer. + + + Archive %lld reports? + No comment provided by engineer. + + + Archive all reports? + No comment provided by engineer. + Archive and upload No comment provided by engineer. + + Archive contacts to chat later. + No comment provided by engineer. + + + Archive report + No comment provided by engineer. + + + Archive report? + No comment provided by engineer. + + + Archive reports + swipe action + + + Archived contacts + No comment provided by engineer. + Archiving database No comment provided by engineer. @@ -895,11 +1050,19 @@ ยอมรับภาพอัตโนมัติ No comment provided by engineer. + + Auto-accept settings + alert title + Back กลับ No comment provided by engineer. + + Background + No comment provided by engineer. + Bad desktop address No comment provided by engineer. @@ -914,15 +1077,51 @@ แฮชข้อความไม่ดี No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups No comment provided by engineer. + + Better groups performance + No comment provided by engineer. + + + Better message dates. + No comment provided by engineer. + Better messages ข้อความที่ดีขึ้น No comment provided by engineer. + + Better networking + No comment provided by engineer. + + + Better notifications + No comment provided by engineer. + + + Better privacy and security + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + + + Black + No comment provided by engineer. + Block No comment provided by engineer. @@ -951,6 +1150,14 @@ Blocked by admin No comment provided by engineer. + + Blur for better privacy. + No comment provided by engineer. + + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. ทั้งคุณและผู้ติดต่อของคุณสามารถเพิ่มปฏิกิริยาของข้อความได้ @@ -980,11 +1187,29 @@ Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + No comment provided by engineer. + + + Business chats + No comment provided by engineer. + + + Businesses + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). ตามโปรไฟล์แชท (ค่าเริ่มต้น) หรือ [โดยการเชื่อมต่อ](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (เบต้า) No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + No comment provided by engineer. + Call already ended! สิ้นสุดการโทรแล้ว! @@ -995,10 +1220,22 @@ โทร No comment provided by engineer. + + Calls prohibited! + No comment provided by engineer. + Camera not available No comment provided by engineer. + + Can't call contact + No comment provided by engineer. + + + Can't call member + No comment provided by engineer. + Can't invite contact! ไม่สามารถเชิญผู้ติดต่อได้! @@ -1009,10 +1246,15 @@ ไม่สามารถเชิญผู้ติดต่อได้! No comment provided by engineer. + + Can't message member + No comment provided by engineer. + Cancel ยกเลิก - No comment provided by engineer. + alert action +alert button Cancel migration @@ -1023,9 +1265,21 @@ ไม่สามารถเข้าถึง keychain เพื่อบันทึกรหัสผ่านฐานข้อมูล No comment provided by engineer. + + Cannot forward message + No comment provided by engineer. + Cannot receive file ไม่สามารถรับไฟล์ได้ + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + snd error text + + + Cellular No comment provided by engineer. @@ -1033,6 +1287,14 @@ เปลี่ยน No comment provided by engineer. + + Change automatic message deletion? + alert title + + + Change chat profiles + authentication reason + Change database passphrase? เปลี่ยนรหัสผ่านฐานข้อมูล? @@ -1077,11 +1339,22 @@ Change self-destruct passcode เปลี่ยนรหัสผ่านแบบทำลายตัวเอง authentication reason - set passcode view +set passcode view - - Chat archive - ที่เก็บแชทถาวร + + Chat + No comment provided by engineer. + + + Chat already exists + No comment provided by engineer. + + + Chat already exists! + No comment provided by engineer. + + + Chat colors No comment provided by engineer. @@ -1099,6 +1372,10 @@ ลบฐานข้อมูลแชทแล้ว No comment provided by engineer. + + Chat database exported + No comment provided by engineer. + Chat database imported นำฐานข้อมูลแชทเข้าแล้ว @@ -1118,6 +1395,10 @@ Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat. No comment provided by engineer. + + Chat list + No comment provided by engineer. + Chat migrated! No comment provided by engineer. @@ -1127,15 +1408,44 @@ ค่ากําหนดในการแชท No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + โปรไฟล์ผู้ใช้ + No comment provided by engineer. + + + Chat theme + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + No comment provided by engineer. + Chats แชท No comment provided by engineer. + + Check messages every 20 min. + No comment provided by engineer. + + + Check messages when allowed. + No comment provided by engineer. + Check server address and try again. ตรวจสอบที่อยู่เซิร์ฟเวอร์แล้วลองอีกครั้ง - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1156,10 +1466,22 @@ เลือกจากอัลบั้ม No comment provided by engineer. + + Chunks deleted + No comment provided by engineer. + + + Chunks downloaded + No comment provided by engineer. + + + Chunks uploaded + No comment provided by engineer. + Clear ลบ - No comment provided by engineer. + swipe action Clear conversation @@ -1171,6 +1493,14 @@ ลบการสนทนา? No comment provided by engineer. + + Clear group? + No comment provided by engineer. + + + Clear or delete group? + No comment provided by engineer. + Clear private notes? No comment provided by engineer. @@ -1180,11 +1510,18 @@ ล้างการยืนยัน No comment provided by engineer. - - Colors - สี + + Color chats with the new themes. No comment provided by engineer. + + Color mode + No comment provided by engineer. + + + Community guidelines violation + report reason + Compare file เปรียบเทียบไฟล์ @@ -1195,11 +1532,47 @@ เปรียบเทียบรหัสความปลอดภัยกับผู้ติดต่อของคุณ No comment provided by engineer. + + Completed + No comment provided by engineer. + + + Conditions accepted on: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + No comment provided by engineer. + + + Conditions of use + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + No comment provided by engineer. + Configure ICE servers กำหนดค่าเซิร์ฟเวอร์ ICE No comment provided by engineer. + + Configure server operators + No comment provided by engineer. + Confirm ยืนยัน @@ -1210,11 +1583,19 @@ ยืนยันรหัสผ่าน No comment provided by engineer. + + Confirm contact deletion? + No comment provided by engineer. + Confirm database upgrades ยืนยันการอัพเกรดฐานข้อมูล No comment provided by engineer. + + Confirm files from unknown servers. + No comment provided by engineer. + Confirm network settings No comment provided by engineer. @@ -1237,6 +1618,10 @@ Confirm upload No comment provided by engineer. + + Confirmed + token status text + Connect เชื่อมต่อ @@ -1254,6 +1639,10 @@ Connect to desktop No comment provided by engineer. + + Connect to your friends faster. + No comment provided by engineer. + Connect to yourself? No comment provided by engineer. @@ -1285,14 +1674,26 @@ This is your own one-time link! Connect with %@ No comment provided by engineer. + + Connected + No comment provided by engineer. + Connected desktop No comment provided by engineer. + + Connected servers + No comment provided by engineer. + Connected to desktop No comment provided by engineer. + + Connecting + No comment provided by engineer. + Connecting to server… กำลังเชื่อมต่อกับเซิร์ฟเวอร์… @@ -1303,6 +1704,10 @@ This is your own one-time link! กำลังเชื่อมต่อกับเซิร์ฟเวอร์... (ข้อผิดพลาด: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + No comment provided by engineer. + Connecting to desktop No comment provided by engineer. @@ -1312,6 +1717,14 @@ This is your own one-time link! การเชื่อมต่อ No comment provided by engineer. + + Connection and servers status. + No comment provided by engineer. + + + Connection blocked + No comment provided by engineer. + Connection error การเชื่อมต่อผิดพลาด @@ -1322,11 +1735,32 @@ This is your own one-time link! การเชื่อมต่อผิดพลาด (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + No comment provided by engineer. + + + Connection not ready. + No comment provided by engineer. + + + Connection notifications + No comment provided by engineer. + Connection request sent! ส่งคําขอเชื่อมต่อแล้ว! No comment provided by engineer. + + Connection requires encryption renegotiation. + No comment provided by engineer. + + + Connection security + No comment provided by engineer. + Connection terminated No comment provided by engineer. @@ -1336,6 +1770,14 @@ This is your own one-time link! หมดเวลาการเชื่อมต่อ No comment provided by engineer. + + Connection with desktop stopped + No comment provided by engineer. + + + Connections + No comment provided by engineer. + Contact allows ผู้ติดต่ออนุญาต @@ -1346,6 +1788,10 @@ This is your own one-time link! ผู้ติดต่อรายนี้มีอยู่แล้ว No comment provided by engineer. + + Contact deleted! + No comment provided by engineer. + Contact hidden: ผู้ติดต่อถูกซ่อน: @@ -1356,9 +1802,8 @@ This is your own one-time link! เชื่อมต่อกับผู้ติดต่อแล้ว notification - - Contact is not connected yet! - ผู้ติดต่อยังไม่ได้เชื่อมต่อ! + + Contact is deleted. No comment provided by engineer. @@ -1371,6 +1816,10 @@ This is your own one-time link! การกําหนดลักษณะการติดต่อ No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + No comment provided by engineer. + Contacts ติดต่อ @@ -1381,21 +1830,37 @@ This is your own one-time link! ผู้ติดต่อสามารถทําเครื่องหมายข้อความเพื่อลบได้ คุณจะสามารถดูได้ No comment provided by engineer. + + Content violates conditions of use + blocking reason + Continue ดำเนินการต่อ No comment provided by engineer. + + Conversation deleted! + No comment provided by engineer. + Copy คัดลอก - chat item action + No comment provided by engineer. + + + Copy error + No comment provided by engineer. Core version: v%@ รุ่นหลัก: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? No comment provided by engineer. @@ -1405,6 +1870,10 @@ This is your own one-time link! สร้าง No comment provided by engineer. + + Create 1-time link + No comment provided by engineer. + Create SimpleX address สร้างที่อยู่ SimpleX @@ -1414,11 +1883,6 @@ This is your own one-time link! Create a group using a random profile. No comment provided by engineer. - - Create an address to let people connect with you. - สร้างที่อยู่เพื่อให้ผู้อื่นเชื่อมต่อกับคุณ - No comment provided by engineer. - Create file สร้างไฟล์ @@ -1438,6 +1902,10 @@ This is your own one-time link! สร้างลิงค์ No comment provided by engineer. + + Create list + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 No comment provided by engineer. @@ -1461,6 +1929,10 @@ This is your own one-time link! สร้างโปรไฟล์ของคุณ No comment provided by engineer. + + Created + No comment provided by engineer. + Created at No comment provided by engineer. @@ -1469,11 +1941,6 @@ This is your own one-time link! Created at: %@ copied message info - - Created on %@ - สร้างเมื่อ %@ - No comment provided by engineer. - Creating archive link No comment provided by engineer. @@ -1487,11 +1954,19 @@ This is your own one-time link! รหัสผ่านปัจจุบัน No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + No comment provided by engineer. + Current passphrase… รหัสผ่านปัจจุบัน… No comment provided by engineer. + + Current profile + No comment provided by engineer. + Currently maximum supported file size is %@. ขนาดไฟล์ที่รองรับสูงสุดในปัจจุบันคือ %@ @@ -1502,11 +1977,23 @@ This is your own one-time link! เวลาที่กําหนดเอง No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + + + Customize theme + No comment provided by engineer. + Dark มืด No comment provided by engineer. + + Dark mode colors + No comment provided by engineer. + Database ID ID ฐานข้อมูล @@ -1605,6 +2092,10 @@ This is your own one-time link! ระบบจะย้ายฐานข้อมูลเมื่อแอปรีสตาร์ท No comment provided by engineer. + + Debug delivery + No comment provided by engineer. + Decentralized กระจายอำนาจแล้ว @@ -1618,17 +2109,17 @@ This is your own one-time link! Delete ลบ - chat item action + alert action +swipe action + + + Delete %lld messages of members? + No comment provided by engineer. Delete %lld messages? No comment provided by engineer. - - Delete Contact - ลบผู้ติดต่อ - No comment provided by engineer. - Delete address ลบที่อยู่ @@ -1653,14 +2144,12 @@ This is your own one-time link! Delete and notify contact No comment provided by engineer. - - Delete archive - ลบที่เก็บถาวร + + Delete chat No comment provided by engineer. - - Delete chat archive? - ลบที่เก็บแชทถาวร? + + Delete chat messages from your device. No comment provided by engineer. @@ -1673,6 +2162,10 @@ This is your own one-time link! ลบโปรไฟล์แชทไหม? No comment provided by engineer. + + Delete chat? + No comment provided by engineer. + Delete connection ลบการเชื่อมต่อ @@ -1683,9 +2176,8 @@ This is your own one-time link! ลบผู้ติดต่อ No comment provided by engineer. - - Delete contact? -This cannot be undone! + + Delete contact? No comment provided by engineer. @@ -1747,6 +2239,10 @@ This cannot be undone! ลบลิงค์ ไหม? No comment provided by engineer. + + Delete list? + alert title + Delete member message? ลบข้อความสมาชิก? @@ -1760,7 +2256,7 @@ This cannot be undone! Delete messages ลบข้อความ - No comment provided by engineer. + alert button Delete messages after @@ -1777,9 +2273,8 @@ This cannot be undone! ลบฐานข้อมูลเก่า? No comment provided by engineer. - - Delete pending connection - ลบการเชื่อมต่อที่รอดำเนินการ + + Delete or moderate up to 200 messages. No comment provided by engineer. @@ -1797,11 +2292,27 @@ This cannot be undone! ลบคิว server test step + + Delete report + No comment provided by engineer. + + + Delete up to 20 messages at once. + No comment provided by engineer. + Delete user profile? ลบโปรไฟล์ผู้ใช้? No comment provided by engineer. + + Delete without notification + No comment provided by engineer. + + + Deleted + No comment provided by engineer. + Deleted at ลบที่ @@ -1812,6 +2323,14 @@ This cannot be undone! ลบที่: %@ copied message info + + Deletion errors + No comment provided by engineer. + + + Delivered even when Apple drops them. + No comment provided by engineer. + Delivery No comment provided by engineer. @@ -1843,11 +2362,35 @@ This cannot be undone! Desktop devices No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + + + Destination server error: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + + + Detailed statistics + No comment provided by engineer. + + + Details + No comment provided by engineer. + Develop พัฒนา No comment provided by engineer. + + Developer options + No comment provided by engineer. + Developer tools เครื่องมือสำหรับนักพัฒนา @@ -1878,8 +2421,12 @@ This cannot be undone! ข้อความโดยตรง chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + No comment provided by engineer. + + + Direct messages between members are prohibited. ข้อความโดยตรงระหว่างสมาชิกเป็นสิ่งต้องห้ามในกลุ่มนี้ No comment provided by engineer. @@ -1893,11 +2440,23 @@ This cannot be undone! ปิดการใช้งาน SimpleX Lock authentication reason + + Disable automatic message deletion? + alert title + + + Disable delete messages + alert button + Disable for all ปิดการใช้งานสำหรับทุกคน No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message ข้อความที่จะหายไปหลังเวลาที่กําหนด (disappearing message) @@ -1913,8 +2472,8 @@ This cannot be undone! ข้อความที่จะหายไปหลังเวลาที่กําหนด (disappearing message) เป็นสิ่งต้องห้ามในแชทนี้ No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. ข้อความที่จะหายไปหลังเวลาที่กําหนด (disappearing message) เป็นสิ่งต้องห้ามในกลุ่มนี้ No comment provided by engineer. @@ -1945,11 +2504,19 @@ This cannot be undone! Discover via local network No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. อย่าใช้ SimpleX สําหรับการโทรฉุกเฉิน No comment provided by engineer. + + Do NOT use private routing. + No comment provided by engineer. + Do it later ทำในภายหลัง @@ -1959,6 +2526,14 @@ This cannot be undone! Do not send history to new members. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + + + Documents: + No comment provided by engineer. + Don't create address อย่าสร้างที่อยู่ @@ -1969,16 +2544,33 @@ This cannot be undone! อย่าเปิดใช้งาน No comment provided by engineer. + + Don't miss important messages. + No comment provided by engineer. + Don't show again ไม่ต้องแสดงอีก No comment provided by engineer. + + Done + No comment provided by engineer. + Downgrade and open chat ปรับลดรุ่นและเปิดแชท No comment provided by engineer. + + Download + alert button +chat item action + + + Download errors + No comment provided by engineer. + Download failed No comment provided by engineer. @@ -1988,6 +2580,18 @@ This cannot be undone! ดาวน์โหลดไฟล์ server test step + + Download files + alert action + + + Downloaded + No comment provided by engineer. + + + Downloaded files + No comment provided by engineer. + Downloading archive No comment provided by engineer. @@ -2006,6 +2610,10 @@ This cannot be undone! ระยะเวลา No comment provided by engineer. + + E2E encrypted notifications. + No comment provided by engineer. + Edit แก้ไข @@ -2026,6 +2634,10 @@ This cannot be undone! เปิดใช้งาน (เก็บการแทนที่) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + No comment provided by engineer. + Enable SimpleX Lock เปิดใช้งาน SimpleX Lock @@ -2039,7 +2651,7 @@ This cannot be undone! Enable automatic message deletion? เปิดใช้งานการลบข้อความอัตโนมัติ? - No comment provided by engineer. + alert title Enable camera access @@ -2084,6 +2696,14 @@ This cannot be undone! เปิดใช้งานรหัสผ่านแบบทําลายตัวเอง set passcode view + + Enabled + No comment provided by engineer. + + + Enabled for + No comment provided by engineer. + Encrypt Encrypt @@ -2149,6 +2769,10 @@ This cannot be undone! Encryption re-negotiation failed. No comment provided by engineer. + + Encryption renegotiation in progress. + No comment provided by engineer. + Enter Passcode ใส่รหัสผ่าน @@ -2210,30 +2834,33 @@ This cannot be undone! เกิดข้อผิดพลาดในการยกเลิกการเปลี่ยนที่อยู่ No comment provided by engineer. + + Error accepting conditions + alert title + Error accepting contact request เกิดข้อผิดพลาดในการรับคำขอติดต่อ No comment provided by engineer. - - Error accessing database file - เกิดข้อผิดพลาดในการเข้าถึงไฟล์ฐานข้อมูล - No comment provided by engineer. - Error adding member(s) เกิดข้อผิดพลาดในการเพิ่มสมาชิก No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + alert title Error changing address เกิดข้อผิดพลาดในการเปลี่ยนที่อยู่ No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role เกิดข้อผิดพลาดในการเปลี่ยนบทบาท @@ -2244,6 +2871,18 @@ This cannot be undone! เกิดข้อผิดพลาดในการเปลี่ยนการตั้งค่า No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + + + Error checking token status + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address เกิดข้อผิดพลาดในการสร้างที่อยู่ @@ -2259,6 +2898,10 @@ This cannot be undone! เกิดข้อผิดพลาดในการสร้างลิงก์กลุ่ม No comment provided by engineer. + + Error creating list + alert title + Error creating member contact No comment provided by engineer. @@ -2272,6 +2915,10 @@ This cannot be undone! เกิดข้อผิดพลาดในการสร้างโปรไฟล์! No comment provided by engineer. + + Error creating report + No comment provided by engineer. + Error decrypting file No comment provided by engineer. @@ -2291,11 +2938,6 @@ This cannot be undone! เกิดข้อผิดพลาดในการลบการเชื่อมต่อ No comment provided by engineer. - - Error deleting contact - เกิดข้อผิดพลาดในการลบผู้ติดต่อ - No comment provided by engineer. - Error deleting database เกิดข้อผิดพลาดในการลบฐานข้อมูล @@ -2340,6 +2982,10 @@ This cannot be undone! เกิดข้อผิดพลาดในการส่งออกฐานข้อมูลแชท No comment provided by engineer. + + Error exporting theme: %@ + No comment provided by engineer. + Error importing chat database เกิดข้อผิดพลาดในการนำเข้าฐานข้อมูลแชท @@ -2350,9 +2996,12 @@ This cannot be undone! เกิดข้อผิดพลาดในการเข้าร่วมกลุ่ม No comment provided by engineer. - - Error loading %@ servers - โหลดเซิร์ฟเวอร์ %@ ผิดพลาด + + Error loading servers + alert title + + + Error migrating settings No comment provided by engineer. @@ -2362,16 +3011,31 @@ This cannot be undone! Error receiving file เกิดข้อผิดพลาดในการรับไฟล์ + alert title + + + Error reconnecting server No comment provided by engineer. + + Error reconnecting servers + No comment provided by engineer. + + + Error registering for notifications + alert title + Error removing member เกิดข้อผิดพลาดในการลบสมาชิก No comment provided by engineer. - - Error saving %@ servers - เกิดข้อผิดพลาดในการบันทึกเซิร์ฟเวอร์ %@ + + Error reordering lists + alert title + + + Error resetting statistics No comment provided by engineer. @@ -2379,6 +3043,10 @@ This cannot be undone! เกิดข้อผิดพลาดในการบันทึกเซิร์ฟเวอร์ ICE No comment provided by engineer. + + Error saving chat list + alert title + Error saving group profile เกิดข้อผิดพลาดในการบันทึกโปรไฟล์กลุ่ม @@ -2394,6 +3062,10 @@ This cannot be undone! เกิดข้อผิดพลาดในการบันทึกรหัสผ่านไปยัง keychain No comment provided by engineer. + + Error saving servers + alert title + Error saving settings when migrating @@ -2436,16 +3108,24 @@ This cannot be undone! เกิดข้อผิดพลาดในการหยุดแชท No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! เกิดข้อผิดพลาดในการเปลี่ยนโปรไฟล์! - No comment provided by engineer. + alertTitle Error synchronizing connection เกิดข้อผิดพลาดในการซิงโครไนซ์การเชื่อมต่อ No comment provided by engineer. + + Error testing server connection + No comment provided by engineer. + Error updating group link เกิดข้อผิดพลาดในการอัปเดตลิงก์กลุ่ม @@ -2456,6 +3136,10 @@ This cannot be undone! เกิดข้อผิดพลาดในการอัปเดตข้อความ No comment provided by engineer. + + Error updating server + alert title + Error updating settings เกิดข้อผิดพลาดในการอัปเดตการตั้งค่า @@ -2482,7 +3166,9 @@ This cannot be undone! Error: %@ ข้อผิดพลาด: % @ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2494,6 +3180,14 @@ This cannot be undone! เกิดข้อผิดพลาด: ไม่มีแฟ้มฐานข้อมูล No comment provided by engineer. + + Errors + No comment provided by engineer. + + + Errors in servers configuration. + servers error + Even when disabled in the conversation. แม้ในขณะที่ปิดใช้งานในการสนทนา @@ -2508,6 +3202,10 @@ This cannot be undone! Expand chat item action + + Expired + token status text + Export database ส่งออกฐานข้อมูล @@ -2518,6 +3216,10 @@ This cannot be undone! ข้อผิดพลาดในการส่งออก: No comment provided by engineer. + + Export theme + No comment provided by engineer. + Exported database archive. ที่เก็บถาวรฐานข้อมูลที่ส่งออก @@ -2542,15 +3244,57 @@ This cannot be undone! รวดเร็วและไม่ต้องรอจนกว่าผู้ส่งจะออนไลน์! No comment provided by engineer. + + Faster deletion of groups. + No comment provided by engineer. + Faster joining and more reliable messages. No comment provided by engineer. + + Faster sending messages. + No comment provided by engineer. + Favorite ที่ชอบ + swipe action + + + Favorites No comment provided by engineer. + + File error + file error alert title + + + File errors: +%@ + alert message + + + File is blocked by server operator: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + file error text + + + File server error: %@ + file error text + + + File status + No comment provided by engineer. + + + File status: %@ + copied message info + File will be deleted from servers. ไฟล์จะถูกลบออกจากเซิร์ฟเวอร์ @@ -2571,6 +3315,10 @@ This cannot be undone! ไฟล์: % @ No comment provided by engineer. + + Files + No comment provided by engineer. + Files & media ไฟล์และสื่อ @@ -2581,11 +3329,15 @@ This cannot be undone! ไฟล์และสื่อ chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. ไฟล์และสื่อเป็นสิ่งต้องห้ามในกลุ่มนี้ No comment provided by engineer. + + Files and media not allowed + No comment provided by engineer. + Files and media prohibited! ไฟล์และสื่อต้องห้าม! @@ -2644,11 +3396,93 @@ This cannot be undone! การแก้ไขไม่สนับสนุนโดยสมาชิกกลุ่ม No comment provided by engineer. + + For all moderators + No comment provided by engineer. + + + For chat profile %@: + servers error + For console สำหรับคอนโซล No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + No comment provided by engineer. + + + For me + No comment provided by engineer. + + + For private routing + No comment provided by engineer. + + + For social media + No comment provided by engineer. + + + Forward + chat item action + + + Forward %d message(s)? + alert title + + + Forward and save messages + No comment provided by engineer. + + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + + + Forwarded + No comment provided by engineer. + + + Forwarded from + No comment provided by engineer. + + + Forwarding %lld messages + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + snd error text + Found desktop No comment provided by engineer. @@ -2668,11 +3502,6 @@ This cannot be undone! ชื่อเต็ม (ไม่บังคับ) No comment provided by engineer. - - Full name: - ชื่อเต็ม: - No comment provided by engineer. - Fully decentralized – visible only to members. No comment provided by engineer. @@ -2692,6 +3521,18 @@ This cannot be undone! GIFs และสติกเกอร์ No comment provided by engineer. + + Get notified when mentioned. + No comment provided by engineer. + + + Good afternoon! + message preview + + + Good morning! + message preview + Group กลุ่ม @@ -2745,36 +3586,6 @@ This cannot be undone! ลิงค์กลุ่ม No comment provided by engineer. - - Group members can add message reactions. - สมาชิกกลุ่มสามารถเพิ่มการแสดงปฏิกิริยาต่อข้อความได้ - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - สมาชิกกลุ่มสามารถลบข้อความที่ส่งแล้วอย่างถาวร - No comment provided by engineer. - - - Group members can send direct messages. - สมาชิกกลุ่มสามารถส่งข้อความโดยตรงได้ - No comment provided by engineer. - - - Group members can send disappearing messages. - สมาชิกกลุ่มสามารถส่งข้อความที่จะหายไปหลังจากเวลาที่กำหนดหลังการอ่าน (disappearing messages) ได้ - No comment provided by engineer. - - - Group members can send files and media. - สมาชิกกลุ่มสามารถส่งไฟล์และสื่อ - No comment provided by engineer. - - - Group members can send voice messages. - สมาชิกกลุ่มสามารถส่งข้อความเสียง - No comment provided by engineer. - Group message: ข้อความกลุ่ม: @@ -2815,11 +3626,19 @@ This cannot be undone! กลุ่มจะถูกลบสำหรับคุณ - ไม่สามารถยกเลิกได้! No comment provided by engineer. + + Groups + No comment provided by engineer. + Help ความช่วยเหลือ No comment provided by engineer. + + Help admins moderating their groups. + No comment provided by engineer. + Hidden ซ่อนอยู่ @@ -2869,10 +3688,17 @@ This cannot be undone! วิธีการ SimpleX ทํางานอย่างไร No comment provided by engineer. + + How it affects privacy + No comment provided by engineer. + + + How it helps privacy + No comment provided by engineer. + How it works - มันทำงานอย่างไร - No comment provided by engineer. + alert button How to @@ -2898,6 +3724,10 @@ This cannot be undone! เซิร์ฟเวอร์ ICE (หนึ่งเครื่องต่อสาย) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. หากคุณไม่สามารถพบกันในชีวิตจริงได้ ให้แสดงคิวอาร์โค้ดในวิดีโอคอล หรือแชร์ลิงก์ @@ -2938,8 +3768,8 @@ This cannot be undone! โดยทันที No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam มีภูมิคุ้มกันต่อสแปมและการละเมิด No comment provided by engineer. @@ -2962,10 +3792,19 @@ This cannot be undone! Import failed No comment provided by engineer. + + Import theme + No comment provided by engineer. + Importing archive No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery No comment provided by engineer. @@ -2989,6 +3828,18 @@ This cannot be undone! ในการตอบกลับถึง No comment provided by engineer. + + In-call sounds + No comment provided by engineer. + + + Inappropriate content + report reason + + + Inappropriate profile + report reason + Incognito ไม่ระบุตัวตน @@ -3056,6 +3907,11 @@ This cannot be undone! ติดตั้ง [SimpleX Chat สำหรับเทอร์มินัล](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + ทันที + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3063,16 +3919,35 @@ This cannot be undone! No comment provided by engineer. - - Instantly - ทันที - No comment provided by engineer. - Interface อินเตอร์เฟซ No comment provided by engineer. + + Interface colors + No comment provided by engineer. + + + Invalid + token status text + + + Invalid (bad token) + token status text + + + Invalid (expired) + token status text + + + Invalid (unregistered) + token status text + + + Invalid (wrong topic) + token status text + Invalid QR code No comment provided by engineer. @@ -3105,7 +3980,7 @@ This cannot be undone! Invalid server address! ที่อยู่เซิร์ฟเวอร์ไม่ถูกต้อง! - No comment provided by engineer. + alert title Invalid status @@ -3126,6 +4001,10 @@ This cannot be undone! เชิญสมาชิก No comment provided by engineer. + + Invite to chat + No comment provided by engineer. + Invite to group เชิญเข้าร่วมกลุ่ม @@ -3141,8 +4020,8 @@ This cannot be undone! ไม่สามารถลบข้อความแบบแก้ไขไม่ได้ในแชทนี้ No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. การลบข้อความแบบแก้ไขไม่ได้เป็นสิ่งที่ห้ามในกลุ่มนี้ No comment provided by engineer. @@ -3167,6 +4046,10 @@ This cannot be undone! 3. การเชื่อมต่อถูกบุกรุก No comment provided by engineer. + + It protects your IP address and connections. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). ดูเหมือนว่าคุณได้เชื่อมต่อผ่านลิงก์นี้แล้ว หากไม่เป็นเช่นนั้น แสดงว่ามีข้อผิดพลาด (%@). @@ -3185,7 +4068,7 @@ This cannot be undone! Join เข้าร่วม - No comment provided by engineer. + swipe action Join group @@ -3221,6 +4104,10 @@ This is your link for group %@! Keep + alert action + + + Keep conversation No comment provided by engineer. @@ -3229,7 +4116,7 @@ This is your link for group %@! Keep unused invitation? - No comment provided by engineer. + alert title Keep your connections @@ -3264,6 +4151,14 @@ This is your link for group %@! Leave ออกจาก + swipe action + + + Leave chat + No comment provided by engineer. + + + Leave chat? No comment provided by engineer. @@ -3303,6 +4198,18 @@ This is your link for group %@! Linked desktops No comment provided by engineer. + + List + swipe action + + + List name and emoji should be different for all lists. + No comment provided by engineer. + + + List name... + No comment provided by engineer. + Live message! ข้อความสด! @@ -3313,11 +4220,6 @@ This is your link for group %@! ข้อความสด No comment provided by engineer. - - Local - ในเครื่อง - No comment provided by engineer. - Local name ชื่อภายในเครื่องเท่านั้น @@ -3338,11 +4240,6 @@ This is your link for group %@! โหมดล็อค No comment provided by engineer. - - Make a private connection - สร้างการเชื่อมต่อแบบส่วนตัว - No comment provided by engineer. - Make one message disappear ทำให้ข้อความหายไปหนึ่งข้อความ @@ -3353,21 +4250,11 @@ This is your link for group %@! ทำให้โปรไฟล์เป็นส่วนตัว! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - ตรวจสอบให้แน่ใจว่าที่อยู่เซิร์ฟเวอร์ %@ อยู่ในรูปแบบที่ถูกต้อง แยกบรรทัดและไม่ซ้ำกัน (%@) - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. ตรวจสอบให้แน่ใจว่าที่อยู่เซิร์ฟเวอร์ WebRTC ICE อยู่ในรูปแบบที่ถูกต้อง แยกบรรทัดและไม่ซ้ำกัน No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - หลายคนถามว่า: *หาก SimpleX ไม่มีตัวระบุผู้ใช้ จะส่งข้อความได้อย่างไร?* - No comment provided by engineer. - Mark deleted for everyone ทำเครื่องหมายว่าลบแล้วสำหรับทุกคน @@ -3393,11 +4280,31 @@ This is your link for group %@! สูงสุด 30 วินาที รับทันที No comment provided by engineer. + + Media & file servers + No comment provided by engineer. + + + Medium + blur media + Member สมาชิก No comment provided by engineer. + + Member inactive + item status text + + + Member reports + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. บทบาทของสมาชิกจะถูกเปลี่ยนเป็น "%@" สมาชิกกลุ่มทั้งหมดจะได้รับแจ้ง @@ -3408,11 +4315,61 @@ This is your link for group %@! บทบาทของสมาชิกจะถูกเปลี่ยนเป็น "%@" สมาชิกจะได้รับคำเชิญใหม่ No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! สมาชิกจะถูกลบออกจากกลุ่ม - ไม่สามารถยกเลิกได้! No comment provided by engineer. + + Members can add message reactions. + สมาชิกกลุ่มสามารถเพิ่มการแสดงปฏิกิริยาต่อข้อความได้ + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + สมาชิกกลุ่มสามารถลบข้อความที่ส่งแล้วอย่างถาวร + No comment provided by engineer. + + + Members can report messsages to moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + No comment provided by engineer. + + + Members can send direct messages. + สมาชิกกลุ่มสามารถส่งข้อความโดยตรงได้ + No comment provided by engineer. + + + Members can send disappearing messages. + สมาชิกกลุ่มสามารถส่งข้อความที่จะหายไปหลังจากเวลาที่กำหนดหลังการอ่าน (disappearing messages) ได้ + No comment provided by engineer. + + + Members can send files and media. + สมาชิกกลุ่มสามารถส่งไฟล์และสื่อ + No comment provided by engineer. + + + Members can send voice messages. + สมาชิกกลุ่มสามารถส่งข้อความเสียง + No comment provided by engineer. + + + Mention members 👋 + No comment provided by engineer. + + + Menus + No comment provided by engineer. + Message delivery error ข้อผิดพลาดในการส่งข้อความ @@ -3423,11 +4380,27 @@ This is your link for group %@! ใบเสร็จการส่งข้อความ! No comment provided by engineer. + + Message delivery warning + item status text + Message draft ร่างข้อความ No comment provided by engineer. + + Message forwarded + item status text + + + Message may be delivered later if member becomes active. + item status description + + + Message queue info + No comment provided by engineer. + Message reactions ปฏิกิริยาของข้อความ @@ -3438,11 +4411,35 @@ This is your link for group %@! ห้ามแสดงปฏิกิริยาบนข้อความในแชทนี้ No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. ปฏิกิริยาบนข้อความเป็นสิ่งต้องห้ามในกลุ่มนี้ No comment provided by engineer. + + Message reception + No comment provided by engineer. + + + Message servers + No comment provided by engineer. + + + Message shape + No comment provided by engineer. + + + Message source remains private. + No comment provided by engineer. + + + Message status + No comment provided by engineer. + + + Message status: %@ + copied message info + Message text ข้อความ @@ -3466,6 +4463,22 @@ This is your link for group %@! Messages from %@ will be shown! No comment provided by engineer. + + Messages in this chat will never be deleted. + alert message + + + Messages received + No comment provided by engineer. + + + Messages sent + No comment provided by engineer. + + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. No comment provided by engineer. @@ -3522,9 +4535,9 @@ This is your link for group %@! การโยกย้ายเสร็จสมบูรณ์ No comment provided by engineer. - - Migrations: %@ - การย้ายข้อมูล: %@ + + Migrations: + การย้ายข้อมูล No comment provided by engineer. @@ -3542,20 +4555,27 @@ This is your link for group %@! กลั่นกรองที่: %@ copied message info + + More + swipe action + More improvements are coming soon! การปรับปรุงเพิ่มเติมกำลังจะมาเร็ว ๆ นี้! No comment provided by engineer. + + More reliable network connection. + No comment provided by engineer. + + + More reliable notifications + No comment provided by engineer. + Most likely this connection is deleted. item status description - - Most likely this contact has deleted the connection with you. - เป็นไปได้มากว่าผู้ติดต่อนี้ได้ลบการเชื่อมต่อกับคุณ - No comment provided by engineer. - Multiple chat profiles โปรไฟล์การแชทหลายรายการ @@ -3564,7 +4584,11 @@ This is your link for group %@! Mute ปิดเสียง - No comment provided by engineer. + notification label action + + + Mute all + notification label action Muted when inactive! @@ -3574,13 +4598,33 @@ This is your link for group %@! Name ชื่อ - No comment provided by engineer. + swipe action Network & servers เครือข่ายและเซิร์ฟเวอร์ No comment provided by engineer. + + Network connection + No comment provided by engineer. + + + Network decentralization + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + snd error text + + + Network management + No comment provided by engineer. + + + Network operator + No comment provided by engineer. + Network settings การตั้งค่าเครือข่าย @@ -3591,15 +4635,31 @@ This is your link for group %@! สถานะเครือข่าย No comment provided by engineer. + + New + token status text + New Passcode รหัสผ่านใหม่ No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat No comment provided by engineer. + + New chat experience 🎉 + No comment provided by engineer. + New contact request คำขอติดต่อใหม่ @@ -3610,11 +4670,6 @@ This is your link for group %@! คำขอติดต่อใหม่: notification - - New database archive - ฐานข้อมูลใหม่สำหรับการเก็บถาวร - No comment provided by engineer. - New desktop app! No comment provided by engineer. @@ -3624,11 +4679,19 @@ This is your link for group %@! ชื่อที่แสดงใหม่ No comment provided by engineer. + + New events + notification + New in %@ ใหม่ใน %@ No comment provided by engineer. + + New media options + No comment provided by engineer. + New member role บทบาทของสมาชิกใหม่ @@ -3644,6 +4707,10 @@ This is your link for group %@! รหัสผ่านใหม่… No comment provided by engineer. + + New server + No comment provided by engineer. + No เลขที่ @@ -3654,6 +4721,18 @@ This is your link for group %@! ไม่มีรหัสผ่านสำหรับแอป Authentication unavailable + + No chats + No comment provided by engineer. + + + No chats found + No comment provided by engineer. + + + No chats in list %@ + No comment provided by engineer. + No contacts selected ไม่ได้เลือกผู้ติดต่อ @@ -3673,6 +4752,10 @@ This is your link for group %@! ไม่มีโทเค็นอุปกรณ์! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + item status description + No filtered chats ไม่มีการกรองการแชท @@ -3688,20 +4771,94 @@ This is your link for group %@! ไม่มีประวัติ No comment provided by engineer. + + No info, try to reload + No comment provided by engineer. + + + No media & file servers. + servers error + + + No message + No comment provided by engineer. + + + No message servers. + servers error + + + No network connection + No comment provided by engineer. + + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message ไม่อนุญาตให้บันทึกข้อความเสียง No comment provided by engineer. + + No push server + ในเครื่อง + No comment provided by engineer. + No received or sent files ไม่มีไฟล์ที่ได้รับหรือส่ง No comment provided by engineer. + + No servers for private message routing. + servers error + + + No servers to receive files. + servers error + + + No servers to receive messages. + servers error + + + No servers to send files. + servers error + + + No token! + alert title + + + No unread chats + No comment provided by engineer. + + + No user identifiers. + แพลตฟอร์มแรกที่ไม่มีตัวระบุผู้ใช้ - ถูกออกแบบให้เป็นส่วนตัว + No comment provided by engineer. + Not compatible! No comment provided by engineer. + + Notes + No comment provided by engineer. + + + Nothing selected + No comment provided by engineer. + + + Nothing to forward! + alert title + Notifications การแจ้งเตือน @@ -3712,6 +4869,18 @@ This is your link for group %@! ปิดการแจ้งเตือน! No comment provided by engineer. + + Notifications error + alert title + + + Notifications privacy + No comment provided by engineer. + + + Notifications status + alert title + Now admins can: - delete members' messages. @@ -3728,35 +4897,32 @@ This is your link for group %@! Off ปิด - No comment provided by engineer. + blur media Ok ตกลง - No comment provided by engineer. + alert button Old database ฐานข้อมูลเก่า No comment provided by engineer. - - Old database archive - คลังฐานข้อมูลเก่า - No comment provided by engineer. - One-time invitation link ลิงก์คำเชิญแบบใช้ครั้งเดียว No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. + + Onion hosts will be **required** for connection. +Requires compatible VPN. จำเป็นต้องมีโฮสต์หัวหอมสำหรับการเชื่อมต่อ ต้องเปิดใช้งาน VPN สำหรับการเชื่อมต่อ No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. + + Onion hosts will be used when available. +Requires compatible VPN. จำเป็นต้องมีโฮสต์หัวหอมสำหรับการเชื่อมต่อ ต้องเปิดใช้งาน VPN สำหรับการเชื่อมต่อ No comment provided by engineer. @@ -3765,11 +4931,19 @@ This is your link for group %@! โฮสต์หัวหอมจะไม่ถูกใช้ No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. เฉพาะอุปกรณ์ไคลเอนต์เท่านั้นที่จัดเก็บโปรไฟล์ผู้ใช้ ผู้ติดต่อ กลุ่ม และข้อความที่ส่งด้วย **การเข้ารหัส encrypt แบบ 2 ชั้น** No comment provided by engineer. + + Only delete conversation + No comment provided by engineer. + Only group owners can change group preferences. เฉพาะเจ้าของกลุ่มเท่านั้นที่สามารถเปลี่ยนค่ากําหนดลักษณะกลุ่มได้ @@ -3785,6 +4959,14 @@ This is your link for group %@! เฉพาะเจ้าของกลุ่มเท่านั้นที่สามารถเปิดใช้งานข้อความเสียงได้ No comment provided by engineer. + + Only sender and moderators see it + No comment provided by engineer. + + + Only you and moderators see it + No comment provided by engineer. + Only you can add message reactions. มีเพียงคุณเท่านั้นที่สามารถแสดงปฏิกิริยาต่อข้อความได้ @@ -3837,13 +5019,17 @@ This is your link for group %@! Open - No comment provided by engineer. + alert action Open Settings เปิดการตั้งค่า No comment provided by engineer. + + Open changes + No comment provided by engineer. + Open chat เปิดแชท @@ -3854,28 +5040,38 @@ This is your link for group %@! เปิดคอนโซลการแชท authentication reason + + Open conditions + No comment provided by engineer. + Open group No comment provided by engineer. + + Open link? + alert title + Open migration to another device authentication reason - - Open user profiles - เปิดโปรไฟล์ผู้ใช้ - authentication reason - - - Open-source protocol and code – anybody can run the servers. - โปรโตคอลและโค้ดโอเพ่นซอร์ส – ใคร ๆ ก็สามารถเปิดใช้เซิร์ฟเวอร์ได้ - No comment provided by engineer. - Opening app… No comment provided by engineer. + + Operator + No comment provided by engineer. + + + Operator server + alert title + + + Or import archive file + No comment provided by engineer. + Or paste archive link No comment provided by engineer. @@ -3892,6 +5088,23 @@ This is your link for group %@! Or show this code No comment provided by engineer. + + Or to share privately + No comment provided by engineer. + + + Organize chats into lists + No comment provided by engineer. + + + Other + No comment provided by engineer. + + + Other file errors: +%@ + alert message + PING count จํานวน PING @@ -3927,6 +5140,10 @@ This is your link for group %@! ตั้งรหัสผ่านเรียบร้อยแล้ว! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show รหัสผ่านที่จะแสดง @@ -3953,13 +5170,12 @@ This is your link for group %@! Paste the link you received No comment provided by engineer. - - People can connect to you only via the links you share. - ผู้คนสามารถเชื่อมต่อกับคุณผ่านลิงก์ที่คุณแบ่งปันเท่านั้น + + Pending No comment provided by engineer. - - Periodically + + Periodic เป็นระยะๆ No comment provided by engineer. @@ -3972,11 +5188,24 @@ This is your link for group %@! Picture-in-picture calls No comment provided by engineer. + + Play from the chat list. + No comment provided by engineer. + + + Please ask your contact to enable calls. + No comment provided by engineer. + Please ask your contact to enable sending voice messages. โปรดขอให้ผู้ติดต่อของคุณเปิดใช้งานการส่งข้อความเสียง No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. โปรดตรวจสอบว่าคุณใช้ลิงก์ที่ถูกต้องหรือขอให้ผู้ติดต่อของคุณส่งลิงก์ใหม่ให้คุณ @@ -4041,59 +5270,106 @@ Error: %@ โปรดจัดเก็บรหัสผ่านอย่างปลอดภัย คุณจะไม่สามารถเปลี่ยนรหัสผ่านได้หากคุณทำรหัสผ่านหาย No comment provided by engineer. + + Please try to disable and re-enable notfications. + token info + + + Please wait for token activation to complete. + token info + + + Please wait for token to be registered. + token info + Polish interface อินเตอร์เฟซภาษาโปแลนด์ No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect อาจเป็นไปได้ว่าลายนิ้วมือของ certificate ในที่อยู่เซิร์ฟเวอร์ไม่ถูกต้อง server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. เก็บข้อความที่ร่างไว้ล่าสุดพร้อมไฟล์แนบ No comment provided by engineer. - - Preset server - เซิร์ฟเวอร์ที่ตั้งไว้ล่วงหน้า - No comment provided by engineer. - Preset server address ที่อยู่เซิร์ฟเวอร์ที่ตั้งไว้ล่วงหน้า No comment provided by engineer. + + Preset servers + No comment provided by engineer. + Preview ดูตัวอย่าง No comment provided by engineer. + + Previously connected servers + No comment provided by engineer. + Privacy & security ความเป็นส่วนตัวและความปลอดภัย No comment provided by engineer. + + Privacy for your customers. + No comment provided by engineer. + + + Privacy policy and conditions of use. + No comment provided by engineer. + Privacy redefined นิยามความเป็นส่วนตัวใหม่ No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + No comment provided by engineer. + Private filenames ชื่อไฟล์ส่วนตัว No comment provided by engineer. + + Private media file names. + No comment provided by engineer. + + + Private message routing + No comment provided by engineer. + + + Private message routing 🚀 + No comment provided by engineer. + Private notes name of notes to self + + Private routing + No comment provided by engineer. + + + Private routing error + No comment provided by engineer. + Profile and server connections การเชื่อมต่อโปรไฟล์และเซิร์ฟเวอร์ @@ -4104,12 +5380,8 @@ Error: %@ รูปโปรไฟล์ No comment provided by engineer. - - Profile name - No comment provided by engineer. - - - Profile name: + + Profile images No comment provided by engineer. @@ -4117,10 +5389,14 @@ Error: %@ รหัสผ่านโปรไฟล์ No comment provided by engineer. + + Profile theme + No comment provided by engineer. + Profile update will be sent to your contacts. การอัปเดตโปรไฟล์จะถูกส่งไปยังผู้ติดต่อของคุณ - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4142,6 +5418,14 @@ Error: %@ ห้ามแสดงปฏิกิริยาต่อข้อความ No comment provided by engineer. + + Prohibit reporting messages to moderators. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + No comment provided by engineer. + Prohibit sending direct messages to members. ห้ามส่งข้อความโดยตรงถึงสมาชิก @@ -4162,11 +5446,20 @@ Error: %@ ห้ามส่งข้อความเสียง No comment provided by engineer. + + Protect IP address + No comment provided by engineer. + Protect app screen ปกป้องหน้าจอแอป No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + No comment provided by engineer. + Protect your chat profiles with a password! ปกป้องโปรไฟล์การแชทของคุณด้วยรหัสผ่าน! @@ -4182,6 +5475,18 @@ Error: %@ การหมดเวลาของโปรโตคอลต่อ KB No comment provided by engineer. + + Proxied + No comment provided by engineer. + + + Proxied servers + No comment provided by engineer. + + + Proxy requires password + No comment provided by engineer. + Push notifications การแจ้งเตือนแบบทันที @@ -4200,6 +5505,10 @@ Error: %@ ให้คะแนนแอป No comment provided by engineer. + + Reachable chat toolbar + No comment provided by engineer. + React… ตอบสนอง… @@ -4208,32 +5517,27 @@ Error: %@ Read อ่าน - No comment provided by engineer. + swipe action Read more อ่านเพิ่มเติม No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - อ่านเพิ่มเติมใน[คู่มือผู้ใช้](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address) - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + อ่านเพิ่มเติมใน[คู่มือผู้ใช้](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses) + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). อ่านเพิ่มเติมใน[คู่มือผู้ใช้](https://simplex.chat/docs/guide/readme.html#connect-to-friends) No comment provided by engineer. - - Read more in our GitHub repository. - อ่านเพิ่มเติมในที่เก็บ GitHub ของเรา - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). อ่านเพิ่มเติมใน[พื้นที่เก็บข้อมูล GitHub](https://github.com/simplex-chat/simplex-chat#readme) @@ -4243,6 +5547,10 @@ Error: %@ Receipts are disabled No comment provided by engineer. + + Receive errors + No comment provided by engineer. + Received at ได้รับเมื่อ @@ -4263,6 +5571,18 @@ Error: %@ ได้รับข้อความ message info title + + Received messages + No comment provided by engineer. + + + Received reply + No comment provided by engineer. + + + Received total + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. ที่อยู่ผู้รับจะถูกเปลี่ยนเป็นเซิร์ฟเวอร์อื่น การเปลี่ยนแปลงที่อยู่จะเสร็จสมบูรณ์หลังจากที่ผู้ส่งออนไลน์ @@ -4282,16 +5602,40 @@ Error: %@ Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + No comment provided by engineer. + Recipients see updates as you type them. ผู้รับจะเห็นการอัปเดตเมื่อคุณพิมพ์ No comment provided by engineer. + + Reconnect + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. เชื่อมต่อเซิร์ฟเวอร์ที่เชื่อมต่อทั้งหมดอีกครั้งเพื่อบังคับให้ส่งข้อความ มันใช้การจราจรเพิ่มเติม No comment provided by engineer. + + Reconnect all servers + No comment provided by engineer. + + + Reconnect all servers? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + No comment provided by engineer. + + + Reconnect server? + No comment provided by engineer. + Reconnect servers? เชื่อมต่อเซิร์ฟเวอร์อีกครั้งหรือไม่? @@ -4312,10 +5656,23 @@ Error: %@ ลดการใช้แบตเตอรี่ No comment provided by engineer. + + Register + No comment provided by engineer. + + + Register notification token? + token info + + + Registered + token status text + Reject ปฏิเสธ - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4341,6 +5698,14 @@ Error: %@ ลบ No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + + + Remove image + No comment provided by engineer. + Remove member ลบสมาชิกออก @@ -4396,6 +5761,46 @@ Error: %@ ตอบ chat item action + + Report + chat item action + + + Report content: only group moderators will see it. + report reason + + + Report member profile: only group moderators will see it. + report reason + + + Report other: only group moderators will see it. + report reason + + + Report reason? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + report reason + + + Report violation: only group moderators will see it. + report reason + + + Report: %@ + report in notification + + + Reporting messages to moderators is prohibited. + No comment provided by engineer. + + + Reports + No comment provided by engineer. + Required ที่จำเป็น @@ -4406,16 +5811,36 @@ Error: %@ รีเซ็ต No comment provided by engineer. + + Reset all hints + No comment provided by engineer. + + + Reset all statistics + No comment provided by engineer. + + + Reset all statistics? + No comment provided by engineer. + Reset colors รีเซ็ตสี No comment provided by engineer. + + Reset to app theme + No comment provided by engineer. + Reset to defaults รีเซ็ตเป็นค่าเริ่มต้น No comment provided by engineer. + + Reset to user theme + No comment provided by engineer. + Restart the app to create a new chat profile รีสตาร์ทแอปเพื่อสร้างโปรไฟล์แชทใหม่ @@ -4455,9 +5880,8 @@ Error: %@ เปิดเผย chat item action - - Revert - เปลี่ยนกลับ + + Review conditions No comment provided by engineer. @@ -4485,9 +5909,16 @@ Error: %@ เรียกใช้แชท No comment provided by engineer. - - SMP servers - เซิร์ฟเวอร์ SMP + + SMP server + No comment provided by engineer. + + + SOCKS proxy + No comment provided by engineer. + + + Safely receive files No comment provided by engineer. @@ -4497,43 +5928,42 @@ Error: %@ Save บันทึก - chat item action + alert button +chat item action Save (and notify contacts) บันทึก (และแจ้งผู้ติดต่อ) - No comment provided by engineer. + alert button Save and notify contact บันทึกและแจ้งผู้ติดต่อ - No comment provided by engineer. + alert button Save and notify group members บันทึกและแจ้งให้สมาชิกในกลุ่มทราบ No comment provided by engineer. + + Save and reconnect + No comment provided by engineer. + Save and update group profile บันทึกและอัปเดตโปรไฟล์กลุ่ม No comment provided by engineer. - - Save archive - บันทึกไฟล์เก็บถาวร - No comment provided by engineer. - - - Save auto-accept settings - บันทึกการตั้งค่าการยอมรับอัตโนมัติ - No comment provided by engineer. - Save group profile บันทึกโปรไฟล์กลุ่ม No comment provided by engineer. + + Save list + No comment provided by engineer. + Save passphrase and open chat บันทึกรหัสผ่านและเปิดแชท @@ -4547,7 +5977,7 @@ Error: %@ Save preferences? บันทึกการตั้งค่า? - No comment provided by engineer. + alert title Save profile password @@ -4562,27 +5992,46 @@ Error: %@ Save servers? บันทึกเซิร์ฟเวอร์? - No comment provided by engineer. - - - Save settings? - บันทึกการตั้งค่า? - No comment provided by engineer. + alert title Save welcome message? บันทึกข้อความต้อนรับ? No comment provided by engineer. + + Save your profile? + alert title + + + Saved + No comment provided by engineer. + Saved WebRTC ICE servers will be removed เซิร์ฟเวอร์ WebRTC ICE ที่บันทึกไว้จะถูกลบออก No comment provided by engineer. + + Saved from + No comment provided by engineer. + Saved message message info title + + Saving %lld messages + No comment provided by engineer. + + + Scale + No comment provided by engineer. + + + Scan / Paste link + No comment provided by engineer. + Scan QR code สแกนคิวอาร์โค้ด @@ -4620,11 +6069,19 @@ Error: %@ Search or paste SimpleX link No comment provided by engineer. + + Secondary + No comment provided by engineer. + Secure queue คิวที่ปลอดภัย server test step + + Secured + No comment provided by engineer. + Security assessment การประเมินความปลอดภัย @@ -4638,6 +6095,18 @@ Error: %@ Select เลือก + chat item action + + + Select chat profile + No comment provided by engineer. + + + Selected %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. No comment provided by engineer. @@ -4675,11 +6144,6 @@ Error: %@ ส่งใบเสร็จรับการจัดส่งข้อความไปที่ No comment provided by engineer. - - Send direct message - ส่งข้อความโดยตรง - No comment provided by engineer. - Send direct message to connect No comment provided by engineer. @@ -4689,6 +6153,10 @@ Error: %@ ส่งข้อความแบบที่หายไป No comment provided by engineer. + + Send errors + No comment provided by engineer. + Send link previews ส่งตัวอย่างลิงก์ @@ -4699,14 +6167,25 @@ Error: %@ ส่งข้อความสด No comment provided by engineer. + + Send message to enable calls. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + No comment provided by engineer. + Send notifications ส่งการแจ้งเตือน No comment provided by engineer. - - Send notifications: - ส่งการแจ้งเตือน: + + Send private reports No comment provided by engineer. @@ -4731,7 +6210,7 @@ Error: %@ Sender cancelled file transfer. ผู้ส่งยกเลิกการโอนไฟล์ - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4786,6 +6265,10 @@ Error: %@ ส่งเมื่อ: %@ copied message info + + Sent directly + No comment provided by engineer. + Sent file event เหตุการณ์ไฟล์ที่ส่ง @@ -4796,11 +6279,59 @@ Error: %@ ข้อความที่ส่งแล้ว message info title + + Sent messages + No comment provided by engineer. + Sent messages will be deleted after set time. ข้อความที่ส่งจะถูกลบหลังเกินเวลาที่กําหนด No comment provided by engineer. + + Sent reply + No comment provided by engineer. + + + Sent total + No comment provided by engineer. + + + Sent via proxy + No comment provided by engineer. + + + Server + No comment provided by engineer. + + + Server added to operator %@. + alert message + + + Server address + No comment provided by engineer. + + + Server address is incompatible with network settings. + srv error text. + + + Server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Server operator changed. + alert title + + + Server operators + No comment provided by engineer. + + + Server protocol changed. + alert title + Server requires authorization to create queues, check password เซิร์ฟเวอร์ต้องการการอนุญาตในการสร้างคิว โปรดตรวจสอบรหัสผ่าน @@ -4816,11 +6347,31 @@ Error: %@ การทดสอบเซิร์ฟเวอร์ล้มเหลว! No comment provided by engineer. + + Server type + No comment provided by engineer. + + + Server version is incompatible with network settings. + srv error text + + + Server version is incompatible with your app: %@. + No comment provided by engineer. + Servers เซิร์ฟเวอร์ No comment provided by engineer. + + Servers info + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + No comment provided by engineer. + Session code No comment provided by engineer. @@ -4830,11 +6381,19 @@ Error: %@ ตั้ง 1 วัน No comment provided by engineer. + + Set chat name… + No comment provided by engineer. + Set contact name… ตั้งชื่อผู้ติดต่อ… No comment provided by engineer. + + Set default theme + No comment provided by engineer. + Set group preferences ตั้งค่าการกําหนดลักษณะกลุ่ม @@ -4845,6 +6404,10 @@ Error: %@ ตั้งแทนการรับรองความถูกต้องของระบบ No comment provided by engineer. + + Set message expiration in chats. + No comment provided by engineer. + Set passcode ตั้งรหัสผ่าน @@ -4874,24 +6437,49 @@ Error: %@ การตั้งค่า No comment provided by engineer. + + Settings were changed. + alert message + + + Shape profile images + No comment provided by engineer. + Share แชร์ - chat item action + alert action +chat item action Share 1-time link แชร์ลิงก์แบบใช้ครั้งเดียว No comment provided by engineer. + + Share 1-time link with a friend + No comment provided by engineer. + + + Share SimpleX address on social media. + No comment provided by engineer. + Share address แชร์ที่อยู่ No comment provided by engineer. + + Share address publicly + No comment provided by engineer. + Share address with contacts? แชร์ที่อยู่กับผู้ติดต่อ? + alert title + + + Share from other apps. No comment provided by engineer. @@ -4899,15 +6487,27 @@ Error: %@ แชร์ลิงก์ No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link No comment provided by engineer. + + Share to SimpleX + No comment provided by engineer. + Share with contacts แชร์กับผู้ติดต่อ No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code No comment provided by engineer. @@ -4926,21 +6526,41 @@ Error: %@ Show last messages No comment provided by engineer. + + Show message status + No comment provided by engineer. + + + Show percentage + No comment provided by engineer. + Show preview แสดงตัวอย่าง No comment provided by engineer. + + Show → on messages sent via private routing. + No comment provided by engineer. + Show: แสดง: No comment provided by engineer. + + SimpleX + No comment provided by engineer. + SimpleX Address ที่อยู่ SimpleX No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. ความปลอดภัยของ SimpleX Chat ได้รับการตรวจสอบโดย Trail of Bits @@ -4971,6 +6591,18 @@ Error: %@ ที่อยู่ SimpleX No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + No comment provided by engineer. + + + SimpleX address or 1-time link? + No comment provided by engineer. + + + SimpleX channel link + simplex link type + SimpleX contact address ที่อยู่ติดต่อ SimpleX @@ -4989,6 +6621,14 @@ Error: %@ SimpleX links ลิงก์ SimpleX + chat feature + + + SimpleX links are prohibited. + No comment provided by engineer. + + + SimpleX links not allowed No comment provided by engineer. @@ -4996,10 +6636,18 @@ Error: %@ คำเชิญ SimpleX แบบครั้งเดียว simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode No comment provided by engineer. + + Size + No comment provided by engineer. + Skip ข้าม @@ -5014,16 +6662,46 @@ Error: %@ Small groups (max 20) No comment provided by engineer. + + Soft + blur media + + + Some app settings were not migrated. + No comment provided by engineer. + + + Some file(s) were not exported: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. ข้อผิดพลาดที่ไม่ร้ายแรงบางอย่างเกิดขึ้นระหว่างการนำเข้า - คุณอาจดูรายละเอียดเพิ่มเติมได้ที่คอนโซล Chat No comment provided by engineer. + + Some non-fatal errors occurred during import: + No comment provided by engineer. + + + Some servers failed the test: +%@ + alert message + Somebody ใครบางคน notification title + + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + No comment provided by engineer. + Start chat เริ่มแชท @@ -5038,6 +6716,14 @@ Error: %@ เริ่มการย้ายข้อมูล No comment provided by engineer. + + Starting from %@. + No comment provided by engineer. + + + Statistics + No comment provided by engineer. + Stop หยุด @@ -5052,11 +6738,6 @@ Error: %@ Stop chat No comment provided by engineer. - - Stop chat to enable database actions - หยุดการแชทเพื่อเปิดใช้งานการดำเนินการกับฐานข้อมูล - No comment provided by engineer. - Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. หยุดแชทเพื่อส่งออก นำเข้า หรือลบฐานข้อมูลแชท คุณจะไม่สามารถรับและส่งข้อความได้ในขณะที่การแชทหยุดลง @@ -5085,27 +6766,55 @@ Error: %@ Stop sharing หยุดแชร์ - No comment provided by engineer. + alert action Stop sharing address? หยุดแชร์ที่อยู่ไหม? - No comment provided by engineer. + alert title Stopping chat No comment provided by engineer. + + Storage + No comment provided by engineer. + + + Strong + blur media + Submit ส่ง No comment provided by engineer. + + Subscribed + No comment provided by engineer. + + + Subscription errors + No comment provided by engineer. + + + Subscriptions ignored + No comment provided by engineer. + Support SimpleX Chat สนับสนุน SimpleX แชท No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System ระบบ @@ -5116,11 +6825,19 @@ Error: %@ การรับรองความถูกต้องของระบบ No comment provided by engineer. + + TCP connection + No comment provided by engineer. + TCP connection timeout หมดเวลาการเชื่อมต่อ TCP No comment provided by engineer. + + TCP port for messaging + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5136,11 +6853,19 @@ Error: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture ถ่ายภาพ No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + No comment provided by engineer. + Tap button แตะปุ่ม @@ -5173,16 +6898,19 @@ Error: %@ Tap to scan No comment provided by engineer. - - Tap to start a new chat - แตะเพื่อเริ่มแชทใหม่ - No comment provided by engineer. + + Temporary file error + file error alert title Test failed at step %@. การทดสอบล้มเหลวในขั้นตอน %@ server test failure + + Test notifications + No comment provided by engineer. + Test server เซิร์ฟเวอร์ทดสอบ @@ -5196,7 +6924,7 @@ Error: %@ Tests failed! การทดสอบล้มเหลว! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5213,11 +6941,6 @@ Error: %@ ขอบคุณผู้ใช้ – มีส่วนร่วมผ่าน Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - แพลตฟอร์มแรกที่ไม่มีตัวระบุผู้ใช้ - ถูกออกแบบให้เป็นส่วนตัว - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5231,6 +6954,14 @@ It can happen because of some bug or when the connection is compromised.แอปสามารถแจ้งให้คุณทราบเมื่อคุณได้รับข้อความหรือคำขอติดต่อ - โปรดเปิดการตั้งค่าเพื่อเปิดใช้งาน No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + No comment provided by engineer. + The attempt to change database passphrase was not completed. ความพยายามในการเปลี่ยนรหัสผ่านของฐานข้อมูลไม่เสร็จสมบูรณ์ @@ -5240,6 +6971,10 @@ It can happen because of some bug or when the connection is compromised.The code you scanned is not a SimpleX link QR code. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + No comment provided by engineer. + The connection you accepted will be cancelled! การเชื่อมต่อที่คุณยอมรับจะถูกยกเลิก! @@ -5260,6 +6995,11 @@ It can happen because of some bug or when the connection is compromised.encryption กำลังทำงานและไม่จำเป็นต้องใช้ข้อตกลง encryption ใหม่ อาจทำให้การเชื่อมต่อผิดพลาดได้! No comment provided by engineer. + + The future of messaging + การส่งข้อความส่วนตัวรุ่นต่อไป + No comment provided by engineer. + The hash of the previous message is different. แฮชของข้อความก่อนหน้านี้แตกต่างกัน @@ -5275,9 +7015,12 @@ It can happen because of some bug or when the connection is compromised.ข้อความจะถูกทำเครื่องหมายว่ากลั่นกรองสำหรับสมาชิกทุกคน No comment provided by engineer. - - The next generation of private messaging - การส่งข้อความส่วนตัวรุ่นต่อไป + + The messages will be deleted for all members. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. No comment provided by engineer. @@ -5285,9 +7028,12 @@ It can happen because of some bug or when the connection is compromised.ฐานข้อมูลเก่าไม่ได้ถูกลบในระหว่างการย้ายข้อมูล แต่สามารถลบได้ No comment provided by engineer. - - The profile is only shared with your contacts. - โปรไฟล์นี้แชร์กับผู้ติดต่อของคุณเท่านั้น + + The same conditions will apply to operator **%@**. + No comment provided by engineer. + + + The second preset operator in the app! No comment provided by engineer. @@ -5305,13 +7051,24 @@ It can happen because of some bug or when the connection is compromised.เซิร์ฟเวอร์สำหรับการเชื่อมต่อใหม่ของโปรไฟล์การแชทปัจจุบันของคุณ **%@** No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. No comment provided by engineer. - - Theme - ธีม + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + + + Themes + No comment provided by engineer. + + + These conditions will also apply for: **%@**. No comment provided by engineer. @@ -5333,6 +7090,10 @@ It can happen because of some bug or when the connection is compromised.การดำเนินการนี้ไม่สามารถเลิกทำได้ - ข้อความที่ส่งและรับก่อนหน้าที่เลือกไว้จะถูกลบ อาจใช้เวลาหลายนาที No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. การดำเนินการนี้ไม่สามารถยกเลิกได้ - โปรไฟล์ ผู้ติดต่อ ข้อความ และไฟล์ของคุณจะสูญหายไปอย่างถาวร @@ -5371,11 +7132,27 @@ It can happen because of some bug or when the connection is compromised.This is your own one-time link! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + No comment provided by engineer. + + + This message was deleted or not received yet. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. การตั้งค่านี้ใช้กับข้อความในโปรไฟล์แชทปัจจุบันของคุณ **%@** No comment provided by engineer. + + Title + No comment provided by engineer. + To ask any questions and to receive updates: หากต้องการถามคำถามและรับการอัปเดต: @@ -5395,9 +7172,8 @@ It can happen because of some bug or when the connection is compromised.เพื่อสร้างการเชื่อมต่อใหม่ No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - เพื่อปกป้องความเป็นส่วนตัว แทนที่จะใช้ ID ผู้ใช้เหมือนที่แพลตฟอร์มอื่นๆใช้ SimpleX มีตัวระบุสำหรับคิวข้อความ โดยแยกจากกันสำหรับผู้ติดต่อแต่ละราย + + To protect against your link being replaced, you can compare contact security codes. No comment provided by engineer. @@ -5405,6 +7181,10 @@ It can happen because of some bug or when the connection is compromised.ไฟล์ภาพ/เสียงใช้ UTC เพื่อป้องกันเขตเวลา No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5412,6 +7192,23 @@ You will be prompted to complete authentication before this feature is enabled.< คุณจะได้รับแจ้งให้ยืนยันตัวตนให้เสร็จสมบูรณ์ก่อนที่จะเปิดใช้งานคุณลักษณะนี้ No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + เพื่อปกป้องความเป็นส่วนตัว แทนที่จะใช้ ID ผู้ใช้เหมือนที่แพลตฟอร์มอื่นๆใช้ SimpleX มีตัวระบุสำหรับคิวข้อความ โดยแยกจากกันสำหรับผู้ติดต่อแต่ละราย + No comment provided by engineer. + + + To receive + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. ในการบันทึกข้อความเสียง โปรดให้สิทธิ์ในการใช้ไมโครโฟน @@ -5422,25 +7219,53 @@ You will be prompted to complete authentication before this feature is enabled.< หากต้องการเปิดเผยโปรไฟล์ที่ซ่อนอยู่ของคุณ ให้ป้อนรหัสผ่านแบบเต็มในช่องค้นหาในหน้า **โปรไฟล์แชทของคุณ** No comment provided by engineer. + + To send + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. เพื่อรองรับการแจ้งเตือนแบบทันที ฐานข้อมูลการแชทจะต้องได้รับการโยกย้าย No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. ในการตรวจสอบการเข้ารหัสแบบ encrypt จากต้นจนจบ กับผู้ติดต่อของคุณ ให้เปรียบเทียบ (หรือสแกน) รหัสบนอุปกรณ์ของคุณ No comment provided by engineer. + + Toggle chat list: + No comment provided by engineer. + Toggle incognito when connecting. No comment provided by engineer. + + Token status: %@. + token status + + + Toolbar opacity + No comment provided by engineer. + + + Total + No comment provided by engineer. + Transport isolation การแยกการขนส่ง No comment provided by engineer. + + Transport sessions + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). กำลังพยายามเชื่อมต่อกับเซิร์ฟเวอร์ที่ใช้รับข้อความจากผู้ติดต่อนี้ (ข้อผิดพลาด: %@) @@ -5490,10 +7315,9 @@ You will be prompted to complete authentication before this feature is enabled.< Unblock member? No comment provided by engineer. - - Unexpected error: %@ - ข้อผิดพลาดที่ไม่คาดคิด: %@ - item status description + + Undelivered messages + No comment provided by engineer. Unexpected migration state @@ -5503,7 +7327,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unfav. เลิกชอบ - No comment provided by engineer. + swipe action Unhide @@ -5540,6 +7364,10 @@ You will be prompted to complete authentication before this feature is enabled.< ข้อผิดพลาดที่ไม่รู้จัก No comment provided by engineer. + + Unknown servers! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. ยกเว้นกรณีที่คุณใช้อินเทอร์เฟซการโทรของ iOS ให้เปิดใช้งานโหมดห้ามรบกวนเพื่อหลีกเลี่ยงการรบกวน @@ -5573,11 +7401,15 @@ To connect, please ask your contact to create another connection link and check Unmute เปิดเสียง - No comment provided by engineer. + notification label action Unread เปลี่ยนเป็นยังไม่ได้อ่าน + swipe action + + + Unsupported connection link No comment provided by engineer. @@ -5589,11 +7421,6 @@ To connect, please ask your contact to create another connection link and check อัปเดต No comment provided by engineer. - - Update .onion hosts setting? - อัปเดตการตั้งค่าโฮสต์ .onion ไหม? - No comment provided by engineer. - Update database passphrase อัปเดตรหัสผ่านของฐานข้อมูล @@ -5604,9 +7431,12 @@ To connect, please ask your contact to create another connection link and check อัปเดตการตั้งค่าเครือข่ายไหม? No comment provided by engineer. - - Update transport isolation mode? - อัปเดตโหมดการแยกการขนส่งไหม? + + Update settings? + No comment provided by engineer. + + + Updated conditions No comment provided by engineer. @@ -5614,16 +7444,15 @@ To connect, please ask your contact to create another connection link and check การอัปเดตการตั้งค่าจะเชื่อมต่อไคลเอนต์กับเซิร์ฟเวอร์ทั้งหมดอีกครั้ง No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - การอัปเดตการตั้งค่านี้จะเชื่อมต่อไคลเอนต์กับเซิร์ฟเวอร์ทั้งหมดอีกครั้ง - No comment provided by engineer. - Upgrade and open chat อัปเกรดและเปิดการแชท No comment provided by engineer. + + Upload errors + No comment provided by engineer. + Upload failed No comment provided by engineer. @@ -5633,20 +7462,44 @@ To connect, please ask your contact to create another connection link and check อัปโหลดไฟล์ server test step + + Uploaded + No comment provided by engineer. + + + Uploaded files + No comment provided by engineer. + Uploading archive No comment provided by engineer. + + Use %@ + No comment provided by engineer. + Use .onion hosts ใช้โฮสต์ .onion No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? ใช้เซิร์ฟเวอร์ SimpleX Chat ไหม? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat ใช้แชท @@ -5656,6 +7509,14 @@ To connect, please ask your contact to create another connection link and check Use current profile No comment provided by engineer. + + Use for files + No comment provided by engineer. + + + Use for messages + No comment provided by engineer. + Use for new connections ใช้สำหรับการเชื่อมต่อใหม่ @@ -5678,23 +7539,45 @@ To connect, please ask your contact to create another connection link and check Use only local notifications? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + No comment provided by engineer. + + + Use private routing with unknown servers. + No comment provided by engineer. + Use server ใช้เซิร์ฟเวอร์ No comment provided by engineer. + + Use servers + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. No comment provided by engineer. - - User profile - โปรไฟล์ผู้ใช้ + + Use the app with one hand. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - การใช้โฮสต์ .onion ต้องการผู้ให้บริการ VPN ที่เข้ากันได้ + + Use web port + No comment provided by engineer. + + + User selection + No comment provided by engineer. + + + Username No comment provided by engineer. @@ -5761,11 +7644,19 @@ To connect, please ask your contact to create another connection link and check วิดีโอและไฟล์สูงสุด 1gb No comment provided by engineer. + + View conditions + No comment provided by engineer. + View security code ดูรหัสความปลอดภัย No comment provided by engineer. + + View updated conditions + No comment provided by engineer. + Visible history chat feature @@ -5780,11 +7671,15 @@ To connect, please ask your contact to create another connection link and check ห้ามส่งข้อความเสียงในแชทนี้ No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. ข้อความเสียงเป็นสิ่งต้องห้ามในกลุ่มนี้ No comment provided by engineer. + + Voice messages not allowed + No comment provided by engineer. + Voice messages prohibited! ห้ามข้อความเสียง! @@ -5814,6 +7709,14 @@ To connect, please ask your contact to create another connection link and check กําลังรอวิดีโอ No comment provided by engineer. + + Wallpaper accent + No comment provided by engineer. + + + Wallpaper background + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures No comment provided by engineer. @@ -5852,9 +7755,12 @@ To connect, please ask your contact to create another connection link and check เมื่อพร้อมใช้งาน No comment provided by engineer. - - When people request to connect, you can accept or reject it. - เมื่อมีคนขอเชื่อมต่อ คุณสามารถยอมรับหรือปฏิเสธได้ + + When connecting audio and video calls. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. No comment provided by engineer. @@ -5862,6 +7768,18 @@ To connect, please ask your contact to create another connection link and check เมื่อคุณแชร์โปรไฟล์ที่ไม่ระบุตัวตนกับใครสักคน โปรไฟล์นี้จะใช้สำหรับกลุ่มที่พวกเขาเชิญคุณ No comment provided by engineer. + + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + No comment provided by engineer. + + + Wired ethernet + No comment provided by engineer. + With encrypted files and media. No comment provided by engineer. @@ -5875,24 +7793,34 @@ To connect, please ask your contact to create another connection link and check With reduced battery usage. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + alert message + Wrong database passphrase รหัสผ่านฐานข้อมูลไม่ถูกต้อง No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + file error text + Wrong passphrase! รหัสผ่านผิด! No comment provided by engineer. - - XFTP servers - เซิร์ฟเวอร์ XFTP - No comment provided by engineer. - - - You - คุณ + + XFTP server No comment provided by engineer. @@ -5919,6 +7847,10 @@ To connect, please ask your contact to create another connection link and check คุณได้เชื่อมต่อกับ %@ แล้ว No comment provided by engineer. + + You are already connected with %@. + No comment provided by engineer. + You are already connecting to %@. No comment provided by engineer. @@ -5958,11 +7890,23 @@ Repeat join request? คุณได้รับเชิญให้เข้าร่วมกลุ่ม No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. คุณสามารถรับสายจากหน้าจอล็อกโดยไม่ต้องมีการตรวจสอบสิทธิ์อุปกรณ์และแอป No comment provided by engineer. + + You can change it in Appearance settings. + No comment provided by engineer. + + + You can configure servers via settings. + No comment provided by engineer. + You can create it later คุณสามารถสร้างได้ในภายหลัง @@ -5991,11 +7935,19 @@ Repeat join request? You can make it visible to your SimpleX contacts via Settings. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ ตอนนี้คุณสามารถส่งข้อความถึง %@ notification body + + You can send messages to %@ from Archived contacts. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + No comment provided by engineer. + You can set lock screen notification preview via settings. คุณสามารถตั้งค่าแสดงตัวอย่างการแจ้งเตือนบนหน้าจอล็อคผ่านการตั้งค่า @@ -6011,16 +7963,15 @@ Repeat join request? คุณสามารถแบ่งปันที่อยู่นี้กับผู้ติดต่อของคุณเพื่อให้พวกเขาเชื่อมต่อกับ **%@** No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - คุณสามารถแชร์ที่อยู่ของคุณเป็นลิงก์หรือรหัสคิวอาร์ - ใคร ๆ ก็สามารถเชื่อมต่อกับคุณได้ - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app คุณสามารถเริ่มแชทผ่านการตั้งค่าแอป / ฐานข้อมูล หรือโดยการรีสตาร์ทแอป No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. คุณสามารถเปิด SimpleX Lock ผ่านการตั้งค่า @@ -6033,23 +7984,23 @@ Repeat join request? You can view invitation link again in connection details. - No comment provided by engineer. + alert message You can't send messages! คุณไม่สามารถส่งข้อความได้! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - คุณควบคุมผ่านเซิร์ฟเวอร์ **เพื่อรับ** ข้อความผู้ติดต่อของคุณ - เซิร์ฟเวอร์ที่คุณใช้เพื่อส่งข้อความถึงพวกเขา - No comment provided by engineer. - You could not be verified; please try again. เราไม่สามารถตรวจสอบคุณได้ กรุณาลองอีกครั้ง. No comment provided by engineer. + + You decide who can connect. + ผู้คนสามารถเชื่อมต่อกับคุณผ่านลิงก์ที่คุณแบ่งปันเท่านั้น + No comment provided by engineer. + You have already requested connection via this address! No comment provided by engineer. @@ -6059,11 +8010,6 @@ Repeat join request? Repeat connection request? No comment provided by engineer. - - You have no chats - คุณไม่มีการแชท - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. คุณต้องใส่รหัสผ่านทุกครั้งที่เริ่มแอป - รหัสผ่านไม่ได้จัดเก็บไว้ในอุปกรณ์ @@ -6083,11 +8029,23 @@ Repeat connection request? คุณเข้าร่วมกลุ่มนี้แล้ว กำลังเชื่อมต่อเพื่อเชิญสมาชิกกลุ่ม No comment provided by engineer. + + You may migrate the exported database. + No comment provided by engineer. + + + You may save the exported archive. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. คุณต้องใช้ฐานข้อมูลแชทเวอร์ชันล่าสุดบนอุปกรณ์เครื่องเดียวเท่านั้น มิฉะนั้น คุณอาจหยุดได้รับข้อความจากผู้ติดต่อบางคน No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. คุณต้องอนุญาตให้ผู้ติดต่อของคุณส่งข้อความเสียงจึงจะสามารถส่งได้ @@ -6103,6 +8061,10 @@ Repeat connection request? คุณส่งคำเชิญเข้าร่วมกลุ่มแล้ว No comment provided by engineer. + + You should receive notifications. + token info + You will be connected to group when the group host's device is online, please wait or check later! คุณจะเชื่อมต่อกับกลุ่มเมื่ออุปกรณ์โฮสต์ของกลุ่มออนไลน์อยู่ โปรดรอหรือตรวจสอบภายหลัง! @@ -6136,6 +8098,10 @@ Repeat connection request? คุณจะยังได้รับสายเรียกเข้าและการแจ้งเตือนจากโปรไฟล์ที่ปิดเสียงเมื่อโปรไฟล์ของเขามีการใช้งาน No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. คุณจะหยุดได้รับข้อความจากกลุ่มนี้ ประวัติการแชทจะถูกรักษาไว้ @@ -6156,31 +8122,16 @@ Repeat connection request? คุณกำลังใช้โปรไฟล์ที่ไม่ระบุตัวตนสำหรับกลุ่มนี้ - ไม่อนุญาตให้เชิญผู้ติดต่อเพื่อป้องกันการแชร์โปรไฟล์หลักของคุณ No comment provided by engineer. - - Your %@ servers - เซิร์ฟเวอร์ %@ ของคุณ - No comment provided by engineer. - Your ICE servers เซิร์ฟเวอร์ ICE ของคุณ No comment provided by engineer. - - Your SMP servers - เซิร์ฟเวอร์ SMP ของคุณ - No comment provided by engineer. - Your SimpleX address ที่อยู่ SimpleX ของคุณ No comment provided by engineer. - - Your XFTP servers - เซิร์ฟเวอร์ XFTP ของคุณ - No comment provided by engineer. - Your calls การโทรของคุณ @@ -6196,16 +8147,17 @@ Repeat connection request? ฐานข้อมูลการแชทของคุณไม่ได้ถูก encrypt - ตั้งรหัสผ่านเพื่อ encrypt No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles โปรไฟล์แชทของคุณ No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - ผู้ติดต่อของคุณจะต้องออนไลน์เพื่อให้การเชื่อมต่อเสร็จสมบูรณ์ -คุณสามารถยกเลิกการเชื่อมต่อนี้และลบผู้ติดต่อออก (และลองใหม่ในภายหลังด้วยลิงก์ใหม่) + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. No comment provided by engineer. @@ -6223,6 +8175,10 @@ You can cancel this connection and remove the contact (and try later with a new ผู้ติดต่อของคุณจะยังคงเชื่อมต่ออยู่ No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. ฐานข้อมูลแชทปัจจุบันของคุณจะถูกลบและแทนที่ด้วยฐานข้อมูลที่นำเข้า @@ -6251,33 +8207,34 @@ You can cancel this connection and remove the contact (and try later with a new Your profile **%@** will be shared. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - โปรไฟล์ของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณและแชร์กับผู้ติดต่อของคุณเท่านั้น -เซิร์ฟเวอร์ SimpleX ไม่สามารถดูโปรไฟล์ของคุณได้ + + Your profile is stored on your device and only shared with your contacts. + โปรไฟล์นี้แชร์กับผู้ติดต่อของคุณเท่านั้น No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - โปรไฟล์ รายชื่อผู้ติดต่อ และข้อความที่ส่งของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณ + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + โปรไฟล์ของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณและแชร์กับผู้ติดต่อของคุณเท่านั้น เซิร์ฟเวอร์ SimpleX ไม่สามารถดูโปรไฟล์ของคุณได้ No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your random profile โปรไฟล์แบบสุ่มของคุณ No comment provided by engineer. - - Your server - เซิร์ฟเวอร์ของคุณ - No comment provided by engineer. - Your server address ที่อยู่เซิร์ฟเวอร์ของคุณ No comment provided by engineer. + + Your servers + No comment provided by engineer. + Your settings การตั้งค่าของคุณ @@ -6318,11 +8275,19 @@ SimpleX servers cannot see your profile. รับสายแล้ว call status + + accepted invitation + chat list item title + admin ผู้ดูแลระบบ member role + + admins + feature role + agreeing encryption for %@… ยอมรับ encryption สำหรับ %@… @@ -6333,6 +8298,10 @@ SimpleX servers cannot see your profile. เห็นด้วยกับการ encryption… chat item text + + all members + feature role + always เสมอ @@ -6342,6 +8311,14 @@ SimpleX servers cannot see your profile. and %lld other events No comment provided by engineer. + + archived report + No comment provided by engineer. + + + attempts + No comment provided by engineer. + audio call (not e2e encrypted) การโทรด้วยเสียง (ไม่ได้ encrypt จากต้นจนจบ) @@ -6371,13 +8348,18 @@ SimpleX servers cannot see your profile. blocked by admin - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold ตัวหนา No comment provided by engineer. + + call + No comment provided by engineer. + call error การโทรผิดพลาด @@ -6480,7 +8462,7 @@ SimpleX servers cannot see your profile. connecting… กำลังเชื่อมต่อ… - chat list item title + No comment provided by engineer. connection established @@ -6526,10 +8508,15 @@ SimpleX servers cannot see your profile. วัน time unit + + decryption errors + No comment provided by engineer. + default (%@) ค่าเริ่มต้น (%@) - pref value + delete after time +pref value default (no) @@ -6574,6 +8561,10 @@ SimpleX servers cannot see your profile. ข้อความที่ซ้ำกัน integrity error chat item + + duplicates + No comment provided by engineer. + e2e encrypted encrypted จากต้นจนจบ @@ -6649,8 +8640,12 @@ SimpleX servers cannot see your profile. ผิดพลาด No comment provided by engineer. - - event happened + + expired + No comment provided by engineer. + + + forwarded No comment provided by engineer. @@ -6678,6 +8673,10 @@ SimpleX servers cannot see your profile. iOS Keychain จะใช้เพื่อจัดเก็บรหัสผ่านอย่างปลอดภัยหลังจากที่คุณรีสตาร์ทแอปหรือเปลี่ยนรหัสผ่าน ซึ่งจะช่วยให้รับการแจ้งเตือนแบบทันทีได้ No comment provided by engineer. + + inactive + No comment provided by engineer. + incognito via contact address link ไม่ระบุตัวตนผ่านลิงค์ที่อยู่ติดต่อ @@ -6718,6 +8717,10 @@ SimpleX servers cannot see your profile. คำเชิญเข้าร่วมกลุ่ม %@ group name + + invite + No comment provided by engineer. + invited เชิญ @@ -6772,6 +8775,10 @@ SimpleX servers cannot see your profile. เชื่อมต่อสำเร็จ rcv group event chat item + + message + No comment provided by engineer. + message received ข้อความที่ได้รับ @@ -6797,6 +8804,10 @@ SimpleX servers cannot see your profile. กลั่นกรองโดย %@ marked deleted chat item preview text + + moderator + member role + months เดือน @@ -6805,7 +8816,7 @@ SimpleX servers cannot see your profile. never ไม่เคย - No comment provided by engineer. + delete after time new message @@ -6836,8 +8847,8 @@ SimpleX servers cannot see your profile. off ปิด enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -6854,16 +8865,36 @@ SimpleX servers cannot see your profile. เปิด group pref value + + other + No comment provided by engineer. + + + other errors + No comment provided by engineer. + owner เจ้าของ member role + + owners + feature role + peer-to-peer เพื่อนต่อเพื่อน No comment provided by engineer. + + pending + No comment provided by engineer. + + + pending approval + No comment provided by engineer. + quantum resistant e2e encryption chat item text @@ -6878,6 +8909,10 @@ SimpleX servers cannot see your profile. ได้รับการยืนยัน… No comment provided by engineer. + + rejected + No comment provided by engineer. + rejected call สายถูกปฏิเสธ @@ -6906,6 +8941,22 @@ SimpleX servers cannot see your profile. ลบคุณออกแล้ว rcv group event chat item + + requested to connect + chat list item title + + + saved + No comment provided by engineer. + + + saved from %@ + No comment provided by engineer. + + + search + No comment provided by engineer. + sec วินาที @@ -6930,6 +8981,12 @@ SimpleX servers cannot see your profile. send direct message No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + queue info + set new contact address profile update event chat item @@ -6966,10 +9023,18 @@ SimpleX servers cannot see your profile. ไม่ทราบ connection info + + unknown servers + No comment provided by engineer. + unknown status No comment provided by engineer. + + unprotected + No comment provided by engineer. + updated group profile อัปเดตโปรไฟล์กลุ่มแล้ว @@ -7008,6 +9073,10 @@ SimpleX servers cannot see your profile. ผ่านรีเลย์ No comment provided by engineer. + + video + No comment provided by engineer. + video call (not e2e encrypted) การสนทนาทางวิดีโอ (ไม่ได้ encrypt จากต้นจนจบ) @@ -7033,11 +9102,19 @@ SimpleX servers cannot see your profile. สัปดาห์ time unit + + when IP hidden + No comment provided by engineer. + yes ใช่ pref value + + you + No comment provided by engineer. + you are invited to group คุณได้รับเชิญให้เข้าร่วมกลุ่ม @@ -7110,7 +9187,7 @@ SimpleX servers cannot see your profile.
- +
@@ -7146,7 +9223,7 @@ SimpleX servers cannot see your profile.
- +
@@ -7166,4 +9243,205 @@ SimpleX servers cannot see your profile.
+ +
+ +
+ + + %d new events + notification body + + + From %d chat(s) + notification body + + + From: %@ + notification body + + + New events + notification + + + New messages + notification + + +
+ +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + App is locked! + No comment provided by engineer. + + + Cancel + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Cannot forward message + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database encrypted! + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database passphrase is required to open chat. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Passphrase + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + No comment provided by engineer. + + + Sending a message takes longer than expected. + No comment provided by engineer. + + + Sending message… + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Slow network? + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wait + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/th.xcloc/contents.json b/apps/ios/SimpleX Localizations/th.xcloc/contents.json index b60f9edb3e..ee6ee63ea9 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/th.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "th", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff b/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff index d5264200c3..bbee40c2b9 100644 --- a/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (kopyalanabilir) @@ -99,7 +72,7 @@ %1$@ at %2$@: - %1$@, %2$@ de + 1$@, %2$@'de: copied message info, <sender> at <time> @@ -109,6 +82,7 @@ %@ downloaded + %@ indirildi No comment provided by engineer. @@ -126,13 +100,19 @@ %@ onaylandı No comment provided by engineer. + + %@ server + %@ sunucu + No comment provided by engineer. + %@ servers - %@ sunucuları + %@ sunucular No comment provided by engineer. %@ uploaded + %@ yüklendi No comment provided by engineer. @@ -140,6 +120,11 @@ %@ bağlanmak istiyor! notification title + + %1$@, %2$@ + %1$@,%2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ ve %lld üyeleri @@ -160,11 +145,36 @@ %d gün time interval + + %d file(s) are still being downloaded. + %d dosyası(ları) hala indiriliyor. + forward confirmation reason + + + %d file(s) failed to download. + %d dosyası(ları) indirilemedi. + forward confirmation reason + + + %d file(s) were deleted. + %d dosyası(ları) silindi. + forward confirmation reason + + + %d file(s) were not downloaded. + %d dosyası(ları) indirilmedi. + forward confirmation reason + %d hours %d saat time interval + + %d messages not forwarded + %d mesajı iletilmeyedi + alert title + %d min %d dakika @@ -180,6 +190,10 @@ %d saniye time interval + + %d seconds(s) + delete after time + %d skipped message(s) %d okunmamış mesaj(lar) @@ -250,11 +264,6 @@ %lld yeni arayüz dilleri No comment provided by engineer. - - %lld second(s) - %lld saniye - No comment provided by engineer. - %lld seconds %lld saniye @@ -302,12 +311,7 @@ %u messages skipped. - %u mesaj atlandı - No comment provided by engineer. - - - ( - ( + %u mesajlar atlandı. No comment provided by engineer. @@ -320,38 +324,29 @@ (bu cihaz v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. + + **Create 1-time link**: to create and share a new invitation link. **Kişi ekle**: yeni bir davet bağlantısı oluşturmak için, ya da aldığın bağlantıyla bağlan. No comment provided by engineer. - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Yeni kişi ekleyin**: tek seferlik QR Kodunuzu oluşturmak veya kişisel ulaşım bilgileri bağlantısı için. - No comment provided by engineer. - **Create group**: to create a new group. **Grup oluştur**: yeni bir grup oluşturmak için. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **Daha gizli**: her 20 dakikada yeni mesajlar için kontrol et. Cihaz jetonu SimpleX Chat sunucusuyla paylaşılacak, ama ne kadar kişi veya mesaja sahip olduğun paylaşılmayacak. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **En gizli**: SimpleX Chat bildirim sunucusunu kullanma, arkaplanda mesajları periyodik olarak kontrol edin (uygulamayı ne sıklıkta kullandığınıza bağlıdır). No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Lütfen dikkat**: Aynı veritabanını iki cihazda kullanmak, güvenlik koruması olarak bağlantılarınızdaki mesajların şifresinin çözülmesini engelleyecektir. No comment provided by engineer. @@ -359,11 +354,16 @@ **Lütfen aklınızda bulunsun**: eğer parolanızı kaybederseniz parolanızı değiştirme veya geri kurtarma ihtimaliniz YOKTUR. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Önerilen**: cihaz tokeni ve bildirimler SimpleX Chat bildirim sunucularına gönderilir, ama mesajın içeriği, boyutu veya kimden geldiği gönderilmez. No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + edindiğiniz bağlantı aracılığıyla bağlanmak için **Linki tarayın/yapıştırın**. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **Dikkat**: Anında iletilen bildirimlere Anahtar Zinciri'nde kaydedilmiş parola gereklidir. @@ -371,6 +371,7 @@ **Warning**: the archive will be removed. + **Uyarı**: arşiv silinecektir. No comment provided by engineer. @@ -388,11 +389,6 @@ \*kalın* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -429,11 +425,6 @@ - düzenleme geçmişi. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 saniye @@ -441,13 +432,14 @@ 0s - 0 saniye + 0sn No comment provided by engineer. 1 day 1 gün - time interval + delete after time +time interval 1 hour @@ -462,12 +454,28 @@ 1 month 1 ay - time interval + delete after time +time interval 1 week 1 hafta - time interval + delete after time +time interval + + + 1 year + delete after time + + + 1-time link + tek kullanımlık bağlantı + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + Tek kullanımlık bağlantı *sadece bir kişi ile* kullanılabilir - kişiyle veya uygulama içinden paylaş. + No comment provided by engineer. 5 minutes @@ -484,11 +492,6 @@ 30 saniye No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -538,31 +541,32 @@ Adres değişimi iptal edilsin mi? No comment provided by engineer. - - About SimpleX - SimpleX Hakkında - No comment provided by engineer. - About SimpleX Chat SimpleX Chat hakkında No comment provided by engineer. - - About SimpleX address - SimpleX Chat adresi hakkında + + About operators + Operatörler hakkında No comment provided by engineer. - - Accent color - Vurgu rengi + + Accent + Ana renk No comment provided by engineer. Accept Kabul et accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Koşulları kabul et + No comment provided by engineer. Accept connection request? @@ -577,21 +581,45 @@ Accept incognito Takma adla kabul et - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + Kabul edilmiş koşullar + No comment provided by engineer. + + + Acknowledged + Onaylandı + No comment provided by engineer. + + + Acknowledgement errors + Onay hataları + No comment provided by engineer. + + + Active + token status text + + + Active connections + Aktif bağlantılar + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Kişilerinizin başkalarıyla paylaşabilmesi için profilinize adres ekleyin. Profil güncellemesi kişilerinize gönderilecek. No comment provided by engineer. - - Add contact - Kişi ekle + + Add friends + Arkadaş ekle No comment provided by engineer. - - Add preset servers - Önceden ayarlanmış sunucu ekle + + Add list No comment provided by engineer. @@ -599,14 +627,19 @@ Profil ekle No comment provided by engineer. + + Add server + Sunucu ekle + No comment provided by engineer. + Add servers by scanning QR codes. Karekod taratarak sunucuları ekleyin. No comment provided by engineer. - - Add server… - Sunucu ekle… + + Add team members + Takım üyesi ekle No comment provided by engineer. @@ -614,11 +647,45 @@ Başka bir cihaza ekle No comment provided by engineer. + + Add to list + No comment provided by engineer. + Add welcome message Karşılama mesajı ekleyin No comment provided by engineer. + + Add your team members to the conversations. + Takım üyelerini konuşmalara ekle. + No comment provided by engineer. + + + Added media & file servers + medya ve dosya sunucuları eklendi + No comment provided by engineer. + + + Added message servers + Mesaj sunucuları eklendi + No comment provided by engineer. + + + Additional accent + Ek ana renk + No comment provided by engineer. + + + Additional accent 2 + Ek vurgu 2 + No comment provided by engineer. + + + Additional secondary + Ek ikincil renk + No comment provided by engineer. + Address Adres @@ -629,8 +696,19 @@ Adres değişikliği iptal edilecek. Eski alıcı adresi kullanılacaktır. No comment provided by engineer. + + Address or 1-time link? + adres mi yoksa tek kullanımlık bağlantı mı? + No comment provided by engineer. + + + Address settings + Adres seçenekleri + No comment provided by engineer. + Admins can block a member for all. + Yöneticiler bir üyeyi tamamen engelleyebilirler. No comment provided by engineer. @@ -643,6 +721,15 @@ Gelişmiş ağ ayarları No comment provided by engineer. + + Advanced settings + Gelişmiş ayarlar + No comment provided by engineer. + + + All + No comment provided by engineer. + All app data is deleted. Tüm uygulama verileri silinir. @@ -653,16 +740,30 @@ Tüm konuşmalar ve mesajlar silinecektir. Bu, geri alınamaz! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + alert message + All data is erased when it is entered. Kullanıldığında bütün veriler silinir. No comment provided by engineer. + + All data is kept private on your device. + Tüm veriler cihazınıza özeldir. + No comment provided by engineer. + All group members will remain connected. Tüm grup üyeleri bağlı kalacaktır. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + Bütün mesajlar ve dosyalar **uçtan-uca şifrelemeli** gönderilir, doğrudan mesajlarda kuantum güvenlik ile birlikte. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! Tüm mesajlar silinecektir - bu geri alınamaz! @@ -678,6 +779,19 @@ %@ 'den gelen bütün yeni mesajlar saklı olacak! No comment provided by engineer. + + All profiles + Tüm Profiller + profile dropdown + + + All reports will be archived for you. + No comment provided by engineer. + + + All servers + No comment provided by engineer. + All your contacts will remain connected. Konuştuğun kişilerin tümü bağlı kalacaktır. @@ -690,6 +804,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Tüm kişileriniz, konuşmalarınız ve dosyalarınız güvenli bir şekilde şifrelenir ve yapılandırılmış XFTP yönlendiricilerine parçalar halinde yüklenir. No comment provided by engineer. @@ -702,11 +817,21 @@ Yalnızca irtibat kişiniz izin veriyorsa aramalara izin verin. No comment provided by engineer. + + Allow calls? + Aramalara izin verilsin mi ? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Eğer kişide izin verirse kaybolan mesajlara izin ver. No comment provided by engineer. + + Allow downgrade + Sürüm düşürmeye izin ver + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Konuştuğun kişi, kalıcı olarak silinebilen mesajlara izin veriyorsa sen de ver. (24 saat içinde) @@ -724,7 +849,7 @@ Allow sending direct messages to members. - Üyelere direkt mesaj göndermeye izin ver. + Üyelere doğrudan mesaj göndermeye izin ver. No comment provided by engineer. @@ -732,11 +857,25 @@ Kendiliğinden yok olan mesajlar göndermeye izin ver. No comment provided by engineer. + + Allow sharing + Paylaşıma izin ver + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Gönderilen mesajların kalıcı olarak silinmesine izin ver. (24 saat içinde) No comment provided by engineer. + + Allow to report messsages to moderators. + No comment provided by engineer. + + + Allow to send SimpleX links. + SimpleX bağlantıları göndilmesine izin ver. + No comment provided by engineer. + Allow to send files and media. Dosya ve medya göndermeye izin ver. @@ -797,9 +936,14 @@ Zaten gruba bağlanılıyor! No comment provided by engineer. + + Always use private routing. + Her zaman gizli yönlendirme kullan. + No comment provided by engineer. + Always use relay - Her zaman yönlendirici kulan + Her zaman yönlendirici kullan No comment provided by engineer. @@ -807,11 +951,20 @@ Verilen adla boş bir sohbet profili oluşturulur ve uygulama her zamanki gibi açılır. No comment provided by engineer. + + Another reason + report reason + Answer call Aramayı cevapla No comment provided by engineer. + + Anybody can host servers. + Açık kaynak protokolü ve kodu - herhangi biri sunucuları çalıştırabilir. + No comment provided by engineer. + App build: %@ Uygulama sürümü: %@ @@ -819,6 +972,7 @@ App data migration + Uygulama verisi taşıma No comment provided by engineer. @@ -826,6 +980,10 @@ Uygulama yerel dosyaları şifreler (videolar dışında). No comment provided by engineer. + + App group: + No comment provided by engineer. + App icon Uygulama simgesi @@ -841,6 +999,11 @@ Uygulama parolası kendi kendini imha eden parolayla değiştirildi. No comment provided by engineer. + + App session + Uygulama oturumu + No comment provided by engineer. + App version Uygulama sürümü @@ -858,14 +1021,56 @@ Apply + Uygula + No comment provided by engineer. + + + Apply to + Şuna uygula + No comment provided by engineer. + + + Archive + No comment provided by engineer. + + + Archive %lld reports? + No comment provided by engineer. + + + Archive all reports? No comment provided by engineer. Archive and upload + Arşivle ve yükle + No comment provided by engineer. + + + Archive contacts to chat later. + Daha sonra görüşmek için kişileri arşivleyin. + No comment provided by engineer. + + + Archive report + No comment provided by engineer. + + + Archive report? + No comment provided by engineer. + + + Archive reports + swipe action + + + Archived contacts + Arşivli kişiler No comment provided by engineer. Archiving database + Veritabanı arşivleniyor No comment provided by engineer. @@ -928,11 +1133,21 @@ Fotoğrafları otomatik kabul et No comment provided by engineer. + + Auto-accept settings + Ayarları otomatik olarak kabul et + alert title + Back Geri No comment provided by engineer. + + Background + Arka plan + No comment provided by engineer. + Bad desktop address Kötü bilgisayar adresi @@ -948,16 +1163,59 @@ Kötü mesaj karması No comment provided by engineer. + + Better calls + Daha iyi aramalar + No comment provided by engineer. + Better groups Daha iyi gruplar No comment provided by engineer. + + Better groups performance + No comment provided by engineer. + + + Better message dates. + Daha iyi mesaj tarihleri. + No comment provided by engineer. + Better messages Daha iyi mesajlar No comment provided by engineer. + + Better networking + Daha iyi ağ oluşturma + No comment provided by engineer. + + + Better notifications + Daha iyi bildirimler + No comment provided by engineer. + + + Better privacy and security + No comment provided by engineer. + + + Better security ✅ + Daha iyi güvenlik ✅ + No comment provided by engineer. + + + Better user experience + Daha iyi kullanıcı deneyimi + No comment provided by engineer. + + + Black + Siyah + No comment provided by engineer. + Block Engelle @@ -993,6 +1251,16 @@ Yönetici tarafından engellendi No comment provided by engineer. + + Blur for better privacy. + Daha iyi gizlilik için bulanıklaştır. + No comment provided by engineer. + + + Blur media + Medyayı bulanıklaştır + No comment provided by engineer. + Both you and your contact can add message reactions. Sen ve konuştuğun kişi mesaj tepkileri ekleyebilir. @@ -1023,11 +1291,31 @@ Bulgarca, Fince, Tayca ve Ukraynaca - kullanıcılara ve [Weblate] e teşekkürler! (https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + İş adresi + No comment provided by engineer. + + + Business chats + İş konuşmaları + No comment provided by engineer. + + + Businesses + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). Sohbet profiline göre (varsayılan) veya [bağlantıya göre](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + No comment provided by engineer. + Call already ended! Arama çoktan bitti! @@ -1038,11 +1326,26 @@ Aramalar No comment provided by engineer. + + Calls prohibited! + Aramalara izin verilmiyor! + No comment provided by engineer. + Camera not available Kamera mevcut değil No comment provided by engineer. + + Can't call contact + Kişi aranamıyor + No comment provided by engineer. + + + Can't call member + Üye aranamaz + No comment provided by engineer. + Can't invite contact! Kişi davet edilemiyor! @@ -1053,13 +1356,20 @@ Kişiler davet edilemiyor! No comment provided by engineer. + + Can't message member + Üyeye mesaj gönderilemiyor + No comment provided by engineer. + Cancel İptal et - No comment provided by engineer. + alert action +alert button Cancel migration + Taşımayı iptal et No comment provided by engineer. @@ -1067,9 +1377,24 @@ Veritabanı şifresini kaydetmek için Anahtar Zinciri'ne erişilemiyor No comment provided by engineer. + + Cannot forward message + Mesaj iletilemiyor + No comment provided by engineer. + Cannot receive file Dosya alınamıyor + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Kapasite aşıldı - alıcı önceden gönderilen mesajları almadı. + snd error text + + + Cellular + Hücresel Veri No comment provided by engineer. @@ -1077,6 +1402,15 @@ Değiştir No comment provided by engineer. + + Change automatic message deletion? + alert title + + + Change chat profiles + Sohbet profillerini değiştir + authentication reason + Change database passphrase? Veritabanı parolasını değiştir? @@ -1121,11 +1455,26 @@ Change self-destruct passcode Kendini yok eden parolayı değiştir authentication reason - set passcode view +set passcode view - - Chat archive - Sohbet arşivi + + Chat + Sohbet + No comment provided by engineer. + + + Chat already exists + Sohbet zaten mevcut + No comment provided by engineer. + + + Chat already exists! + Sohbet zaten mevcut! + No comment provided by engineer. + + + Chat colors + Sohbet renkleri No comment provided by engineer. @@ -1143,6 +1492,11 @@ Sohbet veritabanı silindi No comment provided by engineer. + + Chat database exported + Veritabanı dışa aktarıldı + No comment provided by engineer. + Chat database imported Sohbet veritabanı içe aktarıldı @@ -1163,8 +1517,14 @@ Sohbet durduruldu. Bu veritabanını zaten başka bir cihazda kullandıysanız, sohbete başlamadan önce onu geri aktarmalısınız. No comment provided by engineer. + + Chat list + Sohbet listesi + No comment provided by engineer. + Chat migrated! + Sohbet taşındı! No comment provided by engineer. @@ -1172,15 +1532,50 @@ Sohbet tercihleri No comment provided by engineer. + + Chat preferences were changed. + Sohbet tercihleri değiştirildi. + alert message + + + Chat profile + Kullanıcı profili + No comment provided by engineer. + + + Chat theme + Sohbet teması + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + Sohbet bütün üyeler için silinecek - bu geri alınamaz! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + Sohbet senden silinecek - bu geri alınamaz! + No comment provided by engineer. + Chats Sohbetler No comment provided by engineer. + + Check messages every 20 min. + Her 20 dakikada mesajları kontrol et. + No comment provided by engineer. + + + Check messages when allowed. + İzin verildiğinde mesajları kontrol et. + No comment provided by engineer. + Check server address and try again. Sunucu adresini kontrol edip tekrar deneyin. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1189,6 +1584,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + Yeni cihazda _Başka bir cihazdan taşı_ seçeneğini seçin ve QR kodunu tarayın. No comment provided by engineer. @@ -1201,10 +1597,25 @@ Kütüphaneden seç No comment provided by engineer. + + Chunks deleted + Parçalar silindi + No comment provided by engineer. + + + Chunks downloaded + Parçalar indirildi + No comment provided by engineer. + + + Chunks uploaded + Parçalar yüklendi + No comment provided by engineer. + Clear Temizle - No comment provided by engineer. + swipe action Clear conversation @@ -1216,6 +1627,14 @@ Sohbet temizlensin mi? No comment provided by engineer. + + Clear group? + No comment provided by engineer. + + + Clear or delete group? + No comment provided by engineer. + Clear private notes? Gizli notlar temizlensin mi? @@ -1226,11 +1645,20 @@ Doğrulamayı temizle No comment provided by engineer. - - Colors - Renkler + + Color chats with the new themes. + Yeni temalarla renkli sohbetler. No comment provided by engineer. + + Color mode + Renk modu + No comment provided by engineer. + + + Community guidelines violation + report reason + Compare file Dosya karşılaştır @@ -1241,11 +1669,55 @@ Güvenlik kodlarını kişilerinle karşılaştır. No comment provided by engineer. + + Completed + Tamamlandı + No comment provided by engineer. + + + Conditions accepted on: %@. + Şuradaki koşullar kabul edildi: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + Koşullar operatör(ler) için kabul edildi: **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + Koşullar çoktan operatör(ler) tarafından kabul edildi: **%@**. + No comment provided by engineer. + + + Conditions of use + Kullanım koşulları + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + Koşullar bu operatör(ler) için kabul edilecektir: **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + Koşullar şu tarihte kabul edilecektir: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + Koşullar etkin operatörler için şu tarihte otomatik olarak kabul edilecektir: %@. + No comment provided by engineer. + Configure ICE servers ICE sunucularını ayarla No comment provided by engineer. + + Configure server operators + No comment provided by engineer. + Confirm Onayla @@ -1256,13 +1728,24 @@ Parolayı onayla No comment provided by engineer. + + Confirm contact deletion? + Kişiyi silmek istediğinizden emin misiniz ? + No comment provided by engineer. + Confirm database upgrades Veritabanı geliştirmelerini onayla No comment provided by engineer. + + Confirm files from unknown servers. + Bilinmeyen sunuculardan gelen dosyaları onayla. + No comment provided by engineer. + Confirm network settings + Ağ ayarlarını onaylayın No comment provided by engineer. @@ -1277,12 +1760,18 @@ Confirm that you remember database passphrase to migrate it. + Taşımak için veritabanı parolasını hatırladığınızı doğrulayın. No comment provided by engineer. Confirm upload + Yüklemeyi onayla No comment provided by engineer. + + Confirmed + token status text + Connect Bağlan @@ -1303,6 +1792,11 @@ Bilgisayara bağlan No comment provided by engineer. + + Connect to your friends faster. + Arkadaşlarınıza daha hızlı bağlanın. + No comment provided by engineer. + Connect to yourself? Kendine mi bağlanacaksın? @@ -1342,14 +1836,29 @@ Bu senin kendi tek kullanımlık bağlantın! %@ ile bağlan No comment provided by engineer. + + Connected + Bağlandı + No comment provided by engineer. + Connected desktop Bilgisayara bağlandı No comment provided by engineer. + + Connected servers + Bağlı sunucular + No comment provided by engineer. + Connected to desktop - Bilgisayara bağlanıldı + Masaüstüne bağlandı + No comment provided by engineer. + + + Connecting + Bağlanıyor No comment provided by engineer. @@ -1362,6 +1871,11 @@ Bu senin kendi tek kullanımlık bağlantın! Sunucuya bağlanıyor…(hata:%@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + Kişiye bağlanılıyor, lütfen bekleyin ya da daha sonra kontrol edin! + No comment provided by engineer. + Connecting to desktop Bilgisayara bağlanıyor @@ -1372,6 +1886,15 @@ Bu senin kendi tek kullanımlık bağlantın! Bağlantı No comment provided by engineer. + + Connection and servers status. + Bağlantı ve sunucuların durumu. + No comment provided by engineer. + + + Connection blocked + No comment provided by engineer. + Connection error Bağlantı hatası @@ -1382,11 +1905,34 @@ Bu senin kendi tek kullanımlık bağlantın! Bağlantı hatası (DOĞRULAMA) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + No comment provided by engineer. + + + Connection not ready. + No comment provided by engineer. + + + Connection notifications + Bağlantı bildirimleri + No comment provided by engineer. + Connection request sent! Bağlantı daveti gönderildi! No comment provided by engineer. + + Connection requires encryption renegotiation. + No comment provided by engineer. + + + Connection security + Bağlantı güvenliği + No comment provided by engineer. + Connection terminated Bağlantı sonlandırılmış @@ -1397,6 +1943,16 @@ Bu senin kendi tek kullanımlık bağlantın! Bağlantı süresi geçmiş No comment provided by engineer. + + Connection with desktop stopped + Masaüstü ile bağlantı durduruldu + No comment provided by engineer. + + + Connections + Bağlantılar + No comment provided by engineer. + Contact allows Kişi izin veriyor @@ -1407,6 +1963,11 @@ Bu senin kendi tek kullanımlık bağlantın! Kişi zaten mevcut No comment provided by engineer. + + Contact deleted! + Kişiler silindi! + No comment provided by engineer. + Contact hidden: Kişi gizli: @@ -1417,9 +1978,9 @@ Bu senin kendi tek kullanımlık bağlantın! Kişi bağlandı notification - - Contact is not connected yet! - Kişi şuan bağlanmadı! + + Contact is deleted. + Kişi silindi. No comment provided by engineer. @@ -1432,6 +1993,11 @@ Bu senin kendi tek kullanımlık bağlantın! Kişi tercihleri No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + Kişiler silinecek - bu geri alınamaz ! + No comment provided by engineer. + Contacts Kişiler @@ -1442,21 +2008,40 @@ Bu senin kendi tek kullanımlık bağlantın! Kişiler silinmesi için mesajları işaretleyebilir; onları görüntüleyebilirsin. No comment provided by engineer. + + Content violates conditions of use + blocking reason + Continue Devam et No comment provided by engineer. + + Conversation deleted! + Sohbet silindi! + No comment provided by engineer. + Copy Kopyala - chat item action + No comment provided by engineer. + + + Copy error + Kopyalama hatası + No comment provided by engineer. Core version: v%@ Çekirdek sürümü: v%@ No comment provided by engineer. + + Corner + Köşeleri yuvarlama + No comment provided by engineer. + Correct name to %@? İsim %@ olarak düzeltilsin mi? @@ -1467,6 +2052,11 @@ Bu senin kendi tek kullanımlık bağlantın! Oluştur No comment provided by engineer. + + Create 1-time link + Tek kullanımlık bağlantı oluştur + No comment provided by engineer. + Create SimpleX address SimpleX adresi oluştur @@ -1477,11 +2067,6 @@ Bu senin kendi tek kullanımlık bağlantın! Rasgele profil kullanarak grup oluştur. No comment provided by engineer. - - Create an address to let people connect with you. - İnsanların seninle bağlanması için bir adres oluştur. - No comment provided by engineer. - Create file Dosya oluştur @@ -1502,6 +2087,10 @@ Bu senin kendi tek kullanımlık bağlantın! Bağlantı oluştur No comment provided by engineer. + + Create list + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 [bilgisayar uygulaması] nda yeni bir profil oluştur(https://simplex.chat/downloads/). 💻 @@ -1527,6 +2116,11 @@ Bu senin kendi tek kullanımlık bağlantın! Profilini oluştur No comment provided by engineer. + + Created + Yaratıldı + No comment provided by engineer. + Created at Şurada oluşturuldu @@ -1537,13 +2131,9 @@ Bu senin kendi tek kullanımlık bağlantın! Şurada oluşturuldu: %@ copied message info - - Created on %@ - %@ de oluşturuldu - No comment provided by engineer. - Creating archive link + Arşiv bağlantısı oluşturuluyor No comment provided by engineer. @@ -1556,11 +2146,21 @@ Bu senin kendi tek kullanımlık bağlantın! Şu anki şifre No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + Şu anki koşulların yazısı yüklenemiyor, bu bağlantıdan koşullara inceleyebilirsin: + No comment provided by engineer. + Current passphrase… Şu anki parola… No comment provided by engineer. + + Current profile + Aktif profil + No comment provided by engineer. + Currently maximum supported file size is %@. Şu anki maksimum desteklenen dosya boyutu %@ kadardır. @@ -1571,11 +2171,26 @@ Bu senin kendi tek kullanımlık bağlantın! Özel saat No comment provided by engineer. + + Customizable message shape. + Özelleştirilebilir mesaj şekli. + No comment provided by engineer. + + + Customize theme + Renk temalarını kişiselleştir + No comment provided by engineer. + Dark Karanlık No comment provided by engineer. + + Dark mode colors + Karanlık mod renkleri + No comment provided by engineer. + Database ID Veritabanı kimliği @@ -1674,6 +2289,11 @@ Bu senin kendi tek kullanımlık bağlantın! Uygulama yeniden başlatıldığında veritabanı taşınacaktır No comment provided by engineer. + + Debug delivery + Hata ayıklama teslimatı + No comment provided by engineer. + Decentralized Merkezi Olmayan @@ -1687,18 +2307,19 @@ Bu senin kendi tek kullanımlık bağlantın! Delete Sil - chat item action + alert action +swipe action + + + Delete %lld messages of members? + Üyelerin %lld mesajları silinsin mi? + No comment provided by engineer. Delete %lld messages? %lld mesaj silinsin mi? No comment provided by engineer. - - Delete Contact - Kişiyi sil - No comment provided by engineer. - Delete address Adresi sil @@ -1724,14 +2345,13 @@ Bu senin kendi tek kullanımlık bağlantın! Sil ve kişiye bildir No comment provided by engineer. - - Delete archive - Arşivi sil + + Delete chat + Sohbeti sil No comment provided by engineer. - - Delete chat archive? - Sohbet arşivi silinsin mi? + + Delete chat messages from your device. No comment provided by engineer. @@ -1744,6 +2364,11 @@ Bu senin kendi tek kullanımlık bağlantın! Sohbet profili silinsin mi? No comment provided by engineer. + + Delete chat? + Sohbet silinsin mi? + No comment provided by engineer. + Delete connection Bağlantıyı sil @@ -1754,11 +2379,9 @@ Bu senin kendi tek kullanımlık bağlantın! Kişiyi sil No comment provided by engineer. - - Delete contact? -This cannot be undone! - Kişi silinsin mi? -Bu geri alınamaz! + + Delete contact? + Kişiyi sil? No comment provided by engineer. @@ -1768,6 +2391,7 @@ Bu geri alınamaz! Delete database from this device + Veritabanını bu cihazdan sil No comment provided by engineer. @@ -1820,6 +2444,10 @@ Bu geri alınamaz! Bağlantı silinsin mi? No comment provided by engineer. + + Delete list? + alert title + Delete member message? Kişinin mesajı silinsin mi? @@ -1833,7 +2461,7 @@ Bu geri alınamaz! Delete messages Mesajları sil - No comment provided by engineer. + alert button Delete messages after @@ -1850,9 +2478,9 @@ Bu geri alınamaz! Eski veritabanı silinsin mi? No comment provided by engineer. - - Delete pending connection - Bekleyen bağlantıyı sil + + Delete or moderate up to 200 messages. + 200'e kadar mesajı silin veya düzenleyin. No comment provided by engineer. @@ -1870,11 +2498,30 @@ Bu geri alınamaz! Sırayı sil server test step + + Delete report + No comment provided by engineer. + + + Delete up to 20 messages at once. + Tek seferde en fazla 20 mesaj silin. + No comment provided by engineer. + Delete user profile? Kullanıcı profili silinsin mi? No comment provided by engineer. + + Delete without notification + Bildirim göndermeden sil + No comment provided by engineer. + + + Deleted + Silindi + No comment provided by engineer. + Deleted at de silindi @@ -1885,6 +2532,15 @@ Bu geri alınamaz! %@ de silindi copied message info + + Deletion errors + Silme hatası + No comment provided by engineer. + + + Delivered even when Apple drops them. + No comment provided by engineer. + Delivery Teslimat @@ -1920,11 +2576,41 @@ Bu geri alınamaz! Bilgisayar cihazları No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + Hedef sunucu adresi %@, yönlendirme sunucusu %@ ayarlarıyla uyumlu değil. + No comment provided by engineer. + + + Destination server error: %@ + Hedef sunucu hatası: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + Hedef sunucu %@ sürümü, yönlendirme sunucusu %@ ile uyumlu değil. + No comment provided by engineer. + + + Detailed statistics + Detaylı istatistikler + No comment provided by engineer. + + + Details + Detaylar + No comment provided by engineer. + Develop Geliştir No comment provided by engineer. + + Developer options + Geliştirici seçenekleri + No comment provided by engineer. + Developer tools Geliştirici araçları @@ -1955,8 +2641,13 @@ Bu geri alınamaz! Doğrudan mesajlar chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + Üyeler arası doğrudan mesajlar bu sohbette yasaktır. + No comment provided by engineer. + + + Direct messages between members are prohibited. Bu grupta üyeler arasında direkt mesajlaşma yasaktır. No comment provided by engineer. @@ -1970,11 +2661,24 @@ Bu geri alınamaz! SimpleX Kilidini devre dışı bırak authentication reason + + Disable automatic message deletion? + alert title + + + Disable delete messages + alert button + Disable for all Herkes için devre dışı bırak No comment provided by engineer. + + Disabled + Devre dışı + No comment provided by engineer. + Disappearing message Kaybolan mesaj @@ -1990,8 +2694,8 @@ Bu geri alınamaz! Kaybolan mesajlar bu sohbette yasaklanmış. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. Kaybolan mesajlar bu grupta yasaklanmış. No comment provided by engineer. @@ -2025,11 +2729,21 @@ Bu geri alınamaz! Yerel ağ aracılığıyla keşfet No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + Sizin veya hedef sunucunun özel yönlendirmeyi desteklememesi durumunda bile mesajları doğrudan GÖNDERMEYİN. + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. Acil aramalar için SimpleX'i KULLANMAYIN. No comment provided by engineer. + + Do NOT use private routing. + Gizli yönlendirmeyi KULLANMA. + No comment provided by engineer. + Do it later Sonra yap @@ -2040,6 +2754,15 @@ Bu geri alınamaz! Yeni üyelere geçmişi gönderme. No comment provided by engineer. + + Do not use credentials with proxy. + Kimlik bilgilerini proxy ile kullanmayın. + No comment provided by engineer. + + + Documents: + No comment provided by engineer. + Don't create address Adres oluşturma @@ -2050,18 +2773,38 @@ Bu geri alınamaz! Etkinleştirme No comment provided by engineer. + + Don't miss important messages. + No comment provided by engineer. + Don't show again Yeniden gösterme No comment provided by engineer. + + Done + No comment provided by engineer. + Downgrade and open chat Sürüm düşür ve sohbeti aç No comment provided by engineer. + + Download + İndir + alert button +chat item action + + + Download errors + İndirme hataları + No comment provided by engineer. + Download failed + Yükleme başarısız oldu No comment provided by engineer. @@ -2069,12 +2812,29 @@ Bu geri alınamaz! Dosya indir server test step + + Download files + Dosyaları indirin + alert action + + + Downloaded + İndirildi + No comment provided by engineer. + + + Downloaded files + Dosyalar İndirildi + No comment provided by engineer. + Downloading archive + Arşiv indiriliyor No comment provided by engineer. Downloading link details + Bağlantı detayları indiriliyor No comment provided by engineer. @@ -2087,6 +2847,11 @@ Bu geri alınamaz! Süre No comment provided by engineer. + + E2E encrypted notifications. + Uçtan uca şifrelenmiş bildirimler. + No comment provided by engineer. + Edit Düzenle @@ -2107,6 +2872,10 @@ Bu geri alınamaz! Etkinleştir (geçersiz kılmaları koru) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + No comment provided by engineer. + Enable SimpleX Lock SimpleX Kilidini etkinleştir @@ -2120,7 +2889,7 @@ Bu geri alınamaz! Enable automatic message deletion? Otomatik mesaj silme etkinleştirilsin mi? - No comment provided by engineer. + alert title Enable camera access @@ -2134,6 +2903,7 @@ Bu geri alınamaz! Enable in direct chats (BETA)! + Doğrudan sohbetlerde etkinleştirin (BETA)! No comment provided by engineer. @@ -2166,6 +2936,16 @@ Bu geri alınamaz! Kendini imha şifresini etkinleştir set passcode view + + Enabled + Etkin + No comment provided by engineer. + + + Enabled for + Şunlar için etkinleştirildi + No comment provided by engineer. + Encrypt Şifreleme @@ -2236,6 +3016,10 @@ Bu geri alınamaz! Şifreleme yeniden anlaşma başarısız oldu. No comment provided by engineer. + + Encryption renegotiation in progress. + No comment provided by engineer. + Enter Passcode Şifre gir @@ -2253,6 +3037,7 @@ Bu geri alınamaz! Enter passphrase + Parolayı girin No comment provided by engineer. @@ -2300,30 +3085,36 @@ Bu geri alınamaz! Adres değişikliği iptal edilirken hata oluştu No comment provided by engineer. + + Error accepting conditions + Koşulları kabul ederken hata oluştu + alert title + Error accepting contact request Bağlantı isteği kabul edilirken hata oluştu No comment provided by engineer. - - Error accessing database file - Veritabanı dosyasına erişilirken hata oluştu - No comment provided by engineer. - Error adding member(s) Üye(ler) eklenirken hata oluştu No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + Sunucu eklenirken hata oluştu + alert title Error changing address Adres değiştirilirken hata oluştu No comment provided by engineer. + + Error changing connection profile + Bağlantı profili değiştirilirken hata oluştu + No comment provided by engineer. + Error changing role Rol değiştirilirken hata oluştu @@ -2334,6 +3125,20 @@ Bu geri alınamaz! Ayar değiştirilirken hata oluştu No comment provided by engineer. + + Error changing to incognito! + Gizli moduna geçerken hata oluştu! + No comment provided by engineer. + + + Error checking token status + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + Yönlendirme sunucusu %@'ya bağlanırken hata oluştu. Lütfen daha sonra deneyin. + No comment provided by engineer. + Error creating address Adres oluşturulurken hata oluştu @@ -2349,6 +3154,10 @@ Bu geri alınamaz! Grup bağlantısı oluşturulurken hata oluştu No comment provided by engineer. + + Error creating list + alert title + Error creating member contact Kişi iletişimi oluşturulurken hata oluştu @@ -2364,6 +3173,10 @@ Bu geri alınamaz! Profil oluşturulurken hata oluştu! No comment provided by engineer. + + Error creating report + No comment provided by engineer. + Error decrypting file Dosya şifresi çözülürken hata oluştu @@ -2384,11 +3197,6 @@ Bu geri alınamaz! Bağlantı silinirken hata oluştu No comment provided by engineer. - - Error deleting contact - Kişi silinirken hata oluştu - No comment provided by engineer. - Error deleting database Veritabanı silinirken hata oluştu @@ -2411,6 +3219,7 @@ Bu geri alınamaz! Error downloading the archive + Arşiv indirilirken hata oluştu No comment provided by engineer. @@ -2433,6 +3242,11 @@ Bu geri alınamaz! Sohbet veritabanı dışa aktarılırken hata oluştu No comment provided by engineer. + + Error exporting theme: %@ + Tema dışa aktarılırken hata oluştu: %@ + No comment provided by engineer. + Error importing chat database Sohbet veritabanı içe aktarılırken hata oluştu @@ -2443,9 +3257,14 @@ Bu geri alınamaz! Gruba katılırken hata oluştu No comment provided by engineer. - - Error loading %@ servers - %@ sunucuları yüklenirken hata oluştu + + Error loading servers + Sunucular yüklenirken hata oluştu + alert title + + + Error migrating settings + Ayarlar taşınırken hata oluştu No comment provided by engineer. @@ -2456,16 +3275,34 @@ Bu geri alınamaz! Error receiving file Dosya alınırken sorun oluştu + alert title + + + Error reconnecting server + Hata, sunucuya yeniden bağlanılıyor No comment provided by engineer. + + Error reconnecting servers + Hata sunuculara yeniden bağlanılıyor + No comment provided by engineer. + + + Error registering for notifications + alert title + Error removing member Kişiyi silerken sorun oluştu No comment provided by engineer. - - Error saving %@ servers - %@ sunucuları kaydedilirken sorun oluştu + + Error reordering lists + alert title + + + Error resetting statistics + Hata istatistikler sıfırlanıyor No comment provided by engineer. @@ -2473,6 +3310,10 @@ Bu geri alınamaz! ICE sunucularını kaydedirken sorun oluştu No comment provided by engineer. + + Error saving chat list + alert title + Error saving group profile Grup profili kaydedilirken sorun oluştu @@ -2488,8 +3329,14 @@ Bu geri alınamaz! Parolayı Anahtar Zincirine kaydederken hata oluştu No comment provided by engineer. + + Error saving servers + Sunucular kaydedilirken hata oluştu + alert title + Error saving settings + Ayarlar kaydedilirken hata oluştu when migrating @@ -2532,16 +3379,25 @@ Bu geri alınamaz! Sohbet durdurulurken hata oluştu No comment provided by engineer. + + Error switching profile + Profil değiştirme sırasında hata oluştu + No comment provided by engineer. + Error switching profile! Profil değiştirilirken hata oluştu! - No comment provided by engineer. + alertTitle Error synchronizing connection Bağlantı senkronizasyonunda hata oluştu No comment provided by engineer. + + Error testing server connection + No comment provided by engineer. + Error updating group link Grup bağlantısı güncellenirken hata oluştu @@ -2552,6 +3408,11 @@ Bu geri alınamaz! Mesaj güncellenirken hata oluştu No comment provided by engineer. + + Error updating server + Sunucu güncellenirken hata oluştu + alert title + Error updating settings Ayarları güncellerken hata oluştu @@ -2564,10 +3425,12 @@ Bu geri alınamaz! Error uploading the archive + Arşiv yüklenirken hata oluştu No comment provided by engineer. Error verifying passphrase: + Parola doğrulanırken hata oluştu: No comment provided by engineer. @@ -2578,7 +3441,9 @@ Bu geri alınamaz! Error: %@ Hata: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2590,6 +3455,16 @@ Bu geri alınamaz! Hata: veritabanı dosyası yok No comment provided by engineer. + + Errors + Hatalar + No comment provided by engineer. + + + Errors in servers configuration. + Sunucular yapılandırılırken hatalar oluştu. + servers error + Even when disabled in the conversation. Konuşma sırasında devre dışı bırakılsa bile. @@ -2605,6 +3480,10 @@ Bu geri alınamaz! Genişlet chat item action + + Expired + token status text + Export database Veritabanını dışarı aktar @@ -2615,6 +3494,11 @@ Bu geri alınamaz! Dışarı çıkarma hatası: No comment provided by engineer. + + Export theme + Temayı dışa aktar + No comment provided by engineer. + Exported database archive. Dışarı çıkarılmış veritabanı arşivi. @@ -2622,6 +3506,7 @@ Bu geri alınamaz! Exported file doesn't exist + Dışa aktarılan dosya mevcut değil No comment provided by engineer. @@ -2639,16 +3524,65 @@ Bu geri alınamaz! Hızlı ve gönderici çevrimiçi olana kadar beklemek yok! No comment provided by engineer. + + Faster deletion of groups. + No comment provided by engineer. + Faster joining and more reliable messages. Daha hızlı katılma ve daha güvenilir mesajlar. No comment provided by engineer. + + Faster sending messages. + No comment provided by engineer. + Favorite Favori + swipe action + + + Favorites No comment provided by engineer. + + File error + Dosya hatası + file error alert title + + + File errors: +%@ + Dosya hataları: +%@ + alert message + + + File is blocked by server operator: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + Dosya bulunamadı - muhtemelen dosya silindi veya göderim iptal edildi. + file error text + + + File server error: %@ + Dosya sunucusu hatası: %@ + file error text + + + File status + Dosya durumu + No comment provided by engineer. + + + File status: %@ + Dosya durumu: %@ + copied message info + File will be deleted from servers. Dosya sunuculardan silinecek. @@ -2669,6 +3603,11 @@ Bu geri alınamaz! Dosya: %@ No comment provided by engineer. + + Files + Dosyalar + No comment provided by engineer. + Files & media Dosyalar & medya @@ -2679,11 +3618,16 @@ Bu geri alınamaz! Dosyalar ve medya chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. Dosyalar ve medya bu grupta yasaklandı. No comment provided by engineer. + + Files and media not allowed + Dosyalar ve medyaya izin verilmiyor + No comment provided by engineer. + Files and media prohibited! Dosyalar ve medya yasaklandı! @@ -2696,10 +3640,12 @@ Bu geri alınamaz! Finalize migration + Taşıma işlemini sonlandır No comment provided by engineer. Finalize migration on another device. + Taşıma işlemini başka bir cihazda sonlandırın. No comment provided by engineer. @@ -2742,11 +3688,113 @@ Bu geri alınamaz! Düzeltme grup üyesi tarafından desteklenmiyor No comment provided by engineer. + + For all moderators + No comment provided by engineer. + + + For chat profile %@: + Sohbet profili için %@: + servers error + For console Konsol için No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + Örneğin, eğer kişiniz SimpleX Sohbet sunucusundan mesajları alıyorsa, uygulamanız bu mesajları Flux sunucusundan iletecektir. + No comment provided by engineer. + + + For me + No comment provided by engineer. + + + For private routing + Gizli yönlendirme için + No comment provided by engineer. + + + For social media + Sosyal medya için + No comment provided by engineer. + + + Forward + İlet + chat item action + + + Forward %d message(s)? + %d mesaj(lar)ı iletilsin mi? + alert title + + + Forward and save messages + Mesajları ilet ve kaydet + No comment provided by engineer. + + + Forward messages + İletileri ilet + alert action + + + Forward messages without files? + Mesajlar dosyalar olmadan iletilsin mi ? + alert message + + + Forward up to 20 messages at once. + Aynı anda en fazla 20 mesaj iletin. + No comment provided by engineer. + + + Forwarded + İletildi + No comment provided by engineer. + + + Forwarded from + Şuradan iletildi + No comment provided by engineer. + + + Forwarding %lld messages + %lld mesajlarını ilet + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + Yönlendirme sunucusu %@, hedef sunucu %@'ya bağlanamadı. Lütfen daha sonra deneyin. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + Yönlendirme sunucusu adresi ağ ayarlarıyla uyumsuz: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + Yönlendirme sunucusu sürümü ağ ayarlarıyla uyumsuz: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + Yönlendirme sunucusu: %1$@ +Hedef sunucu hatası: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + Yönlendirme sunucusu: %1$@ +Hata: %2$@ + snd error text + Found desktop Bilgisayar bulundu @@ -2767,11 +3815,6 @@ Bu geri alınamaz! Bütün isim (opsiyonel) No comment provided by engineer. - - Full name: - Bütün isim: - No comment provided by engineer. - Fully decentralized – visible only to members. Tamamiyle merkezi olmayan - sadece kişilere görünür. @@ -2792,6 +3835,20 @@ Bu geri alınamaz! GİFler ve çıkartmalar No comment provided by engineer. + + Get notified when mentioned. + No comment provided by engineer. + + + Good afternoon! + İyi öğlenler! + message preview + + + Good morning! + Günaydın! + message preview + Group Grup @@ -2847,36 +3904,6 @@ Bu geri alınamaz! Grup bağlantıları No comment provided by engineer. - - Group members can add message reactions. - Grup üyeleri mesaj tepkileri ekleyebilir. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Grup üyeleri, gönderilen mesajları kalıcı olarak silebilir. (24 saat içinde) - No comment provided by engineer. - - - Group members can send direct messages. - Grup üyeleri doğrudan mesajlar gönderebilir. - No comment provided by engineer. - - - Group members can send disappearing messages. - Grup üyeleri kaybolan mesajlar gönderebilir. - No comment provided by engineer. - - - Group members can send files and media. - Grup üyeleri dosyalar ve medya gönderebilir. - No comment provided by engineer. - - - Group members can send voice messages. - Grup üyeleri sesli mesajlar gönderebilir. - No comment provided by engineer. - Group message: Grup mesajı: @@ -2917,11 +3944,19 @@ Bu geri alınamaz! Grup senden silinecektir - bu geri alınamaz! No comment provided by engineer. + + Groups + No comment provided by engineer. + Help Yardım No comment provided by engineer. + + Help admins moderating their groups. + No comment provided by engineer. + Hidden Gizlenmiş @@ -2972,10 +4007,19 @@ Bu geri alınamaz! SimpleX nasıl çalışır No comment provided by engineer. + + How it affects privacy + Gizliliğinizi nasıl etkiler + No comment provided by engineer. + + + How it helps privacy + Gizliliğinizi nasıl arttırır + No comment provided by engineer. + How it works - Nasıl çalışıyor - No comment provided by engineer. + alert button How to @@ -2994,6 +4038,7 @@ Bu geri alınamaz! Hungarian interface + Macarca arayüz No comment provided by engineer. @@ -3001,6 +4046,11 @@ Bu geri alınamaz! ICE sunucuları (her satıra bir tane) No comment provided by engineer. + + IP address + IP adresi + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Eğer onunla buluşamıyorsan görüntülü aramada QR kod göster veya bağlantığı paylaş. @@ -3041,8 +4091,8 @@ Bu geri alınamaz! Hemen No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Spam ve kötüye kullanıma karşı bağışıklı No comment provided by engineer. @@ -3063,10 +4113,24 @@ Bu geri alınamaz! Import failed + İçe aktarma başarısız oldu + No comment provided by engineer. + + + Import theme + Temayı içe aktar No comment provided by engineer. Importing archive + Arşiv içe aktarılıyor + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + İyileştirilmiş teslimat, azaltılmış trafik kullanımı. +Daha fazla iyileştirme yakında geliyor! No comment provided by engineer. @@ -3086,6 +4150,7 @@ Bu geri alınamaz! In order to continue, chat should be stopped. + Devam etmek için sohbetin durdurulması gerekiyor. No comment provided by engineer. @@ -3093,6 +4158,19 @@ Bu geri alınamaz! Cevap olarak No comment provided by engineer. + + In-call sounds + Arama içi sesler + No comment provided by engineer. + + + Inappropriate content + report reason + + + Inappropriate profile + report reason + Incognito Gizli @@ -3163,6 +4241,11 @@ Bu geri alınamaz! [Terminal için SimpleX Chat]i indir(https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Anında + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3170,16 +4253,36 @@ Bu geri alınamaz! No comment provided by engineer. - - Instantly - Anında - No comment provided by engineer. - Interface Arayüz No comment provided by engineer. + + Interface colors + Arayüz renkleri + No comment provided by engineer. + + + Invalid + token status text + + + Invalid (bad token) + token status text + + + Invalid (expired) + token status text + + + Invalid (unregistered) + token status text + + + Invalid (wrong topic) + token status text + Invalid QR code Geçersiz QR kodu @@ -3202,6 +4305,7 @@ Bu geri alınamaz! Invalid migration confirmation + Geçersiz taşıma onayı No comment provided by engineer. @@ -3217,7 +4321,7 @@ Bu geri alınamaz! Invalid server address! Geçersiz sunucu adresi! - No comment provided by engineer. + alert title Invalid status @@ -3239,6 +4343,11 @@ Bu geri alınamaz! Üyeleri davet et No comment provided by engineer. + + Invite to chat + Sohbete davet et + No comment provided by engineer. + Invite to group Gruba davet et @@ -3254,8 +4363,8 @@ Bu geri alınamaz! Bu sohbette geri döndürülemez mesaj silme yasaktır. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. Bu grupta geri döndürülemez mesaj silme yasaktır. No comment provided by engineer. @@ -3280,6 +4389,11 @@ Bu geri alınamaz! 3. Bağlantı tehlikeye girmiştir. No comment provided by engineer. + + It protects your IP address and connections. + IP adresinizi ve bağlantılarınızı korur. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Bu bağlantı üzerinden zaten bağlanmışsınız gibi görünüyor. Eğer durum böyle değilse, bir hata oluştu (%@). @@ -3298,7 +4412,7 @@ Bu geri alınamaz! Join Katıl - No comment provided by engineer. + swipe action Join group @@ -3340,6 +4454,11 @@ Bu senin grup için bağlantın %@! Keep Tut + alert action + + + Keep conversation + Sohbeti sakla No comment provided by engineer. @@ -3350,7 +4469,7 @@ Bu senin grup için bağlantın %@! Keep unused invitation? Kullanılmamış davet tutulsun mu? - No comment provided by engineer. + alert title Keep your connections @@ -3385,6 +4504,16 @@ Bu senin grup için bağlantın %@! Leave Ayrıl + swipe action + + + Leave chat + Sohbetten ayrıl + No comment provided by engineer. + + + Leave chat? + Sohbetten ayrılsın mı? No comment provided by engineer. @@ -3427,6 +4556,18 @@ Bu senin grup için bağlantın %@! Bağlanmış bilgisayarlar No comment provided by engineer. + + List + swipe action + + + List name and emoji should be different for all lists. + No comment provided by engineer. + + + List name... + No comment provided by engineer. + Live message! Canlı mesaj! @@ -3437,11 +4578,6 @@ Bu senin grup için bağlantın %@! Canlı mesajlar No comment provided by engineer. - - Local - Yerel - No comment provided by engineer. - Local name Yerel isim @@ -3462,11 +4598,6 @@ Bu senin grup için bağlantın %@! Kilit modu No comment provided by engineer. - - Make a private connection - Gizli bir bağlantı oluştur - No comment provided by engineer. - Make one message disappear Bir mesajın kaybolmasını sağlayın @@ -3477,21 +4608,11 @@ Bu senin grup için bağlantın %@! Profili gizli yap! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - %@ sunucu adreslerinin doğru formatta olduğundan, satır ayrımı yapıldığından ve yinelenmediğinden (%@) emin olun. - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. WebRTC ICE sunucu adreslerinin doğru formatta olduğundan, satırlara ayrıldığından ve yinelenmediğinden emin olun. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Çoğu kişi sordu: *eğer SimpleX'in hiç kullanıcı tanımlayıcıları yok, o zaman mesajları nasıl gönderebiliyor?* - No comment provided by engineer. - Mark deleted for everyone Herkes için silinmiş olarak işaretle @@ -3517,11 +4638,35 @@ Bu senin grup için bağlantın %@! Maksimum 30 saniye, anında alındı. No comment provided by engineer. + + Media & file servers + Medya ve dosya sunucuları + No comment provided by engineer. + + + Medium + Orta + blur media + Member Kişi No comment provided by engineer. + + Member inactive + Üye inaktif + item status text + + + Member reports + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + Üye rolü "%@" olarak değiştirilecektir. Tüm sohbet üyeleri bilgilendirilecektir. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. Üye rolü "%@" olarak değiştirilecektir. Ve tüm grup üyeleri bilgilendirilecektir. @@ -3532,11 +4677,63 @@ Bu senin grup için bağlantın %@! Üye rolü "%@" olarak değiştirilecektir. Ve üye yeni bir davetiye alacaktır. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! Üye gruptan çıkarılacaktır - bu geri alınamaz! No comment provided by engineer. + + Members can add message reactions. + Grup üyeleri mesaj tepkileri ekleyebilir. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + Grup üyeleri, gönderilen mesajları kalıcı olarak silebilir. (24 saat içinde) + No comment provided by engineer. + + + Members can report messsages to moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + Grup üyeleri SimpleX bağlantıları gönderebilir. + No comment provided by engineer. + + + Members can send direct messages. + Grup üyeleri doğrudan mesajlar gönderebilir. + No comment provided by engineer. + + + Members can send disappearing messages. + Grup üyeleri kaybolan mesajlar gönderebilir. + No comment provided by engineer. + + + Members can send files and media. + Grup üyeleri dosyalar ve medya gönderebilir. + No comment provided by engineer. + + + Members can send voice messages. + Grup üyeleri sesli mesajlar gönderebilir. + No comment provided by engineer. + + + Mention members 👋 + No comment provided by engineer. + + + Menus + Menüler + No comment provided by engineer. + Message delivery error Mesaj gönderim hatası @@ -3547,11 +4744,31 @@ Bu senin grup için bağlantın %@! Mesaj alındı bilgisi! No comment provided by engineer. + + Message delivery warning + Mesaj iletimi uyarısı + item status text + Message draft Mesaj taslağı No comment provided by engineer. + + Message forwarded + Mesaj iletildi + item status text + + + Message may be delivered later if member becomes active. + Kullanıcı aktif olursa mesaj iletilebilir. + item status description + + + Message queue info + Mesaj kuyruğu bilgisi + No comment provided by engineer. + Message reactions Mesaj tepkileri @@ -3562,11 +4779,41 @@ Bu senin grup için bağlantın %@! Mesaj tepkileri bu sohbette yasaklandı. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. Mesaj tepkileri bu grupta yasaklandı. No comment provided by engineer. + + Message reception + Mesaj alındısı + No comment provided by engineer. + + + Message servers + Mesaj sunucuları + No comment provided by engineer. + + + Message shape + Mesaj şekli + No comment provided by engineer. + + + Message source remains private. + Mesaj kaynağı gizli kalır. + No comment provided by engineer. + + + Message status + Mesaj durumu + No comment provided by engineer. + + + Message status: %@ + Mesaj durumu: %@ + copied message info + Message text Mesaj yazısı @@ -3574,6 +4821,7 @@ Bu senin grup için bağlantın %@! Message too large + Mesaj çok büyük No comment provided by engineer. @@ -3591,36 +4839,63 @@ Bu senin grup için bağlantın %@! %@ den gelen mesajlar gösterilecektir! No comment provided by engineer. + + Messages in this chat will never be deleted. + alert message + + + Messages received + Mesajlar alındı + No comment provided by engineer. + + + Messages sent + Mesajlar gönderildi + No comment provided by engineer. + + + Messages were deleted after you selected them. + Mesajlar siz seçtikten sonra silindi. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + Mesajlar, dosyalar ve aramalar **uçtan uca şifreleme** ile mükemmel ileri gizlilik, inkar ve izinsiz giriş kurtarma ile korunur. No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + Mesajlar, dosyalar ve aramalar **kuantum dirençli e2e şifreleme** ile mükemmel ileri gizlilik, inkar ve zorla girme kurtarma ile korunur. No comment provided by engineer. Migrate device + Cihazı taşıma No comment provided by engineer. Migrate from another device + Başka bir cihazdan geçiş yapın No comment provided by engineer. Migrate here + Buraya göç edin No comment provided by engineer. Migrate to another device + Başka bir cihaza taşıma No comment provided by engineer. Migrate to another device via QR code. + QR kodu aracılığıyla başka bir cihaza geçiş yapın. No comment provided by engineer. Migrating + Göçmenlik No comment provided by engineer. @@ -3630,6 +4905,7 @@ Bu senin grup için bağlantın %@! Migration complete + Geçiş tamamlandı No comment provided by engineer. @@ -3647,9 +4923,9 @@ Bu senin grup için bağlantın %@! Geçiş tamamlandı No comment provided by engineer. - - Migrations: %@ - Geçişler: %@ + + Migrations: + Geçişler: No comment provided by engineer. @@ -3667,21 +4943,29 @@ Bu senin grup için bağlantın %@! %@ de yönetildi copied message info + + More + swipe action + More improvements are coming soon! Daha fazla geliştirmeler yakında geliyor! No comment provided by engineer. + + More reliable network connection. + Daha güvenilir ağ bağlantısı. + No comment provided by engineer. + + + More reliable notifications + No comment provided by engineer. + Most likely this connection is deleted. Büyük ihtimalle bu bağlantı silinmiş. item status description - - Most likely this contact has deleted the connection with you. - Büyük ihtimalle bu kişi seninle bağlantını sildi. - No comment provided by engineer. - Multiple chat profiles Çoklu sohbet profili @@ -3690,7 +4974,11 @@ Bu senin grup için bağlantın %@! Mute Sustur - No comment provided by engineer. + notification label action + + + Mute all + notification label action Muted when inactive! @@ -3700,13 +4988,36 @@ Bu senin grup için bağlantın %@! Name İsim - No comment provided by engineer. + swipe action Network & servers Ağ & sunucular No comment provided by engineer. + + Network connection + Ağ bağlantısı + No comment provided by engineer. + + + Network decentralization + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + Ağ sorunları - birçok gönderme denemesinden sonra mesajın süresi doldu. + snd error text + + + Network management + Ağ yönetimi + No comment provided by engineer. + + + Network operator + No comment provided by engineer. + Network settings Ağ ayarları @@ -3717,16 +5028,35 @@ Bu senin grup için bağlantın %@! Ağ durumu No comment provided by engineer. + + New + token status text + New Passcode Yeni şifre No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Uygulamayı her başlattığınızda yeni SOCKS kimlik bilgileri kullanılacaktır. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Her sunucu için yeni SOCKS kimlik bilgileri kullanılacaktır. + No comment provided by engineer. + New chat Yeni sohbet No comment provided by engineer. + + New chat experience 🎉 + Yeni bir sohbet deneyimi 🎉 + No comment provided by engineer. + New contact request Yeni bağlantı isteği @@ -3737,11 +5067,6 @@ Bu senin grup için bağlantın %@! Yeni kişi: notification - - New database archive - Yeni veritabanı arşivi - No comment provided by engineer. - New desktop app! Yeni bilgisayar uygulaması! @@ -3752,11 +5077,20 @@ Bu senin grup için bağlantın %@! Yeni görünen ad No comment provided by engineer. + + New events + notification + New in %@ %@ da yeni No comment provided by engineer. + + New media options + Yeni medya seçenekleri + No comment provided by engineer. + New member role Yeni üye rolü @@ -3772,6 +5106,10 @@ Bu senin grup için bağlantın %@! Yeni parola… No comment provided by engineer. + + New server + No comment provided by engineer. + No Hayır @@ -3782,6 +5120,18 @@ Bu senin grup için bağlantın %@! Uygulama şifresi yok Authentication unavailable + + No chats + No comment provided by engineer. + + + No chats found + No comment provided by engineer. + + + No chats in list %@ + No comment provided by engineer. + No contacts selected Hiçbir kişi seçilmedi @@ -3802,6 +5152,11 @@ Bu senin grup için bağlantın %@! Cihaz tokeni yok! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + Henüz direkt bağlantı yok mesaj admin tarafından yönlendirildi. + item status description + No filtered chats Filtrelenmiş sohbetler yok @@ -3817,21 +5172,101 @@ Bu senin grup için bağlantın %@! Geçmiş yok No comment provided by engineer. + + No info, try to reload + Bilgi yok, yenilemeyi deneyin + No comment provided by engineer. + + + No media & file servers. + servers error + + + No message + No comment provided by engineer. + + + No message servers. + servers error + + + No network connection + Ağ bağlantısı yok + No comment provided by engineer. + + + No permission to record speech + Konuşma kaydetme izni yok + No comment provided by engineer. + + + No permission to record video + Video kaydı için izin yok + No comment provided by engineer. + No permission to record voice message Sesli mesaj kaydetmek için izin yok No comment provided by engineer. + + No push server + Yerel + No comment provided by engineer. + No received or sent files Hiç alınmış veya gönderilmiş dosya yok No comment provided by engineer. + + No servers for private message routing. + servers error + + + No servers to receive files. + servers error + + + No servers to receive messages. + servers error + + + No servers to send files. + servers error + + + No token! + alert title + + + No unread chats + No comment provided by engineer. + + + No user identifiers. + Herhangi bir kullanıcı tanımlayıcısı yok. + No comment provided by engineer. + Not compatible! Uyumlu değil! No comment provided by engineer. + + Notes + No comment provided by engineer. + + + Nothing selected + Hiçbir şey seçilmedi + No comment provided by engineer. + + + Nothing to forward! + Yönlendirilecek bir şey yok! + alert title + Notifications Bildirimler @@ -3842,6 +5277,18 @@ Bu senin grup için bağlantın %@! Bildirimler devre dışı! No comment provided by engineer. + + Notifications error + alert title + + + Notifications privacy + No comment provided by engineer. + + + Notifications status + alert title + Now admins can: - delete members' messages. @@ -3859,36 +5306,35 @@ Bu senin grup için bağlantın %@! Off Kapalı - No comment provided by engineer. + blur media Ok Tamam - No comment provided by engineer. + alert button Old database Eski veritabanı No comment provided by engineer. - - Old database archive - Eski veritabanı arşivi - No comment provided by engineer. - One-time invitation link Tek zamanlı bağlantı daveti No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Bağlantı için Onion ana bilgisayarları gerekecektir. VPN'nin etkinleştirilmesi gerekir. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Bağlantı için Onion ana bilgisayarları gerekecektir. +VPN'nin etkinleştirilmesi gerekir. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Onion ana bilgisayarları mevcutsa kullanılacaktır. VPN'nin etkinleştirilmesi gerekir. + + Onion hosts will be used when available. +Requires compatible VPN. + Onion ana bilgisayarları mevcutsa kullanılacaktır. +VPN'nin etkinleştirilmesi gerekir. No comment provided by engineer. @@ -3896,11 +5342,20 @@ Bu senin grup için bağlantın %@! Onion ana bilgisayarları kullanılmayacaktır. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. Yalnızca istemci cihazlar kullanıcı profillerini, kişileri, grupları ve **2 katmanlı uçtan uca şifreleme** ile gönderilen mesajları depolar. No comment provided by engineer. + + Only delete conversation + Sadece sohbeti sil + No comment provided by engineer. + Only group owners can change group preferences. Grup tercihlerini yalnızca grup sahipleri değiştirebilir. @@ -3916,6 +5371,14 @@ Bu senin grup için bağlantın %@! Yalnızca grup sahipleri sesli mesajları etkinleştirebilir. No comment provided by engineer. + + Only sender and moderators see it + No comment provided by engineer. + + + Only you and moderators see it + No comment provided by engineer. + Only you can add message reactions. Sadece siz mesaj tepkileri ekleyebilirsiniz. @@ -3969,13 +5432,17 @@ Bu senin grup için bağlantın %@! Open - No comment provided by engineer. + alert action Open Settings Ayarları aç No comment provided by engineer. + + Open changes + No comment provided by engineer. + Open chat Sohbeti aç @@ -3986,32 +5453,44 @@ Bu senin grup için bağlantın %@! Sohbet konsolunu aç authentication reason + + Open conditions + No comment provided by engineer. + Open group Grubu aç No comment provided by engineer. + + Open link? + alert title + Open migration to another device + Başka bir cihaza açık geçiş authentication reason - - Open user profiles - Kullanıcı profillerini aç - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Açık kaynak protokolü ve kodu - herhangi biri sunucuları çalıştırabilir. - No comment provided by engineer. - Opening app… Uygulama açılıyor… No comment provided by engineer. + + Operator + No comment provided by engineer. + + + Operator server + alert title + + + Or import archive file + No comment provided by engineer. + Or paste archive link + Veya arşiv bağlantısını yapıştırın No comment provided by engineer. @@ -4021,6 +5500,7 @@ Bu senin grup için bağlantın %@! Or securely share this file link + Veya bu dosya bağlantısını güvenli bir şekilde paylaşın No comment provided by engineer. @@ -4028,6 +5508,26 @@ Bu senin grup için bağlantın %@! Veya bu kodu göster No comment provided by engineer. + + Or to share privately + No comment provided by engineer. + + + Organize chats into lists + No comment provided by engineer. + + + Other + Diğer + No comment provided by engineer. + + + Other file errors: +%@ + Diğer dosya hataları: +%@ + alert message + PING count PING sayısı @@ -4063,6 +5563,11 @@ Bu senin grup için bağlantın %@! Şifre ayarlandı! No comment provided by engineer. + + Password + Şifre + No comment provided by engineer. + Password to show Gösterilecek şifre @@ -4093,13 +5598,13 @@ Bu senin grup için bağlantın %@! Aldığın bağlantıyı yapıştır No comment provided by engineer. - - People can connect to you only via the links you share. - İnsanlar size yalnızca paylaştığınız bağlantılar üzerinden ulaşabilir. + + Pending + Bekleniyor No comment provided by engineer. - - Periodically + + Periodic Periyodik olarak No comment provided by engineer. @@ -4110,6 +5615,17 @@ Bu senin grup için bağlantın %@! Picture-in-picture calls + Resim içinde resim aramaları + No comment provided by engineer. + + + Play from the chat list. + Sohbet listesinden oynat. + No comment provided by engineer. + + + Please ask your contact to enable calls. + Lütfen kişinizden çağrılara izin vermesini isteyin. No comment provided by engineer. @@ -4117,6 +5633,13 @@ Bu senin grup için bağlantın %@! Lütfen konuştuğunuz kişiden sesli mesaj göndermeyi etkinleştirmesini isteyin. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + Lütfen telefonun ve bilgisayarın aynı lokal ağa bağlı olduğundan ve bilgisayar güvenlik duvarının bağlantıya izin verdiğinden emin olun. +Lütfen diğer herhangi bir sorunu geliştiricilerle paylaşın. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Lütfen doğru bağlantıyı kullandığınızı kontrol edin veya kişiden size başka bir bağlantı göndermesini isteyin. @@ -4134,6 +5657,7 @@ Bu senin grup için bağlantın %@! Please confirm that network settings are correct for this device. + Lütfen bu cihaz için ağ ayarlarının doğru olduğunu onaylayın. No comment provided by engineer. @@ -4183,60 +5707,113 @@ Hata: %@ Lütfen parolayı güvenli bir şekilde saklayın, kaybederseniz parolayı DEĞİŞTİREMEZSİNİZ. No comment provided by engineer. + + Please try to disable and re-enable notfications. + token info + + + Please wait for token activation to complete. + token info + + + Please wait for token to be registered. + token info + Polish interface Lehçe arayüz No comment provided by engineer. + + Port + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Muhtemelen, sunucu adresindeki parmakizi sertifikası doğru değil server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Son mesaj taslağını ekleriyle birlikte koru. No comment provided by engineer. - - Preset server - Ön ayarlı sunucu - No comment provided by engineer. - Preset server address Ön ayarlı sunucu adresi No comment provided by engineer. + + Preset servers + No comment provided by engineer. + Preview Ön izleme No comment provided by engineer. + + Previously connected servers + Önceden bağlanılmış sunucular + No comment provided by engineer. + Privacy & security Gizlilik & güvenlik No comment provided by engineer. + + Privacy for your customers. + No comment provided by engineer. + + + Privacy policy and conditions of use. + No comment provided by engineer. + Privacy redefined Gizlilik yeniden tanımlandı No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + No comment provided by engineer. + Private filenames Gizli dosya adları No comment provided by engineer. + + Private media file names. + No comment provided by engineer. + + + Private message routing + Gizli mesaj yönlendirme + No comment provided by engineer. + + + Private message routing 🚀 + Gizli mesaj yönlendirme 🚀 + No comment provided by engineer. + Private notes Gizli notlar name of notes to self + + Private routing + Gizli yönlendirme + No comment provided by engineer. + + + Private routing error + Gizli yönlendirme hatası + No comment provided by engineer. + Profile and server connections Profil ve sunucu bağlantıları @@ -4247,14 +5824,9 @@ Hata: %@ Profil fotoğrafı No comment provided by engineer. - - Profile name - Profil ismi - No comment provided by engineer. - - - Profile name: - Profil ismi: + + Profile images + Profil resimleri No comment provided by engineer. @@ -4262,10 +5834,15 @@ Hata: %@ Profil parolası No comment provided by engineer. + + Profile theme + Profil teması + No comment provided by engineer. + Profile update will be sent to your contacts. Profil güncellemesi kişilerinize gönderilecektir. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4287,9 +5864,18 @@ Hata: %@ Mesajlarda tepkileri yasakla. No comment provided by engineer. + + Prohibit reporting messages to moderators. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + SimpleX bağlantısı gönderimini yasakla. + No comment provided by engineer. + Prohibit sending direct messages to members. - Geri dönülmez mesaj silme işlemini yasakla. + Üyelere doğrudan mesaj göndermeyi yasakla. No comment provided by engineer. @@ -4307,11 +5893,23 @@ Hata: %@ Sesli mesajların gönderimini yasakla. No comment provided by engineer. + + Protect IP address + IP adresini koru + No comment provided by engineer. + Protect app screen Uygulama ekranını koru No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + IP adresinizi kişileriniz tarafından seçilen mesajlaşma yönlendiricilerinden koruyun. +*Ağ ve sunucular* ayarlarında etkinleştirin. + No comment provided by engineer. + Protect your chat profiles with a password! Bir parolayla birlikte sohbet profillerini koru! @@ -4327,6 +5925,21 @@ Hata: %@ KB başına protokol zaman aşımı No comment provided by engineer. + + Proxied + Proxyli + No comment provided by engineer. + + + Proxied servers + Proxy sunucuları + No comment provided by engineer. + + + Proxy requires password + Proxy şifre gerektirir + No comment provided by engineer. + Push notifications Anında bildirimler @@ -4334,10 +5947,12 @@ Hata: %@ Push server + Push sunucu No comment provided by engineer. Quantum resistant encryption + Kuantum dirençli şifreleme No comment provided by engineer. @@ -4345,6 +5960,11 @@ Hata: %@ Uygulamayı değerlendir No comment provided by engineer. + + Reachable chat toolbar + Erişilebilir sohbet araç çubuğu + No comment provided by engineer. + React… Tepki ver… @@ -4353,33 +5973,28 @@ Hata: %@ Read Oku - No comment provided by engineer. + swipe action Read more Dahasını oku No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - [Kullanıcı Rehberi]nde daha fazlasını okuyun(https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). [Kullanıcı Rehberi]nde daha fazlasını okuyun(https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + [Kullanıcı Rehberi]nde daha fazlasını okuyun(https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). [Kullanıcı Rehberi]nde daha fazlasını okuyun(https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - - Read more in our GitHub repository. - Daha fazlasını GitHub depomuzdan oku. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). [GitHub deposu]nda daha fazlasını okuyun(https://github.com/simplex-chat/simplex-chat#readme). @@ -4387,7 +6002,12 @@ Hata: %@ Receipts are disabled - Görüldü devre dışı bırakıldı + Alıcılar devre dışı bırakıldı + No comment provided by engineer. + + + Receive errors + Alım sırasında hata No comment provided by engineer. @@ -4410,6 +6030,21 @@ Hata: %@ Mesaj alındı message info title + + Received messages + Alınan mesajlar + No comment provided by engineer. + + + Received reply + Alınan cevap + No comment provided by engineer. + + + Received total + Toplam alınan + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. Alıcı adresi farklı bir sunucuya değiştirilecektir. Gönderici çevrimiçi olduktan sonra adres değişikliği tamamlanacaktır. @@ -4430,16 +6065,46 @@ Hata: %@ Yakın geçmiş ve geliştirilmiş [dizin botu](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex. im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. + + Recipient(s) can't see who this message is from. + Alıcı(lar) bu mesajın kimden geldiğini göremez. + No comment provided by engineer. + Recipients see updates as you type them. Alıcılar yazdığına göre güncellemeleri görecektir. No comment provided by engineer. + + Reconnect + Yeniden bağlan + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Mesaj teslimini zorlamak için bağlı tüm sunucuları yeniden bağlayın. Ek trafik kullanır. No comment provided by engineer. + + Reconnect all servers + Tüm sunuculara yeniden bağlan + No comment provided by engineer. + + + Reconnect all servers? + Tüm sunuculara yeniden bağlansın mı? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + Mesajı göndermeye zorlamak için sunucuya yeniden bağlan. Bu ekstra internet kullanır. + No comment provided by engineer. + + + Reconnect server? + Sunucuya yeniden bağlansın mı ? + No comment provided by engineer. + Reconnect servers? Sunuculara yeniden bağlanılsın mı? @@ -4460,10 +6125,23 @@ Hata: %@ Azaltılmış pil kullanımı No comment provided by engineer. + + Register + No comment provided by engineer. + + + Register notification token? + token info + + + Registered + token status text + Reject Reddet - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4477,12 +6155,12 @@ Hata: %@ Relay server is only used if necessary. Another party can observe your IP address. - Aktarma sunucusu yalnızca gerekli olduğunda kullanılır. Başka bir taraf IP adresinizi gözlemleyebilir. + Yönlendirici sunucusu yalnızca gerekli olduğunda kullanılır. Başka bir taraf IP adresinizi gözlemleyebilir. No comment provided by engineer. Relay server protects your IP address, but it can observe the duration of the call. - Aktarıcı sunucu IP adresinizi korur, ancak aramanın süresini gözlemleyebilir. + Yönlendirici sunucu IP adresinizi korur, ancak aramanın süresini gözlemleyebilir. No comment provided by engineer. @@ -4490,6 +6168,16 @@ Hata: %@ Sil No comment provided by engineer. + + Remove archive? + Arşiv kaldırılsın mı ? + No comment provided by engineer. + + + Remove image + Resmi kaldır + No comment provided by engineer. + Remove member Kişiyi sil @@ -4527,10 +6215,12 @@ Hata: %@ Repeat download + Tekrar indir No comment provided by engineer. Repeat import + İthalatı tekrarla No comment provided by engineer. @@ -4540,6 +6230,7 @@ Hata: %@ Repeat upload + Yüklemeyi tekrarla No comment provided by engineer. @@ -4547,6 +6238,46 @@ Hata: %@ Yanıtla chat item action + + Report + chat item action + + + Report content: only group moderators will see it. + report reason + + + Report member profile: only group moderators will see it. + report reason + + + Report other: only group moderators will see it. + report reason + + + Report reason? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + report reason + + + Report violation: only group moderators will see it. + report reason + + + Report: %@ + report in notification + + + Reporting messages to moderators is prohibited. + No comment provided by engineer. + + + Reports + No comment provided by engineer. + Required Gerekli @@ -4557,16 +6288,41 @@ Hata: %@ Sıfırla No comment provided by engineer. + + Reset all hints + Tüm ip uçlarını sıfırla + No comment provided by engineer. + + + Reset all statistics + Tüm istatistikleri sıfırla + No comment provided by engineer. + + + Reset all statistics? + Tüm istatistikler sıfırlansın mı ? + No comment provided by engineer. + Reset colors Renkleri sıfırla No comment provided by engineer. + + Reset to app theme + Uygulama temasına sıfırla + No comment provided by engineer. + Reset to defaults Varsayılanlara sıfırla No comment provided by engineer. + + Reset to user theme + Kullanıcı temasına sıfırla + No comment provided by engineer. + Restart the app to create a new chat profile Yeni bir sohbet profili oluşturmak için uygulamayı yeniden başlatın @@ -4607,9 +6363,8 @@ Hata: %@ Göster chat item action - - Revert - Geri al + + Review conditions No comment provided by engineer. @@ -4637,55 +6392,66 @@ Hata: %@ Sohbeti çalıştır No comment provided by engineer. - - SMP servers - SMP sunucuları + + SMP server + SMP sunucusu + No comment provided by engineer. + + + SOCKS proxy + SOCKS vekili + No comment provided by engineer. + + + Safely receive files + Dosyaları güvenle alın No comment provided by engineer. Safer groups + Daha güvenli gruplar No comment provided by engineer. Save Kaydet - chat item action + alert button +chat item action Save (and notify contacts) Kaydet (ve kişilere bildir) - No comment provided by engineer. + alert button Save and notify contact Kaydet ve kişilere bildir - No comment provided by engineer. + alert button Save and notify group members Kaydet ve grup üyelerine bildir No comment provided by engineer. + + Save and reconnect + Kayıt et ve yeniden bağlan + No comment provided by engineer. + Save and update group profile Kaydet ve grup profilini güncelle No comment provided by engineer. - - Save archive - Arşivi kaydet - No comment provided by engineer. - - - Save auto-accept settings - Otomatik kabul et ayarlarını kaydet - No comment provided by engineer. - Save group profile Grup profilini kaydet No comment provided by engineer. + + Save list + No comment provided by engineer. + Save passphrase and open chat Parolayı kaydet ve sohbeti aç @@ -4699,7 +6465,7 @@ Hata: %@ Save preferences? Tercihler kaydedilsin mi? - No comment provided by engineer. + alert title Save profile password @@ -4714,28 +6480,53 @@ Hata: %@ Save servers? Sunucular kaydedilsin mi? - No comment provided by engineer. - - - Save settings? - Ayarlar kaydedilsin mi? - No comment provided by engineer. + alert title Save welcome message? Hoşgeldin mesajı kaydedilsin mi? No comment provided by engineer. + + Save your profile? + Profiliniz kaydedilsin mi? + alert title + + + Saved + Kaydedildi + No comment provided by engineer. + Saved WebRTC ICE servers will be removed Kaydedilmiş WebRTC ICE sunucuları silinecek No comment provided by engineer. + + Saved from + Tarafından kaydedildi + No comment provided by engineer. + Saved message Kaydedilmiş mesaj message info title + + Saving %lld messages + %lld mesajlarını kaydet + No comment provided by engineer. + + + Scale + Ölçeklendir + No comment provided by engineer. + + + Scan / Paste link + Tara / Bağlantı yapıştır + No comment provided by engineer. + Scan QR code QR kodu okut @@ -4776,11 +6567,21 @@ Hata: %@ Ara veya SimpleX bağlantısını yapıştır No comment provided by engineer. + + Secondary + İkincil renk + No comment provided by engineer. + Secure queue Sırayı koru server test step + + Secured + Güvenli + No comment provided by engineer. + Security assessment Güvenlik değerlendirmesi @@ -4794,6 +6595,21 @@ Hata: %@ Select Seç + chat item action + + + Select chat profile + Sohbet profili seç + No comment provided by engineer. + + + Selected %lld + Seçilen %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Seçilen sohbet tercihleri bu mesajı yasakladı. No comment provided by engineer. @@ -4831,11 +6647,6 @@ Hata: %@ Görüldü bilgilerini şuraya gönder No comment provided by engineer. - - Send direct message - Doğrudan mesaj gönder - No comment provided by engineer. - Send direct message to connect Bağlanmak için doğrudan mesaj gönder @@ -4846,6 +6657,11 @@ Hata: %@ Kaybolan bir mesaj gönder No comment provided by engineer. + + Send errors + Gönderme hataları + No comment provided by engineer. + Send link previews Bağlantı ön gösterimleri gönder @@ -4856,14 +6672,28 @@ Hata: %@ Canlı mesaj gönder No comment provided by engineer. + + Send message to enable calls. + Çağrıları aktif etmek için mesaj gönder. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + IP adresi korumalı olduğunda ve sizin veya hedef sunucunun özel yönlendirmeyi desteklemediği durumlarda mesajları doğrudan gönderin. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + Sizin veya hedef sunucunun özel yönlendirmeyi desteklemediği durumlarda mesajları doğrudan gönderin. + No comment provided by engineer. + Send notifications Bildirimler gönder No comment provided by engineer. - - Send notifications: - Bildirimler gönder: + + Send private reports No comment provided by engineer. @@ -4889,7 +6719,7 @@ Hata: %@ Sender cancelled file transfer. Gönderici dosya gönderimini iptal etti. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4903,7 +6733,7 @@ Hata: %@ Sending delivery receipts will be enabled for all contacts. - Görüldü bilgisi bütün kişileri için etkinleştirilecektir. + Tüm kişiler için iletim bilgisi gönderme özelliği etkinleştirilecek. No comment provided by engineer. @@ -4946,6 +6776,11 @@ Hata: %@ Şuradan gönderildi: %@ copied message info + + Sent directly + Direkt gönderildi + No comment provided by engineer. + Sent file event Dosya etkinliği gönderildi @@ -4956,11 +6791,67 @@ Hata: %@ Mesaj gönderildi message info title + + Sent messages + Gönderilen mesajlar + No comment provided by engineer. + Sent messages will be deleted after set time. Gönderilen mesajlar ayarlanan süreden sonra silinecektir. No comment provided by engineer. + + Sent reply + Gönderilen cevap + No comment provided by engineer. + + + Sent total + Gönderilen tüm mesajların toplamı + No comment provided by engineer. + + + Sent via proxy + Bir proxy aracılığıyla gönderildi + No comment provided by engineer. + + + Server + Sunucu + No comment provided by engineer. + + + Server added to operator %@. + alert message + + + Server address + Sunucu adresi + No comment provided by engineer. + + + Server address is incompatible with network settings. + Sunucu adresi ağ ayarlarıyla uyumlu değil. + srv error text. + + + Server address is incompatible with network settings: %@. + Sunucu adresi ağ ayarlarıyla uyumsuz: %@. + No comment provided by engineer. + + + Server operator changed. + alert title + + + Server operators + No comment provided by engineer. + + + Server protocol changed. + alert title + Server requires authorization to create queues, check password Sunucunun sıra oluşturması için yetki gereklidir, şifreyi kontrol edin @@ -4976,11 +6867,36 @@ Hata: %@ Sunucu testinde hata oluştu! No comment provided by engineer. + + Server type + Sunucu tipi + No comment provided by engineer. + + + Server version is incompatible with network settings. + Sunucu sürümü ağ ayarlarıyla uyumlu değil. + srv error text + + + Server version is incompatible with your app: %@. + Sunucu sürümü uygulamanızla uyumlu değil: %@. + No comment provided by engineer. + Servers Sunucular No comment provided by engineer. + + Servers info + Sunucu bilgileri + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + Sunucu istatistikleri sıfırlanacaktır - bu geri alınamaz! + No comment provided by engineer. + Session code Oturum kodu @@ -4991,11 +6907,20 @@ Hata: %@ 1 günlüğüne ayarla No comment provided by engineer. + + Set chat name… + No comment provided by engineer. + Set contact name… Kişi adı gir… No comment provided by engineer. + + Set default theme + Varsayılan temaya ayarla + No comment provided by engineer. + Set group preferences Grup tercihlerini ayarla @@ -5006,6 +6931,10 @@ Hata: %@ Sistem kimlik doğrulaması yerine ayarla. No comment provided by engineer. + + Set message expiration in chats. + No comment provided by engineer. + Set passcode Şifre ayarla @@ -5013,6 +6942,7 @@ Hata: %@ Set passphrase + Parolayı ayarla No comment provided by engineer. @@ -5035,24 +6965,52 @@ Hata: %@ Ayarlar No comment provided by engineer. + + Settings were changed. + Ayarlar değiştirildi. + alert message + + + Shape profile images + Profil resimlerini şekillendir + No comment provided by engineer. + Share Paylaş - chat item action + alert action +chat item action Share 1-time link Tek kullanımlık bağlantıyı paylaş No comment provided by engineer. + + Share 1-time link with a friend + No comment provided by engineer. + + + Share SimpleX address on social media. + No comment provided by engineer. + Share address Adresi paylaş No comment provided by engineer. + + Share address publicly + No comment provided by engineer. + Share address with contacts? Kişilerle adres paylaşılsın mı? + alert title + + + Share from other apps. + Diğer uygulamalardan paylaşın. No comment provided by engineer. @@ -5060,18 +7018,33 @@ Hata: %@ Bağlantıyı paylaş No comment provided by engineer. + + Share profile + Profil paylaş + No comment provided by engineer. + Share this 1-time invite link Bu tek kullanımlık bağlantı davetini paylaş No comment provided by engineer. + + Share to SimpleX + SimpleX ile paylaş + No comment provided by engineer. + Share with contacts Kişilerle paylaş No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code + QR kodunu göster No comment provided by engineer. @@ -5089,21 +7062,45 @@ Hata: %@ Son mesajları göster No comment provided by engineer. + + Show message status + Mesaj durumunu göster + No comment provided by engineer. + + + Show percentage + Yüzdeyi göster + No comment provided by engineer. + Show preview Ön gösterimi göser No comment provided by engineer. + + Show → on messages sent via private routing. + Gizli yönlendirme yoluyla gönderilen mesajlarda → işaretini göster. + No comment provided by engineer. + Show: Göster: No comment provided by engineer. + + SimpleX + SimpleX + No comment provided by engineer. + SimpleX Address SimpleX Adresi No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. SimpleX Chat güvenliği Trails of Bits tarafından denetlenmiştir. @@ -5134,6 +7131,18 @@ Hata: %@ SimpleX adresi No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + No comment provided by engineer. + + + SimpleX address or 1-time link? + No comment provided by engineer. + + + SimpleX channel link + simplex link type + SimpleX contact address SimpleX kişi adresi @@ -5152,6 +7161,16 @@ Hata: %@ SimpleX links SimpleX bağlantıları + chat feature + + + SimpleX links are prohibited. + SimpleX bağlantıları bu grupta yasaklandı. + No comment provided by engineer. + + + SimpleX links not allowed + SimpleX bağlantılarına izin verilmiyor No comment provided by engineer. @@ -5159,11 +7178,21 @@ Hata: %@ SimpleX tek kullanımlık davet simplex link type + + SimpleX protocols reviewed by Trail of Bits. + SimpleX protokolleri Trail of Bits tarafından incelenmiştir. + No comment provided by engineer. + Simplified incognito mode Basitleştirilmiş gizli mod No comment provided by engineer. + + Size + Boyut + No comment provided by engineer. + Skip Atla @@ -5179,16 +7208,51 @@ Hata: %@ Küçük gruplar (en fazla 20 kişi) No comment provided by engineer. + + Soft + Yumuşak + blur media + + + Some app settings were not migrated. + Bazı uygulama ayarları taşınamadı. + No comment provided by engineer. + + + Some file(s) were not exported: + Bazı dosya(lar) dışa aktarılmadı: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. İçe aktarma sırasında bazı ölümcül olmayan hatalar oluştu - daha fazla ayrıntı için Sohbet konsoluna bakabilirsiniz. No comment provided by engineer. + + Some non-fatal errors occurred during import: + İçe aktarma sırasında bazı önemli olmayan hatalar oluştu: + No comment provided by engineer. + + + Some servers failed the test: +%@ + alert message + Somebody Biri notification title + + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + Kare,daire, veya aralarında herhangi bir şey. + No comment provided by engineer. + Start chat Sohbeti başlat @@ -5204,6 +7268,16 @@ Hata: %@ Geçişi başlat No comment provided by engineer. + + Starting from %@. + %@'dan başlayarak. + No comment provided by engineer. + + + Statistics + İstatistikler + No comment provided by engineer. + Stop Dur @@ -5216,11 +7290,7 @@ Hata: %@ Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - Veritabanı eylemlerini etkinleştirmek için sohbeti durdur + Sohbeti kes No comment provided by engineer. @@ -5251,27 +7321,62 @@ Hata: %@ Stop sharing Paylaşmayı durdur - No comment provided by engineer. + alert action Stop sharing address? Adresi paylaşmak durdurulsun mu? - No comment provided by engineer. + alert title Stopping chat + Sohbeti durdurma No comment provided by engineer. + + Storage + No comment provided by engineer. + + + Strong + Güçlü + blur media + Submit Gönder No comment provided by engineer. + + Subscribed + Abone olundu + No comment provided by engineer. + + + Subscription errors + Abone olurken hata + No comment provided by engineer. + + + Subscriptions ignored + Abonelikler göz ardı edildi + No comment provided by engineer. + Support SimpleX Chat SimpleX Chat'e destek ol No comment provided by engineer. + + Switch audio and video during the call. + Görüşme sırasında ses ve görüntüyü değiştirin. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Sohbet profilini 1 kerelik davetler için değiştirin. + No comment provided by engineer. + System Sistem @@ -5282,14 +7387,23 @@ Hata: %@ Sistem yetkilendirilmesi No comment provided by engineer. + + TCP connection + TCP bağlantısı + No comment provided by engineer. + TCP connection timeout TCP bağlantı zaman aşımı No comment provided by engineer. + + TCP port for messaging + No comment provided by engineer. + TCP_KEEPCNT - TCP_CNTYİTUT + TCP_KEEPCNT No comment provided by engineer. @@ -5302,11 +7416,20 @@ Hata: %@ TCP_TVLDEKAL No comment provided by engineer. + + Tail + Konuşma balonu + No comment provided by engineer. + Take picture Fotoğraf çek No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + No comment provided by engineer. + Tap button Tuşa bas @@ -5342,16 +7465,20 @@ Hata: %@ Taramak için tıkla No comment provided by engineer. - - Tap to start a new chat - Yeni bir sohbet başlatmak için tıkla - No comment provided by engineer. + + Temporary file error + Geçici dosya hatası + file error alert title Test failed at step %@. Test %@ adımında başarısız oldu. server test failure + + Test notifications + No comment provided by engineer. + Test server Sunucuyu test et @@ -5365,7 +7492,7 @@ Hata: %@ Tests failed! Testler başarısız oldu! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5382,11 +7509,6 @@ Hata: %@ Kullanıcılar için teşekkürler - Weblate aracılığıyla katkıda bulun! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - Herhangi bir kullanıcı tanımlayıcısı olmayan ilk platform - tasarım gereği gizli. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5399,6 +7521,15 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Uygulama, mesaj veya iletişim isteği aldığınızda sizi bilgilendirebilir - etkinleştirmek için lütfen ayarları açın. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + Uygulama bilinmeyen dosya sunucularından indirmeleri onaylamanızı isteyecektir (.onion hariç). + No comment provided by engineer. + The attempt to change database passphrase was not completed. Veritabanı parolasını değiştirme girişimi tamamlanmadı. @@ -5409,6 +7540,10 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Taradığınız kod bir SimpleX bağlantı QR kodu değildir. No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + No comment provided by engineer. + The connection you accepted will be cancelled! Bağlantı kabulünüz iptal edilecektir! @@ -5429,6 +7564,11 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Şifreleme çalışıyor ve yeni şifreleme anlaşması gerekli değil. Bağlantı hatalarına neden olabilir! No comment provided by engineer. + + The future of messaging + Gizli mesajlaşmanın yeni nesli + No comment provided by engineer. + The hash of the previous message is different. Önceki mesajın hash'i farklı. @@ -5444,9 +7584,14 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Mesaj tüm üyeler için yönetilmiş olarak işaretlenecektir. No comment provided by engineer. - - The next generation of private messaging - Gizli mesajlaşmanın yeni nesli + + The messages will be deleted for all members. + Mesajlar tüm üyeler için silinecektir. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. + Mesajlar tüm üyeler için moderasyonlu olarak işaretlenecektir. No comment provided by engineer. @@ -5454,9 +7599,12 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Eski veritabanı geçiş sırasında kaldırılmadı, silinebilir. No comment provided by engineer. - - The profile is only shared with your contacts. - Profil sadece kişilerinle paylaşılacak. + + The same conditions will apply to operator **%@**. + No comment provided by engineer. + + + The second preset operator in the app! No comment provided by engineer. @@ -5474,14 +7622,27 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Mevcut sohbet profilinizin yeni bağlantıları için sunucular **%@**. No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. Yapıştırdığın metin bir SimpleX bağlantısı değildir. No comment provided by engineer. - - Theme - Tema + + The uploaded database archive will be permanently removed from the servers. + Yüklenen veritabanı arşivi sunuculardan kalıcı olarak kaldırılacaktır. + No comment provided by engineer. + + + Themes + Temalar + No comment provided by engineer. + + + These conditions will also apply for: **%@**. No comment provided by engineer. @@ -5504,6 +7665,10 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Bu işlem geri alınamaz - seçilenden daha önce gönderilen ve alınan mesajlar silinecektir. Bu işlem birkaç dakika sürebilir. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. Bu işlem geri alınamaz - profiliniz, kişileriniz, mesajlarınız ve dosyalarınız geri döndürülemez şekilde kaybolacaktır. @@ -5511,10 +7676,12 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. This chat is protected by end-to-end encryption. + Bu sohbet uçtan uca şifreleme ile korunmaktadır. E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + Bu sohbet kuantum dirençli uçtan uca şifreleme ile korunmaktadır. E2EE info chat item @@ -5524,8 +7691,7 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. This display name is invalid. Please choose another name. - Görünen ad geçerli değil. - Lütfen başka bir ad seç. + Bu görünen ad geçersiz. Lütfen başka bir isim seçin. No comment provided by engineer. @@ -5548,11 +7714,29 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Bu senin kendi tek kullanımlık bağlantın! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + Bu bağlantı başka bir mobil cihazda kullanıldı, lütfen masaüstünde yeni bir bağlantı oluşturun. + No comment provided by engineer. + + + This message was deleted or not received yet. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. Bu ayar, geçerli sohbet profiliniz **%@** deki mesajlara uygulanır. No comment provided by engineer. + + Title + Başlık + No comment provided by engineer. + To ask any questions and to receive updates: Soru sormak ve güncellemeleri almak için: @@ -5573,9 +7757,8 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Yeni bir bağlantı oluşturmak için No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Gizliliği korumak için, diğer tüm platformlar gibi kullanıcı kimliği kullanmak yerine, SimpleX mesaj kuyrukları için kişilerinizin her biri için ayrı tanımlayıcılara sahiptir. + + To protect against your link being replaced, you can compare contact security codes. No comment provided by engineer. @@ -5583,6 +7766,11 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Zaman bölgesini korumak için,fotoğraf/ses dosyaları UTC kullanır. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + IP adresinizi korumak için,gizli yönlendirme mesajları iletmek için SMP sunucularınızı kullanır. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5590,6 +7778,25 @@ You will be prompted to complete authentication before this feature is enabled.< Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenecektir. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Gizliliği korumak için, diğer tüm platformlar gibi kullanıcı kimliği kullanmak yerine, SimpleX mesaj kuyrukları için kişilerinizin her biri için ayrı tanımlayıcılara sahiptir. + No comment provided by engineer. + + + To receive + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + Konuşmayı kaydetmek için lütfen Mikrofon kullanma izni verin. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Video kaydetmek için lütfen Kamera kullanım izni verin. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Sesli mesaj kaydetmek için lütfen Mikrofon kullanım izni verin. @@ -5600,26 +7807,58 @@ Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenec Gizli profilinizi ortaya çıkarmak için **Sohbet profilleriniz** sayfasındaki arama alanına tam bir şifre girin. No comment provided by engineer. + + To send + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. Anlık anlık bildirimleri desteklemek için sohbet veritabanının taşınması gerekir. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. Kişinizle uçtan uca şifrelemeyi doğrulamak için cihazlarınızdaki kodu karşılaştırın (veya tarayın). No comment provided by engineer. + + Toggle chat list: + Sohbet listesini değiştir: + No comment provided by engineer. + Toggle incognito when connecting. Bağlanırken gizli moda geçiş yap. No comment provided by engineer. + + Token status: %@. + token status + + + Toolbar opacity + Araç çubuğu opaklığı + No comment provided by engineer. + + + Total + Toplam + No comment provided by engineer. + Transport isolation Taşıma izolasyonu No comment provided by engineer. + + Transport sessions + Taşıma oturumları + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Bu kişiden mesaj almak için kullanılan sunucuya bağlanılmaya çalışılıyor (hata: %@). @@ -5675,10 +7914,9 @@ Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenec Üyenin engeli kaldırılsın mı? No comment provided by engineer. - - Unexpected error: %@ - Beklenmeyen hata: %@ - item status description + + Undelivered messages + No comment provided by engineer. Unexpected migration state @@ -5688,7 +7926,7 @@ Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenec Unfav. Favorilerden çık. - No comment provided by engineer. + swipe action Unhide @@ -5725,6 +7963,11 @@ Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenec Bilinmeyen hata No comment provided by engineer. + + Unknown servers! + Bilinmeyen sunucular! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. iOS arama arayüzünü kullanmadığınız sürece, kesintileri önlemek için Rahatsız Etmeyin modunu etkinleştirin. @@ -5760,11 +8003,15 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Unmute Susturmayı kaldır - No comment provided by engineer. + notification label action Unread Okunmamış + swipe action + + + Unsupported connection link No comment provided by engineer. @@ -5777,11 +8024,6 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Güncelle No comment provided by engineer. - - Update .onion hosts setting? - .onion ana bilgisayarların ayarı güncellensin mi? - No comment provided by engineer. - Update database passphrase Veritabanı parolasını güncelle @@ -5792,9 +8034,13 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Bağlantı ayarları güncellensin mi? No comment provided by engineer. - - Update transport isolation mode? - Taşıma izolasyon modu güncellensin mi? + + Update settings? + Ayarları güncelleyelim mi? + No comment provided by engineer. + + + Updated conditions No comment provided by engineer. @@ -5802,18 +8048,19 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Ayarların güncellenmesi, istemciyi tüm sunuculara yeniden bağlayacaktır. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - Bu ayarın güncellenmesi, istemciyi tüm sunuculara yeniden bağlayacaktır. - No comment provided by engineer. - Upgrade and open chat Yükselt ve sohbeti aç No comment provided by engineer. + + Upload errors + Yükleme hataları + No comment provided by engineer. + Upload failed + Yükleme başarısız No comment provided by engineer. @@ -5821,8 +8068,23 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Dosya yükle server test step + + Uploaded + Yüklendi + No comment provided by engineer. + + + Uploaded files + Yüklenen dosyalar + No comment provided by engineer. + Uploading archive + Arşiv yükleme + No comment provided by engineer. + + + Use %@ No comment provided by engineer. @@ -5830,11 +8092,24 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste .onion ana bilgisayarlarını kullan No comment provided by engineer. + + Use SOCKS proxy + SOCKS vekili kullan + No comment provided by engineer. + Use SimpleX Chat servers? SimpleX Chat sunucuları kullanılsın mı? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat Sohbeti kullan @@ -5845,6 +8120,14 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Şu anki profili kullan No comment provided by engineer. + + Use for files + No comment provided by engineer. + + + Use for messages + No comment provided by engineer. + Use for new connections Yeni bağlantılar için kullan @@ -5870,23 +8153,51 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Sadece yerel bildirimler kullanılsın mı? No comment provided by engineer. + + Use private routing with unknown servers when IP address is not protected. + IP adresi korunmadığında bilinmeyen sunucularla gizli yönlendirme kullan. + No comment provided by engineer. + + + Use private routing with unknown servers. + Bilinmeyen sunucularla gizli yönlendirme kullan. + No comment provided by engineer. + Use server Sunucu kullan No comment provided by engineer. + + Use servers + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. + Görüşme sırasında uygulamayı kullanın. No comment provided by engineer. - - User profile - Kullanıcı profili + + Use the app with one hand. + Uygulamayı tek elle kullan. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - .onion ana bilgisayarlarını kullanmak için uyumlu VPN sağlayıcısı gerekir. + + Use web port + No comment provided by engineer. + + + User selection + Kullanıcı seçimi + No comment provided by engineer. + + + Username + Kullanıcı Adı No comment provided by engineer. @@ -5916,10 +8227,12 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Verify database passphrase + Veritabanı parolasını doğrulayın No comment provided by engineer. Verify passphrase + Parolayı doğrula No comment provided by engineer. @@ -5957,11 +8270,19 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste 1gb'a kadar videolar ve dosyalar No comment provided by engineer. + + View conditions + No comment provided by engineer. + View security code Güvenlik kodunu görüntüle No comment provided by engineer. + + View updated conditions + No comment provided by engineer. + Visible history Görünür geçmiş @@ -5977,11 +8298,16 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Bu sohbette sesli mesajlar yasaktır. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. Bu grupta sesli mesajlar yasaktır. No comment provided by engineer. + + Voice messages not allowed + Sesli mesajlara izin verilmiyor + No comment provided by engineer. + Voice messages prohibited! Sesli mesajlar yasaktır! @@ -6012,8 +8338,19 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Video bekleniyor No comment provided by engineer. + + Wallpaper accent + Duvar kağıdı vurgusu + No comment provided by engineer. + + + Wallpaper background + Duvar kağıdı arkaplanı + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures + Uyarı: birden fazla cihazda sohbet başlatmak desteklenmez ve mesaj teslim hatalarına neden olur No comment provided by engineer. @@ -6038,6 +8375,7 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Welcome message is too long + Hoş geldiniz mesajı çok uzun No comment provided by engineer. @@ -6050,9 +8388,13 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Mevcut olduğunda No comment provided by engineer. - - When people request to connect, you can accept or reject it. - İnsanlar bağlantı talebinde bulunduğunda, kabul edebilir veya reddedebilirsiniz. + + When connecting audio and video calls. + Sesli ve görüntülü aramalara bağlanırken. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. No comment provided by engineer. @@ -6060,6 +8402,21 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Biriyle gizli bir profil paylaştığınızda, bu profil sizi davet ettikleri gruplar için kullanılacaktır. No comment provided by engineer. + + WiFi + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + Doğrudan sohbetlerde etkinleştirilecektir! + No comment provided by engineer. + + + Wired ethernet + Kablolu ethernet + No comment provided by engineer. + With encrypted files and media. Şifrelenmiş dosyalar ve medya ile birlikte. @@ -6075,28 +8432,44 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Azaltılmış pil kullanımı ile birlikte. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + Tor veya VPN olmadan, IP adresiniz dosya sunucularına görülebilir. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + Tor veya VPN olmadan, IP adresiniz bu XFTP aktarıcıları tarafından görülebilir: %@. + alert message + Wrong database passphrase Yanlış veritabanı parolası No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + Yanlış anahtar veya bilinmeyen bağlantı - büyük olasılıkla bu bağlantı silinmiştir. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + Yanlış anahtar veya bilinmeyen dosya yığın adresi - büyük olasılıkla dosya silinmiştir. + file error text + Wrong passphrase! Yanlış parola! No comment provided by engineer. - - XFTP servers - XFTP sunucuları - No comment provided by engineer. - - - You - Sen + + XFTP server + XFTP sunucusu No comment provided by engineer. You **must not** use the same database on two devices. + Aynı veritabanını iki cihazda **kullanmamalısınız**. No comment provided by engineer. @@ -6119,6 +8492,10 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Zaten %@'a bağlısınız. No comment provided by engineer. + + You are already connected with %@. + No comment provided by engineer. + You are already connecting to %@. Zaten %@'a bağlanıyorsunuz. @@ -6166,11 +8543,25 @@ Katılma isteği tekrarlansın mı? Gruba davet edildiniz No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + Bu sunuculara bağlı değilsiniz. Mesajları onlara iletmek için özel yönlendirme kullanılır. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. Cihaz ve uygulama kimlik doğrulaması olmadan kilit ekranından çağrı kabul edebilirsiniz. No comment provided by engineer. + + You can change it in Appearance settings. + Görünüm ayarlarından değiştirebilirsiniz. + No comment provided by engineer. + + + You can configure servers via settings. + No comment provided by engineer. + You can create it later Daha sonra oluşturabilirsiniz @@ -6188,6 +8579,7 @@ Katılma isteği tekrarlansın mı? You can give another try. + Bir kez daha deneyebilirsiniz. No comment provided by engineer. @@ -6200,11 +8592,20 @@ Katılma isteği tekrarlansın mı? Ayarlardan SimpleX kişilerinize görünür yapabilirsiniz. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ Artık %@ adresine mesaj gönderebilirsin notification body + + You can send messages to %@ from Archived contacts. + Arşivlenen kişilerden %@'ya mesaj gönderebilirsiniz. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + No comment provided by engineer. + You can set lock screen notification preview via settings. Kilit ekranı bildirim önizlemesini ayarlar üzerinden ayarlayabilirsiniz. @@ -6220,16 +8621,16 @@ Katılma isteği tekrarlansın mı? Bu adresi kişilerinizle paylaşarak onların **%@** ile bağlantı kurmasını sağlayabilirsiniz. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - Adresinizi bir bağlantı veya QR kodu olarak paylaşabilirsiniz - herkes size bağlanabilir. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app Sohbeti uygulamada Ayarlar / Veritabanı üzerinden veya uygulamayı yeniden başlatarak başlatabilirsiniz No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + Sohbet listesinde %@ ile konuşmayı görüntülemeye devam edebilirsiniz. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. SimpleX Kilidini Ayarlar üzerinden açabilirsiniz. @@ -6243,23 +8644,23 @@ Katılma isteği tekrarlansın mı? You can view invitation link again in connection details. Bağlantı detaylarından davet bağlantısını yeniden görüntüleyebilirsin. - No comment provided by engineer. + alert message You can't send messages! Mesajlar gönderemezsiniz! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Mesajların hangi sunucu(lar)dan **alınacağını**, kişilerinizi - onlara mesaj göndermek için kullandığınız sunucuları - siz kontrol edersiniz. - No comment provided by engineer. - You could not be verified; please try again. Doğrulanamadınız; lütfen tekrar deneyin. No comment provided by engineer. + + You decide who can connect. + Kimin bağlanabileceğine siz karar verirsiniz. + No comment provided by engineer. + You have already requested connection via this address! Bu adres üzerinden zaten bağlantı talebinde bulundunuz! @@ -6272,11 +8673,6 @@ Repeat connection request? Bağlantı isteği tekrarlansın mı? No comment provided by engineer. - - You have no chats - Hiç sohbetiniz yok - No comment provided by engineer. - You have to enter passphrase every time the app starts - it is not stored on the device. Uygulama her başladığında parola girmeniz gerekir - parola cihazınızda saklanmaz. @@ -6297,11 +8693,26 @@ Bağlantı isteği tekrarlansın mı? Bu gruba katıldınız. Davet eden grup üyesine bağlanılıyor. No comment provided by engineer. + + You may migrate the exported database. + Dışa aktarılan veritabanını taşıyabilirsiniz. + No comment provided by engineer. + + + You may save the exported archive. + Dışa aktarılan arşivi kaydedebilirsiniz. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. Sohbet veritabanınızın en son sürümünü SADECE bir cihazda kullanmalısınız, aksi takdirde bazı kişilerden daha fazla mesaj alamayabilirsiniz. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + Kendiniz arayabilmeniz için önce irtibat kişinizin sizi aramasına izin vermelisiniz. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. Sesli mesaj gönderebilmeniz için kişinizin de sesli mesaj göndermesine izin vermeniz gerekir. @@ -6309,7 +8720,7 @@ Bağlantı isteği tekrarlansın mı? You rejected group invitation - Grup davetini reddettiniz. + Grup davetini reddettiniz No comment provided by engineer. @@ -6317,6 +8728,10 @@ Bağlantı isteği tekrarlansın mı? Grup daveti gönderdiniz No comment provided by engineer. + + You should receive notifications. + token info + You will be connected to group when the group host's device is online, please wait or check later! Grup sahibinin cihazı çevrimiçi olduğunda gruba bağlanacaksınız, lütfen bekleyin veya daha sonra kontrol edin! @@ -6352,6 +8767,10 @@ Bağlantı isteği tekrarlansın mı? Aktif olduklarında sessize alınmış profillerden arama ve bildirim almaya devam edersiniz. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Bu gruptan artık mesaj almayacaksınız. Sohbet geçmişi korunacaktır. @@ -6372,31 +8791,16 @@ Bağlantı isteği tekrarlansın mı? Bu grup için gizli bir profil kullanıyorsunuz - ana profilinizi paylaşmayı önlemek için kişileri davet etmeye izin verilmiyor No comment provided by engineer. - - Your %@ servers - %@ sunucularınız - No comment provided by engineer. - Your ICE servers ICE sunucularınız No comment provided by engineer. - - Your SMP servers - SMP sunucularınız - No comment provided by engineer. - Your SimpleX address SimpleX adresin No comment provided by engineer. - - Your XFTP servers - XFTP sunucularınız - No comment provided by engineer. - Your calls Aramaların @@ -6412,16 +8816,19 @@ Bağlantı isteği tekrarlansın mı? Sohbet veritabanınız şifrelenmemiş - şifrelemek için parola ayarlayın. No comment provided by engineer. + + Your chat preferences + Sohbet tercihleriniz + alert title + Your chat profiles Sohbet profillerin No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Bağlantının tamamlanması için kişinizin çevrimiçi olması gerekir. -Bu bağlantıyı iptal edebilir ve kişiyi kaldırabilirsiniz (ve daha sonra yeni bir bağlantıyla deneyebilirsiniz). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Bağlantınız %@ adresine taşındı ancak sizi profile yönlendirirken beklenmedik bir hata oluştu. No comment provided by engineer. @@ -6439,6 +8846,11 @@ Bu bağlantıyı iptal edebilir ve kişiyi kaldırabilirsiniz (ve daha sonra yen Kişileriniz bağlı kalacaktır. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Kimlik bilgileriniz şifrelenmeden gönderilebilir. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Mevcut sohbet veritabanınız SİLİNECEK ve içe aktarılan veritabanıyla DEĞİŞTİRİLECEKTİR. @@ -6469,33 +8881,35 @@ Bu bağlantıyı iptal edebilir ve kişiyi kaldırabilirsiniz (ve daha sonra yen Profiliniz **%@** paylaşılacaktır. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Profiliniz cihazınızda saklanır ve sadece kişilerinizle paylaşılır. -SimpleX sunucuları profilinizi göremez. + + Your profile is stored on your device and only shared with your contacts. + Profil sadece kişilerinle paylaşılacak. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Profiliniz, kişileriniz ve gönderilmiş mesajlar cihazınızda saklanır. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Profiliniz cihazınızda saklanır ve sadece kişilerinizle paylaşılır. SimpleX sunucuları profilinizi göremez. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Profiliniz değiştirildi. Kaydederseniz, güncellenmiş profil tüm kişilerinize gönderilecektir. + alert message + Your random profile Rasgele profiliniz No comment provided by engineer. - - Your server - Sunucunuz - No comment provided by engineer. - Your server address Sunucu adresiniz No comment provided by engineer. + + Your servers + No comment provided by engineer. + Your settings Ayarlarınız @@ -6536,11 +8950,20 @@ SimpleX sunucuları profilinizi göremez. kabul edilen arama call status + + accepted invitation + chat list item title + admin yönetici member role + + admins + yöneticiler + feature role + agreeing encryption for %@… %@ için şifreleme kabul ediliyor… @@ -6551,6 +8974,11 @@ SimpleX sunucuları profilinizi göremez. şifreleme kabul ediliyor… chat item text + + all members + bütün üyeler + feature role + always her zaman @@ -6561,6 +8989,15 @@ SimpleX sunucuları profilinizi göremez. ve %lld diğer etkinlikler No comment provided by engineer. + + archived report + No comment provided by engineer. + + + attempts + denemeler + No comment provided by engineer. + audio call (not e2e encrypted) sesli arama (uçtan uca şifreli değil) @@ -6594,13 +9031,19 @@ SimpleX sunucuları profilinizi göremez. blocked by admin yönetici tarafından engellendi - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold kalın No comment provided by engineer. + + call + Ara + No comment provided by engineer. + call error arama hatası @@ -6704,7 +9147,7 @@ SimpleX sunucuları profilinizi göremez. connecting… bağlanılıyor… - chat list item title + No comment provided by engineer. connection established @@ -6751,10 +9194,16 @@ SimpleX sunucuları profilinizi göremez. gün time unit + + decryption errors + Şifre çözme hataları + No comment provided by engineer. + default (%@) varsayılan (%@) - pref value + delete after time +pref value default (no) @@ -6801,6 +9250,11 @@ SimpleX sunucuları profilinizi göremez. yinelenen mesaj integrity error chat item + + duplicates + Kopyalar + No comment provided by engineer. + e2e encrypted uçtan uca şifrelenmiş @@ -6876,9 +9330,14 @@ SimpleX sunucuları profilinizi göremez. hata No comment provided by engineer. - - event happened - etkinlik yaşandı + + expired + Süresi dolmuş + No comment provided by engineer. + + + forwarded + iletildi No comment provided by engineer. @@ -6906,6 +9365,11 @@ SimpleX sunucuları profilinizi göremez. iOS Anahtar Zinciri, uygulamayı yeniden başlattıktan veya parolayı değiştirdikten sonra parolayı güvenli bir şekilde saklamak için kullanılacaktır - anlık bildirimlerin alınmasına izin verecektir. No comment provided by engineer. + + inactive + inaktif + No comment provided by engineer. + incognito via contact address link kişi bağlantı linki aracılığıyla gizli @@ -6946,6 +9410,11 @@ SimpleX sunucuları profilinizi göremez. %@ grubuna davet group name + + invite + davet + No comment provided by engineer. + invited davet edildi @@ -7001,6 +9470,11 @@ SimpleX sunucuları profilinizi göremez. bağlanıldı rcv group event chat item + + message + mesaj + No comment provided by engineer. + message received mesaj alındı @@ -7026,6 +9500,10 @@ SimpleX sunucuları profilinizi göremez. %@ tarafından yönetilmekte marked deleted chat item preview text + + moderator + member role + months aylar @@ -7034,7 +9512,7 @@ SimpleX sunucuları profilinizi göremez. never asla - No comment provided by engineer. + delete after time new message @@ -7065,8 +9543,8 @@ SimpleX sunucuları profilinizi göremez. off kapalı enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7083,18 +9561,42 @@ SimpleX sunucuları profilinizi göremez. açık group pref value + + other + diğer + No comment provided by engineer. + + + other errors + diğer hatalar + No comment provided by engineer. + owner sahip member role + + owners + sahipler + feature role + peer-to-peer eşler arası No comment provided by engineer. + + pending + No comment provided by engineer. + + + pending approval + No comment provided by engineer. + quantum resistant e2e encryption + kuantuma dayanıklı e2e şifreleme chat item text @@ -7107,6 +9609,10 @@ SimpleX sunucuları profilinizi göremez. onaylama alındı… No comment provided by engineer. + + rejected + No comment provided by engineer. + rejected call geri çevrilmiş çağrı @@ -7137,6 +9643,25 @@ SimpleX sunucuları profilinizi göremez. sen kaldırıldın rcv group event chat item + + requested to connect + chat list item title + + + saved + kaydedildi + No comment provided by engineer. + + + saved from %@ + %@ tarafından kaydedildi + No comment provided by engineer. + + + search + ara + No comment provided by engineer. + sec sn @@ -7162,6 +9687,15 @@ SimpleX sunucuları profilinizi göremez. doğrudan mesaj gönder No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + sunucu kuyruk bilgisi: %1$@ + +son alınan msj: %2$@ + queue info + set new contact address yeni kişi adresi ayarla @@ -7174,6 +9708,7 @@ SimpleX sunucuları profilinizi göremez. standard end-to-end encryption + standart uçtan uca şifreleme chat item text @@ -7201,11 +9736,21 @@ SimpleX sunucuları profilinizi göremez. bilinmeyen connection info + + unknown servers + bilinmeyen yönlendiriciler + No comment provided by engineer. + unknown status bilinmeyen durum No comment provided by engineer. + + unprotected + korumasız + No comment provided by engineer. + updated group profile grup profili güncellendi @@ -7246,6 +9791,11 @@ SimpleX sunucuları profilinizi göremez. yönlendirici aracılığıyla No comment provided by engineer. + + video + video + No comment provided by engineer. + video call (not e2e encrypted) Görüntülü arama (şifrelenmiş değil) @@ -7271,11 +9821,21 @@ SimpleX sunucuları profilinizi göremez. haftalar time unit + + when IP hidden + IP gizliyken + No comment provided by engineer. + yes evet pref value + + you + sen + No comment provided by engineer. + you are invited to group gruba davet edildiniz @@ -7350,7 +9910,7 @@ SimpleX sunucuları profilinizi göremez.
- +
@@ -7387,7 +9947,7 @@ SimpleX sunucuları profilinizi göremez.
- +
@@ -7407,4 +9967,245 @@ SimpleX sunucuları profilinizi göremez.
+ +
+ +
+ + + %d new events + notification body + + + From %d chat(s) + notification body + + + From: %@ + notification body + + + New events + notification + + + New messages + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Telif Hakkı © 2024 SimpleX Chat. Tüm hakları saklıdır. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + Uygulama kilitlendi! + No comment provided by engineer. + + + Cancel + İptal et + No comment provided by engineer. + + + Cannot access keychain to save database password + Veritabanı şifresini kaydetmek için Anahtar Zinciri'ne erişilemiyor + No comment provided by engineer. + + + Cannot forward message + Mesaj iletilemiyor + No comment provided by engineer. + + + Comment + Yorum + No comment provided by engineer. + + + Currently maximum supported file size is %@. + Şu anki maksimum desteklenen dosya boyutu %@ kadardır. + No comment provided by engineer. + + + Database downgrade required + Veritabanı sürüm düşürme gerekli + No comment provided by engineer. + + + Database encrypted! + Veritabanı şifrelendi! + No comment provided by engineer. + + + Database error + Veritabanı hatası + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + Veritabanı parolası Anahtar Zinciri'nde kayıtlı olandan farklıdır. + No comment provided by engineer. + + + Database passphrase is required to open chat. + Konuşmayı açmak için veri tabanı parolası gerekli. + No comment provided by engineer. + + + Database upgrade required + Veritabanı yükseltmesi gerekli + No comment provided by engineer. + + + Error preparing file + Dosya hazırlanırken hata oluştu + No comment provided by engineer. + + + Error preparing message + Mesaj hazırlanırken hata oluştu + No comment provided by engineer. + + + Error: %@ + Hata: %@ + No comment provided by engineer. + + + File error + Dosya hatası + No comment provided by engineer. + + + Incompatible database version + Uyumsuz veritabanı sürümü + No comment provided by engineer. + + + Invalid migration confirmation + Geçerli olmayan taşıma onayı + No comment provided by engineer. + + + Keychain error + Anahtarlık hatası + No comment provided by engineer. + + + Large file! + Büyük dosya! + No comment provided by engineer. + + + No active profile + Aktif profil yok + No comment provided by engineer. + + + Ok + Tamam + No comment provided by engineer. + + + Open the app to downgrade the database. + Veritabanının sürümünü düşürmek için uygulamayı açın. + No comment provided by engineer. + + + Open the app to upgrade the database. + Veritabanını güncellemek için uygulamayı açın. + No comment provided by engineer. + + + Passphrase + Parola + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Lütfen SimpleX uygulamasında bir profil oluşturun + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Seçilen sohbet tercihleri bu mesajı yasakladı. + No comment provided by engineer. + + + Sending a message takes longer than expected. + Mesaj göndermek beklenenden daha uzun sürüyor. + No comment provided by engineer. + + + Sending message… + Mesaj gönderiliyor… + No comment provided by engineer. + + + Share + Paylaş + No comment provided by engineer. + + + Slow network? + Ağ yavaş mı? + No comment provided by engineer. + + + Unknown database error: %@ + Bilinmeyen veritabanı hatası: %@ + No comment provided by engineer. + + + Unsupported format + Desteklenmeyen format + No comment provided by engineer. + + + Wait + Bekleyin + No comment provided by engineer. + + + Wrong database passphrase + Yanlış veritabanı parolası + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + Gizlilik ve Güvenlik / SimpleX Lock ayarlarından paylaşıma izin verebilirsiniz. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/contents.json b/apps/ios/SimpleX Localizations/tr.xcloc/contents.json index 0aee97a599..2e32ea2080 100644 --- a/apps/ios/SimpleX Localizations/tr.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/tr.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "tr", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff index 19b46bfc45..c0375e3b02 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (можна скопіювати) @@ -109,6 +82,7 @@ %@ downloaded + %@ встановлено No comment provided by engineer. @@ -126,6 +100,11 @@ %@ перевірено No comment provided by engineer. + + %@ server + %@ сервер + No comment provided by engineer. + %@ servers %@ сервери @@ -133,6 +112,7 @@ %@ uploaded + %@ завантажено No comment provided by engineer. @@ -140,6 +120,11 @@ %@ хоче підключитися! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ та %lld учасників @@ -160,11 +145,36 @@ %d днів time interval + + %d file(s) are still being downloaded. + %их файл(ів) ще досі завантажуються. + forward confirmation reason + + + %d file(s) failed to download. + %их файлів не вийшло завантажити. + forward confirmation reason + + + %d file(s) were deleted. + %их файл(ів) було видалено. + forward confirmation reason + + + %d file(s) were not downloaded. + %d файл(и) не було завантажено. + forward confirmation reason + %d hours %d годин time interval + + %d messages not forwarded + %d повідомлень не переслано + alert title + %d min %d хв @@ -180,6 +190,10 @@ %d сек time interval + + %d seconds(s) + delete after time + %d skipped message(s) %d пропущено повідомлення(ь) @@ -250,11 +264,6 @@ %lld нові мови інтерфейсу No comment provided by engineer. - - %lld second(s) - %lld секунд(и) - No comment provided by engineer. - %lld seconds %lld секунд @@ -305,11 +314,6 @@ %u повідомлень пропущено. No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) (новий) @@ -320,19 +324,9 @@ (цей пристрій v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - **Додати контакт**: створити нове посилання-запрошення або підключитися за отриманим посиланням. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **Додати новий контакт**: щоб створити одноразовий QR-код або посилання для свого контакту. + + **Create 1-time link**: to create and share a new invitation link. + **Додати контакт**: створити нове посилання-запрошення. No comment provided by engineer. @@ -340,18 +334,19 @@ **Створити групу**: створити нову групу. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **Більш приватний**: перевіряти нові повідомлення кожні 20 хвилин. Серверу SimpleX Chat передається токен пристрою, але не кількість контактів або повідомлень, які ви маєте. No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **Найбільш приватний**: не використовуйте сервер сповіщень SimpleX Chat, періодично перевіряйте повідомлення у фоновому режимі (залежить від того, як часто ви користуєтесь додатком). No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **Зверніть увагу**: використання однієї і тієї ж бази даних на двох пристроях порушить розшифровку повідомлень з ваших з'єднань, як захист безпеки. No comment provided by engineer. @@ -359,11 +354,16 @@ **Зверніть увагу: ви НЕ зможете відновити або змінити пароль, якщо втратите його. No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **Рекомендується**: токен пристрою та сповіщення надсилаються на сервер сповіщень SimpleX Chat, але не вміст повідомлення, його розмір або від кого воно надійшло. No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + **Відсканувати / Вставити посилання**: підключитися за отриманим посиланням. + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **Попередження**: Для отримання миттєвих пуш-сповіщень потрібна парольна фраза, збережена у брелоку. @@ -371,6 +371,7 @@ **Warning**: the archive will be removed. + **Попередження**: архів буде видалено. No comment provided by engineer. @@ -388,11 +389,6 @@ \*жирний* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -429,11 +425,6 @@ - історія редагування. No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec 0 сек @@ -447,7 +438,8 @@ 1 day 1 день - time interval + delete after time +time interval 1 hour @@ -462,12 +454,28 @@ 1 month 1 місяць - time interval + delete after time +time interval 1 week 1 тиждень - time interval + delete after time +time interval + + + 1 year + delete after time + + + 1-time link + Одноразове посилання + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + Одноразове посилання можна використовувати *тільки з одним контактом* - поділіться ним особисто або через будь-який месенджер. + No comment provided by engineer. 5 minutes @@ -484,11 +492,6 @@ 30 секунд No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -538,31 +541,32 @@ Скасувати зміну адреси? No comment provided by engineer. - - About SimpleX - Про SimpleX - No comment provided by engineer. - About SimpleX Chat Про чат SimpleX No comment provided by engineer. - - About SimpleX address - Про адресу SimpleX + + About operators + Про операторів No comment provided by engineer. - - Accent color - Акцентний колір + + Accent + Акцент No comment provided by engineer. Accept Прийняти accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + Прийняти умови + No comment provided by engineer. Accept connection request? @@ -577,21 +581,45 @@ Accept incognito Прийняти інкогніто - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + Прийняті умови + No comment provided by engineer. + + + Acknowledged + Визнано + No comment provided by engineer. + + + Acknowledgement errors + Помилки підтвердження + No comment provided by engineer. + + + Active + token status text + + + Active connections + Активні з'єднання + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. Додайте адресу до свого профілю, щоб ваші контакти могли поділитися нею з іншими людьми. Повідомлення про оновлення профілю буде надіслано вашим контактам. No comment provided by engineer. - - Add contact - Додати контакт + + Add friends + Додайте друзів No comment provided by engineer. - - Add preset servers - Додавання попередньо встановлених серверів + + Add list No comment provided by engineer. @@ -599,14 +627,19 @@ Додати профіль No comment provided by engineer. + + Add server + Додати сервер + No comment provided by engineer. + Add servers by scanning QR codes. Додайте сервери, відсканувавши QR-код. No comment provided by engineer. - - Add server… - Додати сервер… + + Add team members + Додайте учасників команди No comment provided by engineer. @@ -614,11 +647,45 @@ Додати до іншого пристрою No comment provided by engineer. + + Add to list + No comment provided by engineer. + Add welcome message Додати вітальне повідомлення No comment provided by engineer. + + Add your team members to the conversations. + Додайте членів своєї команди до розмов. + No comment provided by engineer. + + + Added media & file servers + Додано медіа та файлові сервери + No comment provided by engineer. + + + Added message servers + Додано сервери повідомлень + No comment provided by engineer. + + + Additional accent + Додатковий акцент + No comment provided by engineer. + + + Additional accent 2 + Додатковий акцент 2 + No comment provided by engineer. + + + Additional secondary + Додаткова вторинна + No comment provided by engineer. + Address Адреса @@ -629,8 +696,19 @@ Зміна адреси буде скасована. Буде використано стару адресу отримання. No comment provided by engineer. + + Address or 1-time link? + Адреса чи одноразове посилання? + No comment provided by engineer. + + + Address settings + Налаштування адреси + No comment provided by engineer. + Admins can block a member for all. + Адміністратори можуть заблокувати користувача для всіх. No comment provided by engineer. @@ -643,6 +721,15 @@ Розширені налаштування мережі No comment provided by engineer. + + Advanced settings + Додаткові налаштування + No comment provided by engineer. + + + All + No comment provided by engineer. + All app data is deleted. Всі дані програми видаляються. @@ -653,16 +740,30 @@ Всі чати та повідомлення будуть видалені - це неможливо скасувати! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + alert message + All data is erased when it is entered. Всі дані стираються при введенні. No comment provided by engineer. + + All data is kept private on your device. + Всі дані є приватними для вашого пристрою. + No comment provided by engineer. + All group members will remain connected. Всі учасники групи залишаться на зв'язку. No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + Всі повідомлення та файли надсилаються **наскрізним шифруванням**, з пост-квантовим захистом у прямих повідомленнях. + No comment provided by engineer. + All messages will be deleted - this cannot be undone! Усі повідомлення будуть видалені - цю дію не можна скасувати! @@ -678,6 +779,19 @@ Всі нові повідомлення від %@ будуть приховані! No comment provided by engineer. + + All profiles + Всі профілі + profile dropdown + + + All reports will be archived for you. + No comment provided by engineer. + + + All servers + No comment provided by engineer. + All your contacts will remain connected. Всі ваші контакти залишаться на зв'язку. @@ -690,6 +804,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + Всі ваші контакти, розмови та файли будуть надійно зашифровані та завантажені частинами на налаштовані XFTP-реле. No comment provided by engineer. @@ -702,11 +817,21 @@ Дозволяйте дзвінки, тільки якщо ваш контакт дозволяє їх. No comment provided by engineer. + + Allow calls? + Дозволити дзвінки? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. Дозволяйте зникати повідомленням, тільки якщо контакт дозволяє вам це робити. No comment provided by engineer. + + Allow downgrade + Дозволити пониження версії + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) Дозволяйте безповоротне видалення повідомлень, тільки якщо контакт дозволяє вам це зробити. (24 години) @@ -732,11 +857,25 @@ Дозволити надсилання зникаючих повідомлень. No comment provided by engineer. + + Allow sharing + Дозволити спільний доступ + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) Дозволяє безповоротно видаляти надіслані повідомлення. (24 години) No comment provided by engineer. + + Allow to report messsages to moderators. + No comment provided by engineer. + + + Allow to send SimpleX links. + Дозволити надсилати посилання SimpleX. + No comment provided by engineer. + Allow to send files and media. Дозволяє надсилати файли та медіа. @@ -797,6 +936,11 @@ Вже приєднуємося до групи! No comment provided by engineer. + + Always use private routing. + Завжди використовуйте приватну маршрутизацію. + No comment provided by engineer. + Always use relay Завжди використовуйте реле @@ -807,11 +951,20 @@ Створюється порожній профіль чату з вказаним ім'ям, і додаток відкривається у звичайному режимі. No comment provided by engineer. + + Another reason + report reason + Answer call Відповісти на дзвінок No comment provided by engineer. + + Anybody can host servers. + Кожен може хостити сервери. + No comment provided by engineer. + App build: %@ Збірка програми: %@ @@ -819,6 +972,7 @@ App data migration + Міграція даних додатків No comment provided by engineer. @@ -826,6 +980,10 @@ Додаток шифрує нові локальні файли (крім відео). No comment provided by engineer. + + App group: + No comment provided by engineer. + App icon Іконка програми @@ -841,6 +999,11 @@ Пароль програми замінено на пароль самознищення. No comment provided by engineer. + + App session + Сесія програми + No comment provided by engineer. + App version Версія програми @@ -858,14 +1021,56 @@ Apply + Подати заявку + No comment provided by engineer. + + + Apply to + Звертатися до + No comment provided by engineer. + + + Archive + No comment provided by engineer. + + + Archive %lld reports? + No comment provided by engineer. + + + Archive all reports? No comment provided by engineer. Archive and upload + Архівування та завантаження + No comment provided by engineer. + + + Archive contacts to chat later. + Архівуйте контакти, щоб поспілкуватися пізніше. + No comment provided by engineer. + + + Archive report + No comment provided by engineer. + + + Archive report? + No comment provided by engineer. + + + Archive reports + swipe action + + + Archived contacts + Архівні контакти No comment provided by engineer. Archiving database + Архівування бази даних No comment provided by engineer. @@ -928,11 +1133,21 @@ Автоматичне прийняття зображень No comment provided by engineer. + + Auto-accept settings + Автоприйняття налаштувань + alert title + Back Назад No comment provided by engineer. + + Background + Фон + No comment provided by engineer. + Bad desktop address Неправильна адреса робочого столу @@ -948,16 +1163,59 @@ Поганий хеш повідомлення No comment provided by engineer. + + Better calls + Кращі дзвінки + No comment provided by engineer. + Better groups Кращі групи No comment provided by engineer. + + Better groups performance + No comment provided by engineer. + + + Better message dates. + Кращі дати повідомлень. + No comment provided by engineer. + Better messages Кращі повідомлення No comment provided by engineer. + + Better networking + Краща мережа + No comment provided by engineer. + + + Better notifications + Кращі сповіщення + No comment provided by engineer. + + + Better privacy and security + No comment provided by engineer. + + + Better security ✅ + Краща безпека ✅ + No comment provided by engineer. + + + Better user experience + Покращений користувацький досвід + No comment provided by engineer. + + + Black + Чорний + No comment provided by engineer. + Block Блокувати @@ -993,6 +1251,16 @@ Заблокований адміністратором No comment provided by engineer. + + Blur for better privacy. + Розмиття для кращої приватності. + No comment provided by engineer. + + + Blur media + Розмиття медіа + No comment provided by engineer. + Both you and your contact can add message reactions. Реакції на повідомлення можете додавати як ви, так і ваш контакт. @@ -1023,11 +1291,31 @@ Болгарською, фінською, тайською та українською мовами - завдяки користувачам та [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + Адреса підприємства + No comment provided by engineer. + + + Business chats + Ділові чати + No comment provided by engineer. + + + Businesses + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). Через профіль чату (за замовчуванням) або [за з'єднанням](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + No comment provided by engineer. + Call already ended! Дзвінок вже закінчився! @@ -1038,11 +1326,26 @@ Дзвінки No comment provided by engineer. + + Calls prohibited! + Дзвінки заборонені! + No comment provided by engineer. + Camera not available Камера недоступна No comment provided by engineer. + + Can't call contact + Не вдається додзвонитися до контакту + No comment provided by engineer. + + + Can't call member + Не вдається зателефонувати користувачеві + No comment provided by engineer. + Can't invite contact! Не вдається запросити контакт! @@ -1053,13 +1356,20 @@ Неможливо запросити контакти! No comment provided by engineer. + + Can't message member + Не можу надіслати повідомлення користувачеві + No comment provided by engineer. + Cancel Скасувати - No comment provided by engineer. + alert action +alert button Cancel migration + Скасувати міграцію No comment provided by engineer. @@ -1067,9 +1377,24 @@ Не вдається отримати доступ до зв'язки ключів для збереження пароля до бази даних No comment provided by engineer. + + Cannot forward message + Неможливо переслати повідомлення + No comment provided by engineer. + Cannot receive file Не вдається отримати файл + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + Перевищено ліміт - одержувач не отримав раніше надіслані повідомлення. + snd error text + + + Cellular + Стільниковий No comment provided by engineer. @@ -1077,6 +1402,15 @@ Зміна No comment provided by engineer. + + Change automatic message deletion? + alert title + + + Change chat profiles + Зміна профілів користувачів + authentication reason + Change database passphrase? Змінити пароль до бази даних? @@ -1121,11 +1455,26 @@ Change self-destruct passcode Змінити пароль самознищення authentication reason - set passcode view +set passcode view - - Chat archive - Архів чату + + Chat + Чат + No comment provided by engineer. + + + Chat already exists + Чат вже існує + No comment provided by engineer. + + + Chat already exists! + Чат вже існує! + No comment provided by engineer. + + + Chat colors + Кольори чату No comment provided by engineer. @@ -1143,6 +1492,11 @@ Видалено базу даних чату No comment provided by engineer. + + Chat database exported + Експортовано базу даних чату + No comment provided by engineer. + Chat database imported Імпорт бази даних чату @@ -1163,8 +1517,14 @@ Чат зупинено. Якщо ви вже використовували цю базу даних на іншому пристрої, перенесіть її назад перед запуском чату. No comment provided by engineer. + + Chat list + Список чатів + No comment provided by engineer. + Chat migrated! + Чат перемістився! No comment provided by engineer. @@ -1172,15 +1532,50 @@ Налаштування чату No comment provided by engineer. + + Chat preferences were changed. + Змінено налаштування чату. + alert message + + + Chat profile + Профіль користувача + No comment provided by engineer. + + + Chat theme + Тема чату + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + Чат буде видалено для всіх учасників - цю дію неможливо скасувати! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + Чат буде видалено для вас - цю дію неможливо скасувати! + No comment provided by engineer. + Chats Чати No comment provided by engineer. + + Check messages every 20 min. + Перевіряйте повідомлення кожні 20 хв. + No comment provided by engineer. + + + Check messages when allowed. + Перевірте повідомлення, коли це дозволено. + No comment provided by engineer. + Check server address and try again. Перевірте адресу сервера та спробуйте ще раз. - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1189,6 +1584,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + Виберіть _Перемістити з іншого пристрою_ на новому пристрої та відскануйте QR-код. No comment provided by engineer. @@ -1201,10 +1597,25 @@ Виберіть з бібліотеки No comment provided by engineer. + + Chunks deleted + Фрагменти видалено + No comment provided by engineer. + + + Chunks downloaded + Завантажено фрагменти + No comment provided by engineer. + + + Chunks uploaded + Завантажено фрагменти + No comment provided by engineer. + Clear Чисто - No comment provided by engineer. + swipe action Clear conversation @@ -1216,8 +1627,17 @@ Відверта розмова? No comment provided by engineer. + + Clear group? + No comment provided by engineer. + + + Clear or delete group? + No comment provided by engineer. + Clear private notes? + Чисті приватні нотатки? No comment provided by engineer. @@ -1225,11 +1645,20 @@ Очистити перевірку No comment provided by engineer. - - Colors - Кольори + + Color chats with the new themes. + Кольорові чати з новими темами. No comment provided by engineer. + + Color mode + Колірний режим + No comment provided by engineer. + + + Community guidelines violation + report reason + Compare file Порівняти файл @@ -1240,11 +1669,55 @@ Порівняйте коди безпеки зі своїми контактами. No comment provided by engineer. + + Completed + Завершено + No comment provided by engineer. + + + Conditions accepted on: %@. + Умови приймаються на: %@. + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + Для оператора(ів) приймаються умови: **%@**. + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + Умови вже прийняті для наступних операторів: **%@**. + No comment provided by engineer. + + + Conditions of use + Умови використання + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + Для оператора(ів) приймаються умови: **%@**. + No comment provided by engineer. + + + Conditions will be accepted on: %@. + Умови приймаються на: %@. + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + Умови будуть автоматично прийняті для увімкнених операторів на: %@. + No comment provided by engineer. + Configure ICE servers Налаштування серверів ICE No comment provided by engineer. + + Configure server operators + No comment provided by engineer. + Confirm Підтвердити @@ -1255,13 +1728,24 @@ Підтвердити пароль No comment provided by engineer. + + Confirm contact deletion? + Підтвердити видалення контакту? + No comment provided by engineer. + Confirm database upgrades Підтвердити оновлення бази даних No comment provided by engineer. + + Confirm files from unknown servers. + Підтвердити файли з невідомих серверів. + No comment provided by engineer. + Confirm network settings + Підтвердьте налаштування мережі No comment provided by engineer. @@ -1276,12 +1760,18 @@ Confirm that you remember database passphrase to migrate it. + Переконайтеся, що ви пам'ятаєте пароль до бази даних для її перенесення. No comment provided by engineer. Confirm upload + Підтвердити завантаження No comment provided by engineer. + + Confirmed + token status text + Connect Підключіться @@ -1302,6 +1792,11 @@ Підключення до комп'ютера No comment provided by engineer. + + Connect to your friends faster. + Швидше спілкуйтеся з друзями. + No comment provided by engineer. + Connect to yourself? З'єднатися з самим собою? @@ -1341,13 +1836,29 @@ This is your own one-time link! Підключитися до %@ No comment provided by engineer. + + Connected + Підключено + No comment provided by engineer. + Connected desktop Підключений робочий стіл No comment provided by engineer. + + Connected servers + Підключені сервери + No comment provided by engineer. + Connected to desktop + Підключено до настільного комп'ютера + No comment provided by engineer. + + + Connecting + Підключення No comment provided by engineer. @@ -1360,8 +1871,14 @@ This is your own one-time link! Підключення до сервера... (помилка: %@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + З'єднання з контактом, будь ласка, зачекайте або перевірте пізніше! + No comment provided by engineer. + Connecting to desktop + Підключення до ПК No comment provided by engineer. @@ -1369,6 +1886,15 @@ This is your own one-time link! Підключення No comment provided by engineer. + + Connection and servers status. + Стан з'єднання та серверів. + No comment provided by engineer. + + + Connection blocked + No comment provided by engineer. + Connection error Помилка підключення @@ -1379,13 +1905,37 @@ This is your own one-time link! Помилка підключення (AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + No comment provided by engineer. + + + Connection not ready. + No comment provided by engineer. + + + Connection notifications + Сповіщення про підключення + No comment provided by engineer. + Connection request sent! Запит на підключення відправлено! No comment provided by engineer. + + Connection requires encryption renegotiation. + No comment provided by engineer. + + + Connection security + Безпека з'єднання + No comment provided by engineer. + Connection terminated + З'єднання розірвано No comment provided by engineer. @@ -1393,6 +1943,16 @@ This is your own one-time link! Тайм-аут з'єднання No comment provided by engineer. + + Connection with desktop stopped + Припинено зв'язок з робочим столом + No comment provided by engineer. + + + Connections + З'єднання + No comment provided by engineer. + Contact allows Контакт дозволяє @@ -1403,6 +1963,11 @@ This is your own one-time link! Контакт вже існує No comment provided by engineer. + + Contact deleted! + Контакт видалено! + No comment provided by engineer. + Contact hidden: Контакт приховано: @@ -1413,9 +1978,9 @@ This is your own one-time link! Контакт підключений notification - - Contact is not connected yet! - Контакт ще не підключено! + + Contact is deleted. + Контакт видалено. No comment provided by engineer. @@ -1428,6 +1993,11 @@ This is your own one-time link! Налаштування контактів No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + Контакт буде видалено - це неможливо скасувати! + No comment provided by engineer. + Contacts Контакти @@ -1438,23 +2008,43 @@ This is your own one-time link! Контакти можуть позначати повідомлення для видалення; ви зможете їх переглянути. No comment provided by engineer. + + Content violates conditions of use + blocking reason + Continue Продовжуйте No comment provided by engineer. + + Conversation deleted! + Розмова видалена! + No comment provided by engineer. + Copy Копіювати - chat item action + No comment provided by engineer. + + + Copy error + Помилка копіювання + No comment provided by engineer. Core version: v%@ Основна версія: v%@ No comment provided by engineer. + + Corner + Кут + No comment provided by engineer. + Correct name to %@? + Виправити ім'я на %@? No comment provided by engineer. @@ -1462,6 +2052,11 @@ This is your own one-time link! Створити No comment provided by engineer. + + Create 1-time link + Створити одноразове посилання + No comment provided by engineer. + Create SimpleX address Створіть адресу SimpleX @@ -1469,11 +2064,7 @@ This is your own one-time link! Create a group using a random profile. - No comment provided by engineer. - - - Create an address to let people connect with you. - Створіть адресу, щоб люди могли з вами зв'язатися. + Створіть групу, використовуючи випадковий профіль. No comment provided by engineer. @@ -1483,6 +2074,7 @@ This is your own one-time link! Create group + Створити групу No comment provided by engineer. @@ -1495,12 +2087,18 @@ This is your own one-time link! Створити посилання No comment provided by engineer. + + Create list + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 + Створіть новий профіль у [desktop app](https://simplex.chat/downloads/). 💻 No comment provided by engineer. Create profile + Створити профіль No comment provided by engineer. @@ -1518,25 +2116,29 @@ This is your own one-time link! Створіть свій профіль No comment provided by engineer. + + Created + Створено + No comment provided by engineer. + Created at + Створено за адресою No comment provided by engineer. Created at: %@ + Створено за адресою: %@ copied message info - - Created on %@ - Створено %@ - No comment provided by engineer. - Creating archive link + Створення архівного посилання No comment provided by engineer. Creating link… + Створення посилання… No comment provided by engineer. @@ -1544,11 +2146,21 @@ This is your own one-time link! Поточний пароль No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + Текст поточних умов не вдалося завантажити, ви можете переглянути умови за цим посиланням: + No comment provided by engineer. + Current passphrase… Поточна парольна фраза… No comment provided by engineer. + + Current profile + Поточний профіль + No comment provided by engineer. + Currently maximum supported file size is %@. Наразі максимальний підтримуваний розмір файлу - %@. @@ -1559,11 +2171,26 @@ This is your own one-time link! Індивідуальний час No comment provided by engineer. + + Customizable message shape. + Налаштовується форма повідомлення. + No comment provided by engineer. + + + Customize theme + Налаштувати тему + No comment provided by engineer. + Dark Темний No comment provided by engineer. + + Dark mode colors + Кольори темного режиму + No comment provided by engineer. + Database ID Ідентифікатор бази даних @@ -1662,6 +2289,11 @@ This is your own one-time link! База даних буде перенесена під час перезапуску програми No comment provided by engineer. + + Debug delivery + Доставка налагодження + No comment provided by engineer. + Decentralized Децентралізований @@ -1675,15 +2307,17 @@ This is your own one-time link! Delete Видалити - chat item action + alert action +swipe action + + + Delete %lld messages of members? + Видалити %lld повідомлень користувачів? + No comment provided by engineer. Delete %lld messages? - No comment provided by engineer. - - - Delete Contact - Видалити контакт + Видалити %lld повідомлень? No comment provided by engineer. @@ -1708,16 +2342,16 @@ This is your own one-time link! Delete and notify contact + Видалити та повідомити контакт No comment provided by engineer. - - Delete archive - Видалити архів + + Delete chat + Видалити чат No comment provided by engineer. - - Delete chat archive? - Видалити архів чату? + + Delete chat messages from your device. No comment provided by engineer. @@ -1730,6 +2364,11 @@ This is your own one-time link! Видалити профіль чату? No comment provided by engineer. + + Delete chat? + Видалити чат? + No comment provided by engineer. + Delete connection Видалити підключення @@ -1740,9 +2379,9 @@ This is your own one-time link! Видалити контакт No comment provided by engineer. - - Delete contact? -This cannot be undone! + + Delete contact? + Видалити контакт? No comment provided by engineer. @@ -1752,6 +2391,7 @@ This cannot be undone! Delete database from this device + Видалити базу даних з цього пристрою No comment provided by engineer. @@ -1804,6 +2444,10 @@ This cannot be undone! Видалити посилання? No comment provided by engineer. + + Delete list? + alert title + Delete member message? Видалити повідомлення учасника? @@ -1817,7 +2461,7 @@ This cannot be undone! Delete messages Видалити повідомлення - No comment provided by engineer. + alert button Delete messages after @@ -1834,9 +2478,9 @@ This cannot be undone! Видалити стару базу даних? No comment provided by engineer. - - Delete pending connection - Видалити очікуване з'єднання + + Delete or moderate up to 200 messages. + Видалити або модерувати до 200 повідомлень. No comment provided by engineer. @@ -1854,11 +2498,30 @@ This cannot be undone! Видалити чергу server test step + + Delete report + No comment provided by engineer. + + + Delete up to 20 messages at once. + Видаляйте до 20 повідомлень одночасно. + No comment provided by engineer. + Delete user profile? Видалити профіль користувача? No comment provided by engineer. + + Delete without notification + Видалення без попередження + No comment provided by engineer. + + + Deleted + Видалено + No comment provided by engineer. + Deleted at Видалено за @@ -1869,6 +2532,16 @@ This cannot be undone! Видалено за: %@ copied message info + + Deletion errors + Помилки видалення + No comment provided by engineer. + + + Delivered even when Apple drops them. + Доставляються навіть тоді, коли Apple кидає їх. + No comment provided by engineer. + Delivery Доставка @@ -1891,14 +2564,42 @@ This cannot be undone! Desktop address + Адреса робочого столу No comment provided by engineer. Desktop app version %@ is not compatible with this app. + Версія програми для настільних комп'ютерів %@ не сумісна з цією програмою. No comment provided by engineer. Desktop devices + Настільні пристрої + No comment provided by engineer. + + + Destination server address of %@ is incompatible with forwarding server %@ settings. + Адреса сервера призначення %@ несумісна з налаштуваннями сервера пересилання %@. + No comment provided by engineer. + + + Destination server error: %@ + Помилка сервера призначення: %@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + Версія сервера призначення %@ несумісна з версією сервера переадресації %@. + No comment provided by engineer. + + + Detailed statistics + Детальна статистика + No comment provided by engineer. + + + Details + Деталі No comment provided by engineer. @@ -1906,6 +2607,11 @@ This cannot be undone! Розробник No comment provided by engineer. + + Developer options + Можливості для розробників + No comment provided by engineer. + Developer tools Інструменти для розробників @@ -1936,8 +2642,13 @@ This cannot be undone! Прямі повідомлення chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited in this chat. + У цьому чаті заборонені прямі повідомлення між учасниками. + No comment provided by engineer. + + + Direct messages between members are prohibited. У цій групі заборонені прямі повідомлення між учасниками. No comment provided by engineer. @@ -1951,11 +2662,24 @@ This cannot be undone! Вимкнути SimpleX Lock authentication reason + + Disable automatic message deletion? + alert title + + + Disable delete messages + alert button + Disable for all Вимкнути для всіх No comment provided by engineer. + + Disabled + Вимкнено + No comment provided by engineer. + Disappearing message Зникаюче повідомлення @@ -1971,8 +2695,8 @@ This cannot be undone! Зникаючі повідомлення в цьому чаті заборонені. No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. У цій групі заборонено зникаючі повідомлення. No comment provided by engineer. @@ -1993,14 +2717,22 @@ This cannot be undone! Disconnect desktop? + Відключити робочий стіл? No comment provided by engineer. Discover and join groups + Знаходьте та приєднуйтесь до груп No comment provided by engineer. Discover via local network + Відкриття через локальну мережу + No comment provided by engineer. + + + Do NOT send messages directly, even if your or destination server does not support private routing. + НЕ надсилайте повідомлення напряму, навіть якщо ваш сервер або сервер призначення не підтримує приватну маршрутизацію. No comment provided by engineer. @@ -2008,6 +2740,11 @@ This cannot be undone! НЕ використовуйте SimpleX для екстрених викликів. No comment provided by engineer. + + Do NOT use private routing. + НЕ використовуйте приватну маршрутизацію. + No comment provided by engineer. + Do it later Зробіть це пізніше @@ -2015,6 +2752,16 @@ This cannot be undone! Do not send history to new members. + Не надсилайте історію новим користувачам. + No comment provided by engineer. + + + Do not use credentials with proxy. + Не використовуйте облікові дані з проксі. + No comment provided by engineer. + + + Documents: No comment provided by engineer. @@ -2027,18 +2774,38 @@ This cannot be undone! Не вмикати No comment provided by engineer. + + Don't miss important messages. + No comment provided by engineer. + Don't show again Більше не показувати No comment provided by engineer. + + Done + No comment provided by engineer. + Downgrade and open chat Пониження та відкритий чат No comment provided by engineer. + + Download + Завантажити + alert button +chat item action + + + Download errors + Помилки завантаження + No comment provided by engineer. + Download failed + Не вдалося завантажити No comment provided by engineer. @@ -2046,12 +2813,29 @@ This cannot be undone! Завантажити файл server test step + + Download files + Завантажити файли + alert action + + + Downloaded + Завантажено + No comment provided by engineer. + + + Downloaded files + Завантажені файли + No comment provided by engineer. + Downloading archive + Завантажити архів No comment provided by engineer. Downloading link details + Деталі посилання для завантаження No comment provided by engineer. @@ -2064,6 +2848,11 @@ This cannot be undone! Тривалість No comment provided by engineer. + + E2E encrypted notifications. + Зашифровані сповіщення E2E. + No comment provided by engineer. + Edit Редагувати @@ -2084,6 +2873,10 @@ This cannot be undone! Увімкнути (зберегти перевизначення) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + No comment provided by engineer. + Enable SimpleX Lock Увімкнути SimpleX Lock @@ -2097,10 +2890,11 @@ This cannot be undone! Enable automatic message deletion? Увімкнути автоматичне видалення повідомлень? - No comment provided by engineer. + alert title Enable camera access + Увімкніть доступ до камери No comment provided by engineer. @@ -2110,6 +2904,7 @@ This cannot be undone! Enable in direct chats (BETA)! + Увімкнути в прямих чатах (BETA)! No comment provided by engineer. @@ -2142,6 +2937,16 @@ This cannot be undone! Увімкнути пароль самознищення set passcode view + + Enabled + Увімкнено + No comment provided by engineer. + + + Enabled for + Увімкнено для + No comment provided by engineer. + Encrypt Зашифрувати @@ -2154,10 +2959,12 @@ This cannot be undone! Encrypt local files + Шифрування локальних файлів No comment provided by engineer. Encrypt stored files & media + Шифрування збережених файлів і носіїв No comment provided by engineer. @@ -2172,6 +2979,7 @@ This cannot be undone! Encrypted message: app is stopped + Зашифроване повідомлення: додаток зупинено notification @@ -2201,10 +3009,16 @@ This cannot be undone! Encryption re-negotiation error + Помилка повторного узгодження шифрування message decrypt error item Encryption re-negotiation failed. + Повторне узгодження шифрування не вдалося. + No comment provided by engineer. + + + Encryption renegotiation in progress. No comment provided by engineer. @@ -2219,10 +3033,12 @@ This cannot be undone! Enter group name… + Введіть назву групи… No comment provided by engineer. Enter passphrase + Введіть парольну фразу No comment provided by engineer. @@ -2242,6 +3058,7 @@ This cannot be undone! Enter this device name… + Введіть назву пристрою… No comment provided by engineer. @@ -2256,6 +3073,7 @@ This cannot be undone! Enter your name… + Введіть своє ім'я… No comment provided by engineer. @@ -2268,30 +3086,36 @@ This cannot be undone! Помилка скасування зміни адреси No comment provided by engineer. + + Error accepting conditions + Помилка прийняття умов + alert title + Error accepting contact request Помилка при прийнятті запиту на контакт No comment provided by engineer. - - Error accessing database file - Помилка доступу до файлу бази даних - No comment provided by engineer. - Error adding member(s) Помилка додавання користувача(ів) No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + Помилка додавання сервера + alert title Error changing address Помилка зміни адреси No comment provided by engineer. + + Error changing connection profile + Помилка при зміні профілю з'єднання + No comment provided by engineer. + Error changing role Помилка зміни ролі @@ -2302,6 +3126,20 @@ This cannot be undone! Помилка зміни налаштування No comment provided by engineer. + + Error changing to incognito! + Помилка переходу на інкогніто! + No comment provided by engineer. + + + Error checking token status + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + Помилка підключення до сервера переадресації %@. Спробуйте пізніше. + No comment provided by engineer. + Error creating address Помилка створення адреси @@ -2317,12 +3155,18 @@ This cannot be undone! Помилка створення посилання на групу No comment provided by engineer. + + Error creating list + alert title + Error creating member contact + Помилка при створенні контакту користувача No comment provided by engineer. Error creating message + Повідомлення про створення помилки No comment provided by engineer. @@ -2330,8 +3174,13 @@ This cannot be undone! Помилка створення профілю! No comment provided by engineer. + + Error creating report + No comment provided by engineer. + Error decrypting file + Помилка розшифрування файлу No comment provided by engineer. @@ -2349,11 +3198,6 @@ This cannot be undone! Помилка видалення з'єднання No comment provided by engineer. - - Error deleting contact - Помилка видалення контакту - No comment provided by engineer. - Error deleting database Помилка видалення бази даних @@ -2376,6 +3220,7 @@ This cannot be undone! Error downloading the archive + Помилка завантаження архіву No comment provided by engineer. @@ -2398,6 +3243,11 @@ This cannot be undone! Помилка експорту бази даних чату No comment provided by engineer. + + Error exporting theme: %@ + Помилка експорту теми: %@ + No comment provided by engineer. + Error importing chat database Помилка імпорту бази даних чату @@ -2408,28 +3258,52 @@ This cannot be undone! Помилка приєднання до групи No comment provided by engineer. - - Error loading %@ servers - Помилка завантаження %@ серверів + + Error loading servers + Помилка завантаження серверів + alert title + + + Error migrating settings + Помилка міграції налаштувань No comment provided by engineer. Error opening chat + Помилка відкриття чату No comment provided by engineer. Error receiving file Помилка отримання файлу + alert title + + + Error reconnecting server + Помилка перепідключення сервера No comment provided by engineer. + + Error reconnecting servers + Помилка перепідключення серверів + No comment provided by engineer. + + + Error registering for notifications + alert title + Error removing member Помилка видалення учасника No comment provided by engineer. - - Error saving %@ servers - Помилка збереження %@ серверів + + Error reordering lists + alert title + + + Error resetting statistics + Статистика скидання помилок No comment provided by engineer. @@ -2437,6 +3311,10 @@ This cannot be undone! Помилка збереження серверів ICE No comment provided by engineer. + + Error saving chat list + alert title + Error saving group profile Помилка збереження профілю групи @@ -2452,8 +3330,14 @@ This cannot be undone! Помилка збереження пароля на keychain No comment provided by engineer. + + Error saving servers + Сервери збереження помилок + alert title + Error saving settings + Налаштування збереження помилок when migrating @@ -2463,6 +3347,7 @@ This cannot be undone! Error scanning code: %@ + Код помилки сканування: %@ No comment provided by engineer. @@ -2472,6 +3357,7 @@ This cannot be undone! Error sending member contact invitation + Помилка надсилання запрошення до контактів учасника No comment provided by engineer. @@ -2494,16 +3380,25 @@ This cannot be undone! Помилка зупинки чату No comment provided by engineer. + + Error switching profile + Помилка перемикання профілю + No comment provided by engineer. + Error switching profile! Помилка перемикання профілю! - No comment provided by engineer. + alertTitle Error synchronizing connection Помилка синхронізації з'єднання No comment provided by engineer. + + Error testing server connection + No comment provided by engineer. + Error updating group link Помилка оновлення посилання на групу @@ -2514,6 +3409,11 @@ This cannot be undone! Повідомлення про помилку оновлення No comment provided by engineer. + + Error updating server + Помилка оновлення сервера + alert title + Error updating settings Помилка оновлення налаштувань @@ -2526,10 +3426,12 @@ This cannot be undone! Error uploading the archive + Помилка при завантаженні архіву No comment provided by engineer. Error verifying passphrase: + Помилка при перевірці парольної фрази: No comment provided by engineer. @@ -2540,7 +3442,9 @@ This cannot be undone! Error: %@ Помилка: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2552,6 +3456,16 @@ This cannot be undone! Помилка: немає файлу бази даних No comment provided by engineer. + + Errors + Помилки + No comment provided by engineer. + + + Errors in servers configuration. + Помилки в конфігурації серверів. + servers error + Even when disabled in the conversation. Навіть коли вимкнений у розмові. @@ -2564,8 +3478,13 @@ This cannot be undone! Expand + Розгорнути chat item action + + Expired + token status text + Export database Експорт бази даних @@ -2576,6 +3495,11 @@ This cannot be undone! Помилка експорту: No comment provided by engineer. + + Export theme + Тема експорту + No comment provided by engineer. + Exported database archive. Експортований архів бази даних. @@ -2583,6 +3507,7 @@ This cannot be undone! Exported file doesn't exist + Експортований файл не існує No comment provided by engineer. @@ -2600,15 +3525,65 @@ This cannot be undone! Швидко і без очікування, поки відправник буде онлайн! No comment provided by engineer. + + Faster deletion of groups. + No comment provided by engineer. + Faster joining and more reliable messages. + Швидше приєднання та надійніші повідомлення. + No comment provided by engineer. + + + Faster sending messages. No comment provided by engineer. Favorite Улюблений + swipe action + + + Favorites No comment provided by engineer. + + File error + Помилка файлу + file error alert title + + + File errors: +%@ + Помилки файлів: +%@ + alert message + + + File is blocked by server operator: +%@. + file error text + + + File not found - most likely file was deleted or cancelled. + Файл не знайдено - найімовірніше, файл було видалено або скасовано. + file error text + + + File server error: %@ + Помилка файлового сервера: %@ + file error text + + + File status + Статус файлу + No comment provided by engineer. + + + File status: %@ + Статус файлу: %@ + copied message info + File will be deleted from servers. Файл буде видалено з серверів. @@ -2629,6 +3604,11 @@ This cannot be undone! Файл: %@ No comment provided by engineer. + + Files + Файли + No comment provided by engineer. + Files & media Файли та медіа @@ -2639,11 +3619,16 @@ This cannot be undone! Файли і медіа chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. Файли та медіа в цій групі заборонені. No comment provided by engineer. + + Files and media not allowed + Файли та медіафайли заборонені + No comment provided by engineer. + Files and media prohibited! Файли та медіа заборонені! @@ -2656,10 +3641,12 @@ This cannot be undone! Finalize migration + Завершити міграцію No comment provided by engineer. Finalize migration on another device. + Завершіть міграцію на іншому пристрої. No comment provided by engineer. @@ -2702,13 +3689,116 @@ This cannot be undone! Виправлення не підтримується учасником групи No comment provided by engineer. + + For all moderators + No comment provided by engineer. + + + For chat profile %@: + Для профілю чату %@: + servers error + For console Для консолі No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + Наприклад, якщо ваш контакт отримує повідомлення через сервер SimpleX Chat, ваш додаток доставлятиме їх через сервер Flux. + No comment provided by engineer. + + + For me + No comment provided by engineer. + + + For private routing + Для приватної маршрутизації + No comment provided by engineer. + + + For social media + Для соціальних мереж + No comment provided by engineer. + + + Forward + Пересилання + chat item action + + + Forward %d message(s)? + Переслати %d повідомлення(ь)? + alert title + + + Forward and save messages + Пересилання та збереження повідомлень + No comment provided by engineer. + + + Forward messages + Пересилання повідомлень + alert action + + + Forward messages without files? + Пересилати повідомлення без файлів? + alert message + + + Forward up to 20 messages at once. + Пересилайте до 20 повідомлень одночасно. + No comment provided by engineer. + + + Forwarded + Переслано + No comment provided by engineer. + + + Forwarded from + Переслано з + No comment provided by engineer. + + + Forwarding %lld messages + Пересилання повідомлень %lld + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + Серверу переадресації %@ не вдалося з'єднатися з сервером призначення %@. Спробуйте пізніше. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + Адреса сервера переадресації несумісна з налаштуваннями мережі: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + Версія сервера переадресації несумісна з мережевими налаштуваннями: %@. + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + Сервер переадресації: %1$@ +Помилка сервера призначення: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + Сервер переадресації: %1$@ +Помилка: %2$@ + snd error text + Found desktop + Знайдено робочий стіл No comment provided by engineer. @@ -2726,13 +3816,9 @@ This cannot be undone! Повне ім'я (необов'язково) No comment provided by engineer. - - Full name: - Повне ім'я: - No comment provided by engineer. - Fully decentralized – visible only to members. + Повністю децентралізована - видима лише для учасників. No comment provided by engineer. @@ -2750,6 +3836,20 @@ This cannot be undone! GIF-файли та наклейки No comment provided by engineer. + + Get notified when mentioned. + No comment provided by engineer. + + + Good afternoon! + Доброго дня! + message preview + + + Good morning! + Доброго ранку! + message preview + Group Група @@ -2757,10 +3857,12 @@ This cannot be undone! Group already exists + Група вже існує No comment provided by engineer. Group already exists! + Група вже існує! No comment provided by engineer. @@ -2803,36 +3905,6 @@ This cannot be undone! Групові посилання No comment provided by engineer. - - Group members can add message reactions. - Учасники групи можуть додавати реакції на повідомлення. - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - Учасники групи можуть безповоротно видаляти надіслані повідомлення. (24 години) - No comment provided by engineer. - - - Group members can send direct messages. - Учасники групи можуть надсилати прямі повідомлення. - No comment provided by engineer. - - - Group members can send disappearing messages. - Учасники групи можуть надсилати зникаючі повідомлення. - No comment provided by engineer. - - - Group members can send files and media. - Учасники групи можуть надсилати файли та медіа. - No comment provided by engineer. - - - Group members can send voice messages. - Учасники групи можуть надсилати голосові повідомлення. - No comment provided by engineer. - Group message: Групове повідомлення: @@ -2873,11 +3945,19 @@ This cannot be undone! Група буде видалена для вас - це не може бути скасовано! No comment provided by engineer. + + Groups + No comment provided by engineer. + Help Довідка No comment provided by engineer. + + Help admins moderating their groups. + No comment provided by engineer. + Hidden Приховано @@ -2920,6 +4000,7 @@ This cannot be undone! History is not sent to new members. + Історія не надсилається новим учасникам. No comment provided by engineer. @@ -2927,10 +4008,19 @@ This cannot be undone! Як працює SimpleX No comment provided by engineer. + + How it affects privacy + Як це впливає на конфіденційність + No comment provided by engineer. + + + How it helps privacy + Як це захищає приватність + No comment provided by engineer. + How it works - Як це працює - No comment provided by engineer. + alert button How to @@ -2949,6 +4039,7 @@ This cannot be undone! Hungarian interface + Інтерфейс угорською мовою No comment provided by engineer. @@ -2956,6 +4047,11 @@ This cannot be undone! Сервери ICE (по одному на лінію) No comment provided by engineer. + + IP address + IP-адреса + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Якщо ви не можете зустрітися особисто, покажіть QR-код у відеодзвінку або поділіться посиланням. @@ -2996,8 +4092,8 @@ This cannot be undone! Негайно No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam Імунітет до спаму та зловживань No comment provided by engineer. @@ -3018,14 +4114,29 @@ This cannot be undone! Import failed + Не вдалося імпортувати + No comment provided by engineer. + + + Import theme + Імпорт теми No comment provided by engineer. Importing archive + Імпорт архіву + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Покращена доставка, зменшене використання трафіку. +Незабаром з'являться нові покращення! No comment provided by engineer. Improved message delivery + Покращена доставка повідомлень No comment provided by engineer. @@ -3040,6 +4151,7 @@ This cannot be undone! In order to continue, chat should be stopped. + Для того, щоб продовжити, чат слід зупинити. No comment provided by engineer. @@ -3047,6 +4159,19 @@ This cannot be undone! У відповідь на No comment provided by engineer. + + In-call sounds + Звуки вхідного дзвінка + No comment provided by engineer. + + + Inappropriate content + report reason + + + Inappropriate profile + report reason + Incognito Інкогніто @@ -3054,6 +4179,7 @@ This cannot be undone! Incognito groups + Групи інкогніто No comment provided by engineer. @@ -3088,6 +4214,7 @@ This cannot be undone! Incompatible version + Несумісна версія No comment provided by engineer. @@ -3115,6 +4242,11 @@ This cannot be undone! Встановіть [SimpleX Chat для терміналу](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + Миттєво + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3122,18 +4254,39 @@ This cannot be undone! No comment provided by engineer. - - Instantly - Миттєво - No comment provided by engineer. - Interface Інтерфейс No comment provided by engineer. + + Interface colors + Кольори інтерфейсу + No comment provided by engineer. + + + Invalid + token status text + + + Invalid (bad token) + token status text + + + Invalid (expired) + token status text + + + Invalid (unregistered) + token status text + + + Invalid (wrong topic) + token status text + Invalid QR code + Неправильний QR-код No comment provided by engineer. @@ -3143,28 +4296,33 @@ This cannot be undone! Invalid display name! + Неправильне ім'я користувача! No comment provided by engineer. Invalid link + Невірне посилання No comment provided by engineer. Invalid migration confirmation + Недійсне підтвердження міграції No comment provided by engineer. Invalid name! + Неправильне ім'я! No comment provided by engineer. Invalid response + Неправильна відповідь No comment provided by engineer. Invalid server address! Неправильна адреса сервера! - No comment provided by engineer. + alert title Invalid status @@ -3186,6 +4344,11 @@ This cannot be undone! Запросити учасників No comment provided by engineer. + + Invite to chat + Запросити в чат + No comment provided by engineer. + Invite to group Запросити до групи @@ -3201,8 +4364,8 @@ This cannot be undone! У цьому чаті заборонено безповоротне видалення повідомлень. No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. У цій групі заборонено безповоротне видалення повідомлень. No comment provided by engineer. @@ -3227,6 +4390,11 @@ This cannot be undone! 3. З'єднання було скомпрометовано. No comment provided by engineer. + + It protects your IP address and connections. + Він захищає вашу IP-адресу та з'єднання. + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). Схоже, що ви вже підключені за цим посиланням. Якщо це не так, сталася помилка (%@). @@ -3245,7 +4413,7 @@ This cannot be undone! Join Приєднуйтесь - No comment provided by engineer. + swipe action Join group @@ -3254,10 +4422,12 @@ This cannot be undone! Join group conversations + Приєднуйтесь до групових розмов No comment provided by engineer. Join group? + Приєднатися до групи? No comment provided by engineer. @@ -3267,11 +4437,14 @@ This cannot be undone! Join with current profile + Приєднатися з поточним профілем No comment provided by engineer. Join your group? This is your link for group %@! + Приєднатися до групи? +Це ваше посилання на групу %@! No comment provided by engineer. @@ -3281,15 +4454,23 @@ This is your link for group %@! Keep + Тримай + alert action + + + Keep conversation + Підтримуйте розмову No comment provided by engineer. Keep the app open to use it from desktop + Тримайте додаток відкритим, щоб використовувати його з робочого столу No comment provided by engineer. Keep unused invitation? - No comment provided by engineer. + Зберігати невикористані запрошення? + alert title Keep your connections @@ -3324,6 +4505,16 @@ This is your link for group %@! Leave Залишити + swipe action + + + Leave chat + Вийти з чату + No comment provided by engineer. + + + Leave chat? + Залишити чат? No comment provided by engineer. @@ -3353,14 +4544,29 @@ This is your link for group %@! Link mobile and desktop apps! 🔗 + Зв'яжіть мобільні та десктопні додатки! 🔗 No comment provided by engineer. Linked desktop options + Параметри пов'язаного робочого столу No comment provided by engineer. Linked desktops + Пов'язані робочі столи + No comment provided by engineer. + + + List + swipe action + + + List name and emoji should be different for all lists. + No comment provided by engineer. + + + List name... No comment provided by engineer. @@ -3373,11 +4579,6 @@ This is your link for group %@! Живі повідомлення No comment provided by engineer. - - Local - Локально - No comment provided by engineer. - Local name Місцева назва @@ -3398,11 +4599,6 @@ This is your link for group %@! Режим блокування No comment provided by engineer. - - Make a private connection - Створіть приватне з'єднання - No comment provided by engineer. - Make one message disappear Зробити так, щоб одне повідомлення зникло @@ -3413,21 +4609,11 @@ This is your link for group %@! Зробіть профіль приватним! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Переконайтеся, що адреси серверів %@ мають правильний формат, розділені рядками і не дублюються (%@). - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Переконайтеся, що адреси серверів WebRTC ICE мають правильний формат, розділені рядками і не дублюються. No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Багато людей запитували: *якщо SimpleX не має ідентифікаторів користувачів, як він може доставляти повідомлення?* - No comment provided by engineer. - Mark deleted for everyone Позначити видалено для всіх @@ -3453,11 +4639,35 @@ This is your link for group %@! Максимум 30 секунд, отримується миттєво. No comment provided by engineer. + + Media & file servers + Медіа та файлові сервери + No comment provided by engineer. + + + Medium + Середній + blur media + Member Учасник No comment provided by engineer. + + Member inactive + Користувач неактивний + item status text + + + Member reports + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + Роль учасника буде змінено на "%@". Усі учасники чату отримають сповіщення. + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. Роль учасника буде змінено на "%@". Всі учасники групи будуть повідомлені про це. @@ -3468,11 +4678,64 @@ This is your link for group %@! Роль учасника буде змінено на "%@". Учасник отримає нове запрошення. No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + Учасника буде видалено з чату – це неможливо скасувати! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! Учасник буде видалений з групи - це неможливо скасувати! No comment provided by engineer. + + Members can add message reactions. + Учасники групи можуть додавати реакції на повідомлення. + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + Учасники групи можуть безповоротно видаляти надіслані повідомлення. (24 години) + No comment provided by engineer. + + + Members can report messsages to moderators. + No comment provided by engineer. + + + Members can send SimpleX links. + Учасники групи можуть надсилати посилання SimpleX. + No comment provided by engineer. + + + Members can send direct messages. + Учасники групи можуть надсилати прямі повідомлення. + No comment provided by engineer. + + + Members can send disappearing messages. + Учасники групи можуть надсилати зникаючі повідомлення. + No comment provided by engineer. + + + Members can send files and media. + Учасники групи можуть надсилати файли та медіа. + No comment provided by engineer. + + + Members can send voice messages. + Учасники групи можуть надсилати голосові повідомлення. + No comment provided by engineer. + + + Mention members 👋 + No comment provided by engineer. + + + Menus + Меню + No comment provided by engineer. + Message delivery error Помилка доставки повідомлення @@ -3483,11 +4746,31 @@ This is your link for group %@! Підтвердження доставки повідомлення! No comment provided by engineer. + + Message delivery warning + Попередження про доставку повідомлення + item status text + Message draft Чернетка повідомлення No comment provided by engineer. + + Message forwarded + Повідомлення переслано + item status text + + + Message may be delivered later if member becomes active. + Повідомлення може бути доставлене пізніше, якщо користувач стане активним. + item status description + + + Message queue info + Інформація про чергу повідомлень + No comment provided by engineer. + Message reactions Реакції на повідомлення @@ -3498,11 +4781,41 @@ This is your link for group %@! Реакції на повідомлення в цьому чаті заборонені. No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. Реакції на повідомлення в цій групі заборонені. No comment provided by engineer. + + Message reception + Прийом повідомлень + No comment provided by engineer. + + + Message servers + Сервери повідомлень + No comment provided by engineer. + + + Message shape + Форма повідомлення + No comment provided by engineer. + + + Message source remains private. + Джерело повідомлення залишається приватним. + No comment provided by engineer. + + + Message status + Статус повідомлення + No comment provided by engineer. + + + Message status: %@ + Статус повідомлення: %@ + copied message info + Message text Текст повідомлення @@ -3510,6 +4823,7 @@ This is your link for group %@! Message too large + Повідомлення занадто велике No comment provided by engineer. @@ -3524,38 +4838,66 @@ This is your link for group %@! Messages from %@ will be shown! + Повідомлення від %@ будуть показані! No comment provided by engineer. + + Messages in this chat will never be deleted. + alert message + + + Messages received + Отримані повідомлення + No comment provided by engineer. + + + Messages sent + Надіслані повідомлення + No comment provided by engineer. + + + Messages were deleted after you selected them. + Повідомлення були видалені після того, як ви їх вибрали. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + Повідомлення, файли та дзвінки захищені **наскрізним шифруванням** з ідеальною секретністю переадресації, відмовою та відновленням після злому. No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + Повідомлення, файли та дзвінки захищені **квантово-стійким шифруванням e2e** з ідеальною секретністю переадресації, відмовою та відновленням після злому. No comment provided by engineer. Migrate device + Перенести пристрій No comment provided by engineer. Migrate from another device + Перехід з іншого пристрою No comment provided by engineer. Migrate here + Мігруйте сюди No comment provided by engineer. Migrate to another device + Перехід на інший пристрій No comment provided by engineer. Migrate to another device via QR code. + Перейдіть на інший пристрій за допомогою QR-коду. No comment provided by engineer. Migrating + Міграція No comment provided by engineer. @@ -3565,6 +4907,7 @@ This is your link for group %@! Migration complete + Міграція завершена No comment provided by engineer. @@ -3582,9 +4925,9 @@ This is your link for group %@! Міграцію завершено No comment provided by engineer. - - Migrations: %@ - Міграції: %@ + + Migrations: + Міграції: No comment provided by engineer. @@ -3602,21 +4945,30 @@ This is your link for group %@! Модерується за: %@ copied message info + + More + swipe action + More improvements are coming soon! Незабаром буде ще більше покращень! No comment provided by engineer. + + More reliable network connection. + Більш надійне з'єднання з мережею. + No comment provided by engineer. + + + More reliable notifications + Більш надійні сповіщення + No comment provided by engineer. + Most likely this connection is deleted. Швидше за все, це з'єднання видалено. item status description - - Most likely this contact has deleted the connection with you. - Швидше за все, цей контакт видалив зв'язок з вами. - No comment provided by engineer. - Multiple chat profiles Кілька профілів чату @@ -3625,7 +4977,11 @@ This is your link for group %@! Mute Вимкнути звук - No comment provided by engineer. + notification label action + + + Mute all + notification label action Muted when inactive! @@ -3635,13 +4991,38 @@ This is your link for group %@! Name Ім'я - No comment provided by engineer. + swipe action Network & servers Мережа та сервери No comment provided by engineer. + + Network connection + Підключення до мережі + No comment provided by engineer. + + + Network decentralization + Децентралізація мережі + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + Проблеми з мережею - термін дії повідомлення закінчився після багатьох спроб надіслати його. + snd error text + + + Network management + Керування мережею + No comment provided by engineer. + + + Network operator + Мережевий оператор + No comment provided by engineer. + Network settings Налаштування мережі @@ -3652,13 +5033,33 @@ This is your link for group %@! Стан мережі No comment provided by engineer. + + New + token status text + New Passcode Новий пароль No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Нові облікові дані SOCKS будуть використовуватися при кожному запуску програми. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Для кожного сервера будуть використовуватися нові облікові дані SOCKS. + No comment provided by engineer. + New chat + Новий чат + No comment provided by engineer. + + + New chat experience 🎉 + Новий досвід спілкування в чаті 🎉 No comment provided by engineer. @@ -3671,13 +5072,9 @@ This is your link for group %@! Новий контакт: notification - - New database archive - Новий архів бази даних - No comment provided by engineer. - New desktop app! + Новий десктопний додаток! No comment provided by engineer. @@ -3685,11 +5082,21 @@ This is your link for group %@! Нове ім'я відображення No comment provided by engineer. + + New events + Нові події + notification + New in %@ Нове в %@ No comment provided by engineer. + + New media options + Нові медіа-опції + No comment provided by engineer. + New member role Нова роль учасника @@ -3705,6 +5112,11 @@ This is your link for group %@! Новий пароль… No comment provided by engineer. + + New server + Новий сервер + No comment provided by engineer. + No Ні @@ -3715,6 +5127,18 @@ This is your link for group %@! Немає пароля програми Authentication unavailable + + No chats + No comment provided by engineer. + + + No chats found + No comment provided by engineer. + + + No chats in list %@ + No comment provided by engineer. + No contacts selected Не вибрано жодного контакту @@ -3735,6 +5159,11 @@ This is your link for group %@! Токен пристрою відсутній! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + Прямого зв'язку ще немає, повідомлення пересилається адміністратором. + item status description + No filtered chats Немає фільтрованих чатів @@ -3750,20 +5179,107 @@ This is your link for group %@! Немає історії No comment provided by engineer. + + No info, try to reload + Немає інформації, спробуйте перезавантажити + No comment provided by engineer. + + + No media & file servers. + Ніяких медіа та файлових серверів. + servers error + + + No message + No comment provided by engineer. + + + No message servers. + Ніяких серверів повідомлень. + servers error + + + No network connection + Немає підключення до мережі + No comment provided by engineer. + + + No permission to record speech + Немає дозволу на запис промови + No comment provided by engineer. + + + No permission to record video + Немає дозволу на запис відео + No comment provided by engineer. + No permission to record voice message Немає дозволу на запис голосового повідомлення No comment provided by engineer. + + No push server + Локально + No comment provided by engineer. + No received or sent files Немає отриманих або відправлених файлів No comment provided by engineer. + + No servers for private message routing. + Немає серверів для маршрутизації приватних повідомлень. + servers error + + + No servers to receive files. + Немає серверів для отримання файлів. + servers error + + + No servers to receive messages. + Немає серверів для отримання повідомлень. + servers error + + + No servers to send files. + Немає серверів для надсилання файлів. + servers error + + + No token! + alert title + + + No unread chats + No comment provided by engineer. + + + No user identifiers. + Ніяких ідентифікаторів користувачів. + No comment provided by engineer. + Not compatible! + Не сумісні! No comment provided by engineer. + + Notes + No comment provided by engineer. + + + Nothing selected + Нічого не вибрано + No comment provided by engineer. + + + Nothing to forward! + Нічого пересилати! + alert title + Notifications Сповіщення @@ -3774,6 +5290,19 @@ This is your link for group %@! Сповіщення вимкнено! No comment provided by engineer. + + Notifications error + alert title + + + Notifications privacy + Сповіщення про приватність + No comment provided by engineer. + + + Notifications status + alert title + Now admins can: - delete members' messages. @@ -3785,41 +5314,41 @@ This is your link for group %@! OK + ОК No comment provided by engineer. Off Вимкнено - No comment provided by engineer. + blur media Ok Гаразд - No comment provided by engineer. + alert button Old database Стара база даних No comment provided by engineer. - - Old database archive - Старий архів бази даних - No comment provided by engineer. - One-time invitation link Посилання на одноразове запрошення No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Для підключення будуть потрібні хости onion. Потрібно увімкнути VPN. + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Для підключення будуть потрібні хости onion. +Потрібно увімкнути VPN. No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - Onion хости будуть використовуватися, коли вони будуть доступні. Потрібно увімкнути VPN. + + Onion hosts will be used when available. +Requires compatible VPN. + Onion хости будуть використовуватися, коли вони будуть доступні. +Потрібно увімкнути VPN. No comment provided by engineer. @@ -3827,11 +5356,21 @@ This is your link for group %@! Onion хости не будуть використовуватися. No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + Лише власники чату можуть змінювати налаштування. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. Тільки клієнтські пристрої зберігають профілі користувачів, контакти, групи та повідомлення, надіслані за допомогою **2-шарового наскрізного шифрування**. No comment provided by engineer. + + Only delete conversation + Видаляйте тільки розмови + No comment provided by engineer. + Only group owners can change group preferences. Тільки власники груп можуть змінювати налаштування групи. @@ -3847,6 +5386,14 @@ This is your link for group %@! Тільки власники груп можуть вмикати голосові повідомлення. No comment provided by engineer. + + Only sender and moderators see it + No comment provided by engineer. + + + Only you and moderators see it + No comment provided by engineer. + Only you can add message reactions. Тільки ви можете додавати реакції на повідомлення. @@ -3899,13 +5446,19 @@ This is your link for group %@! Open - No comment provided by engineer. + Відкрито + alert action Open Settings Відкрийте Налаштування No comment provided by engineer. + + Open changes + Відкриті зміни + No comment provided by engineer. + Open chat Відкритий чат @@ -3916,44 +5469,86 @@ This is your link for group %@! Відкрийте консоль чату authentication reason + + Open conditions + Відкриті умови + No comment provided by engineer. + Open group + Відкрита група No comment provided by engineer. + + Open link? + alert title + Open migration to another device + Відкрита міграція на інший пристрій authentication reason - - Open user profiles - Відкрити профілі користувачів - authentication reason - - - Open-source protocol and code – anybody can run the servers. - Протокол і код з відкритим вихідним кодом - будь-хто може запускати сервери. - No comment provided by engineer. - Opening app… + Відкриваємо програму… + No comment provided by engineer. + + + Operator + Оператор + No comment provided by engineer. + + + Operator server + Сервер оператора + alert title + + + Or import archive file + Або імпортуйте архівний файл No comment provided by engineer. Or paste archive link + Або вставте посилання на архів No comment provided by engineer. Or scan QR code + Або відскануйте QR-код No comment provided by engineer. Or securely share this file link + Або безпечно поділіться цим посиланням на файл No comment provided by engineer. Or show this code + Або покажіть цей код No comment provided by engineer. + + Or to share privately + Або поділитися приватно + No comment provided by engineer. + + + Organize chats into lists + No comment provided by engineer. + + + Other + Інше + No comment provided by engineer. + + + Other file errors: +%@ + Інші помилки файлів: +%@ + alert message + PING count Кількість PING @@ -3989,6 +5584,11 @@ This is your link for group %@! Пароль встановлено! No comment provided by engineer. + + Password + Пароль + No comment provided by engineer. + Password to show Показати пароль @@ -3996,10 +5596,12 @@ This is your link for group %@! Past member %@ + Колишній учасник %@ past/unknown group member Paste desktop address + Вставте адресу робочого столу No comment provided by engineer. @@ -4009,19 +5611,21 @@ This is your link for group %@! Paste link to connect! + Вставте посилання для підключення! No comment provided by engineer. Paste the link you received + Вставте отримане посилання No comment provided by engineer. - - People can connect to you only via the links you share. - Люди можуть зв'язатися з вами лише за посиланнями, якими ви ділитеся. + + Pending + В очікуванні No comment provided by engineer. - - Periodically + + Periodic Періодично No comment provided by engineer. @@ -4032,6 +5636,17 @@ This is your link for group %@! Picture-in-picture calls + Дзвінки "картинка в картинці + No comment provided by engineer. + + + Play from the chat list. + Грати зі списку чату. + No comment provided by engineer. + + + Please ask your contact to enable calls. + Будь ласка, попросіть свого контакту ввімкнути дзвінки. No comment provided by engineer. @@ -4039,6 +5654,13 @@ This is your link for group %@! Будь ласка, попросіть вашого контакту увімкнути відправку голосових повідомлень. No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + Переконайтеся, що мобільний і настільний комп'ютери підключені до однієї локальної мережі, і що брандмауер настільного комп'ютера дозволяє з'єднання. +Будь ласка, повідомте про будь-які інші проблеми розробникам. + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. Будь ласка, перевірте, чи ви скористалися правильним посиланням, або попросіть контактну особу надіслати вам інше. @@ -4056,11 +5678,14 @@ This is your link for group %@! Please confirm that network settings are correct for this device. + Переконайтеся, що налаштування мережі для цього пристрою є правильними. No comment provided by engineer. Please contact developers. Error: %@ + Зверніться до розробників. +Помилка: %@ No comment provided by engineer. @@ -4103,59 +5728,115 @@ Error: %@ Будь ласка, зберігайте пароль надійно, ви НЕ зможете змінити його, якщо втратите. No comment provided by engineer. + + Please try to disable and re-enable notfications. + token info + + + Please wait for token activation to complete. + token info + + + Please wait for token to be registered. + token info + Polish interface Польський інтерфейс No comment provided by engineer. + + Port + Порт + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Можливо, в адресі сервера неправильно вказано відбиток сертифіката server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. Зберегти чернетку останнього повідомлення з вкладеннями. No comment provided by engineer. - - Preset server - Попередньо встановлений сервер - No comment provided by engineer. - Preset server address Попередньо встановлена адреса сервера No comment provided by engineer. + + Preset servers + Попередньо встановлені сервери + No comment provided by engineer. + Preview Попередній перегляд No comment provided by engineer. + + Previously connected servers + Раніше підключені сервери + No comment provided by engineer. + Privacy & security Конфіденційність і безпека No comment provided by engineer. + + Privacy for your customers. + Конфіденційність для ваших клієнтів. + No comment provided by engineer. + + + Privacy policy and conditions of use. + No comment provided by engineer. + Privacy redefined Конфіденційність переглянута No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + No comment provided by engineer. + Private filenames Приватні імена файлів No comment provided by engineer. + + Private media file names. + No comment provided by engineer. + + + Private message routing + Маршрутизація приватних повідомлень + No comment provided by engineer. + + + Private message routing 🚀 + Маршрутизація приватних повідомлень 🚀 + No comment provided by engineer. + Private notes + Приватні нотатки name of notes to self + + Private routing + Приватна маршрутизація + No comment provided by engineer. + + + Private routing error + Помилка приватної маршрутизації + No comment provided by engineer. + Profile and server connections З'єднання профілю та сервера @@ -4166,12 +5847,9 @@ Error: %@ Зображення профілю No comment provided by engineer. - - Profile name - No comment provided by engineer. - - - Profile name: + + Profile images + Зображення профілю No comment provided by engineer. @@ -4179,10 +5857,15 @@ Error: %@ Пароль до профілю No comment provided by engineer. + + Profile theme + Тема профілю + No comment provided by engineer. + Profile update will be sent to your contacts. Оновлення профілю буде надіслано вашим контактам. - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4204,6 +5887,15 @@ Error: %@ Заборонити реакції на повідомлення. No comment provided by engineer. + + Prohibit reporting messages to moderators. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + Заборонити надсилання посилань SimpleX. + No comment provided by engineer. + Prohibit sending direct messages to members. Заборонити надсилати прямі повідомлення учасникам. @@ -4224,11 +5916,23 @@ Error: %@ Заборонити надсилання голосових повідомлень. No comment provided by engineer. + + Protect IP address + Захист IP-адреси + No comment provided by engineer. + Protect app screen Захистіть екран програми No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + Захистіть свою IP-адресу від ретрансляторів повідомлень, обраних вашими контактами. +Увімкніть у налаштуваннях *Мережа та сервери*. + No comment provided by engineer. + Protect your chat profiles with a password! Захистіть свої профілі чату паролем! @@ -4244,17 +5948,34 @@ Error: %@ Тайм-аут протоколу на КБ No comment provided by engineer. + + Proxied + Проксі-сервер + No comment provided by engineer. + + + Proxied servers + Проксі-сервери + No comment provided by engineer. + + + Proxy requires password + Проксі вимагає пароль + No comment provided by engineer. + Push notifications - Push-повідомлення + Push-сповіщення No comment provided by engineer. Push server + Push-сервер No comment provided by engineer. Quantum resistant encryption + Квантово-стійке шифрування No comment provided by engineer. @@ -4262,6 +5983,11 @@ Error: %@ Оцініть додаток No comment provided by engineer. + + Reachable chat toolbar + Доступна панель інструментів чату + No comment provided by engineer. + React… Реагуй… @@ -4270,20 +5996,21 @@ Error: %@ Read Читати - No comment provided by engineer. + swipe action Read more Читати далі No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - Читайте більше в [Посібнику користувача](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). + Читайте більше в [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). + No comment provided by engineer. + + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + Читайте більше в [Посібнику користувача](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). No comment provided by engineer. @@ -4291,11 +6018,6 @@ Error: %@ Читайте більше в [Посібнику користувача](https://simplex.chat/docs/guide/readme.html#connect-to-friends). No comment provided by engineer. - - Read more in our GitHub repository. - Читайте більше в нашому репозиторії на GitHub. - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). Читайте більше в нашому [GitHub репозиторії](https://github.com/simplex-chat/simplex-chat#readme). @@ -4306,6 +6028,11 @@ Error: %@ Підтвердження виключені No comment provided by engineer. + + Receive errors + Отримання помилок + No comment provided by engineer. + Received at Отримано за @@ -4326,6 +6053,21 @@ Error: %@ Отримано повідомлення message info title + + Received messages + Отримані повідомлення + No comment provided by engineer. + + + Received reply + Отримано відповідь + No comment provided by engineer. + + + Received total + Отримано всього + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. Адреса отримувача буде змінена на інший сервер. Зміна адреси завершиться після того, як відправник з'явиться в мережі. @@ -4343,6 +6085,12 @@ Error: %@ Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). + Нещодавня історія та покращення [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). + No comment provided by engineer. + + + Recipient(s) can't see who this message is from. + Одержувач(и) не бачить, від кого це повідомлення. No comment provided by engineer. @@ -4350,11 +6098,36 @@ Error: %@ Одержувачі бачать оновлення, коли ви їх вводите. No comment provided by engineer. + + Reconnect + Повторне підключення + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. Перепідключіть всі підключені сервери, щоб примусово доставити повідомлення. Це використовує додатковий трафік. No comment provided by engineer. + + Reconnect all servers + Перепідключіть усі сервери + No comment provided by engineer. + + + Reconnect all servers? + Перепідключити всі сервери? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + Перепідключити сервер для примусової доставки повідомлень. Використовує додатковий трафік. + No comment provided by engineer. + + + Reconnect server? + Перепідключити сервер? + No comment provided by engineer. + Reconnect servers? Перепідключити сервери? @@ -4375,10 +6148,23 @@ Error: %@ Зменшення використання акумулятора No comment provided by engineer. + + Register + No comment provided by engineer. + + + Register notification token? + token info + + + Registered + token status text + Reject Відхилити - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4405,6 +6191,16 @@ Error: %@ Видалити No comment provided by engineer. + + Remove archive? + Видалити архів? + No comment provided by engineer. + + + Remove image + Видалити зображення + No comment provided by engineer. + Remove member Видалити учасника @@ -4437,22 +6233,27 @@ Error: %@ Repeat connection request? + Повторити запит на підключення? No comment provided by engineer. Repeat download + Повторити завантаження No comment provided by engineer. Repeat import + Повторний імпорт No comment provided by engineer. Repeat join request? + Повторити запит на приєднання? No comment provided by engineer. Repeat upload + Повторне завантаження No comment provided by engineer. @@ -4460,6 +6261,46 @@ Error: %@ Відповісти chat item action + + Report + chat item action + + + Report content: only group moderators will see it. + report reason + + + Report member profile: only group moderators will see it. + report reason + + + Report other: only group moderators will see it. + report reason + + + Report reason? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + report reason + + + Report violation: only group moderators will see it. + report reason + + + Report: %@ + report in notification + + + Reporting messages to moderators is prohibited. + No comment provided by engineer. + + + Reports + No comment provided by engineer. + Required Потрібно @@ -4470,16 +6311,41 @@ Error: %@ Перезавантаження No comment provided by engineer. + + Reset all hints + Скинути всі підказки + No comment provided by engineer. + + + Reset all statistics + Скинути всю статистику + No comment provided by engineer. + + + Reset all statistics? + Скинути всю статистику? + No comment provided by engineer. + Reset colors Скинути кольори No comment provided by engineer. + + Reset to app theme + Повернутися до теми програми + No comment provided by engineer. + Reset to defaults Відновити налаштування за замовчуванням No comment provided by engineer. + + Reset to user theme + Повернутися до теми користувача + No comment provided by engineer. + Restart the app to create a new chat profile Перезапустіть програму, щоб створити новий профіль чату @@ -4512,6 +6378,7 @@ Error: %@ Retry + Спробуйте ще раз No comment provided by engineer. @@ -4519,9 +6386,9 @@ Error: %@ Показувати chat item action - - Revert - Повернутися + + Review conditions + Умови перегляду No comment provided by engineer. @@ -4549,55 +6416,66 @@ Error: %@ Запустити чат No comment provided by engineer. - - SMP servers - Сервери SMP + + SMP server + Сервер SMP + No comment provided by engineer. + + + SOCKS proxy + Проксі SOCKS + No comment provided by engineer. + + + Safely receive files + Безпечне отримання файлів No comment provided by engineer. Safer groups + Безпечніші групи No comment provided by engineer. Save Зберегти - chat item action + alert button +chat item action Save (and notify contacts) Зберегти (і повідомити контактам) - No comment provided by engineer. + alert button Save and notify contact Зберегти та повідомити контакт - No comment provided by engineer. + alert button Save and notify group members Зберегти та повідомити учасників групи No comment provided by engineer. + + Save and reconnect + Збережіть і підключіться знову + No comment provided by engineer. + Save and update group profile Збереження та оновлення профілю групи No comment provided by engineer. - - Save archive - Зберегти архів - No comment provided by engineer. - - - Save auto-accept settings - Зберегти налаштування автоприйому - No comment provided by engineer. - Save group profile Зберегти профіль групи No comment provided by engineer. + + Save list + No comment provided by engineer. + Save passphrase and open chat Збережіть пароль і відкрийте чат @@ -4610,8 +6488,8 @@ Error: %@ Save preferences? - Зберегти налаштування? - No comment provided by engineer. + Зберегти настройки? + alert title Save profile password @@ -4626,27 +6504,53 @@ Error: %@ Save servers? Зберегти сервери? - No comment provided by engineer. - - - Save settings? - Зберегти налаштування? - No comment provided by engineer. + alert title Save welcome message? Зберегти вітальне повідомлення? No comment provided by engineer. + + Save your profile? + Зберегти свій профіль? + alert title + + + Saved + Збережено + No comment provided by engineer. + Saved WebRTC ICE servers will be removed Збережені сервери WebRTC ICE буде видалено No comment provided by engineer. + + Saved from + Збережено з + No comment provided by engineer. + Saved message + Збережене повідомлення message info title + + Saving %lld messages + Збереження повідомлень %lld + No comment provided by engineer. + + + Scale + Масштаб + No comment provided by engineer. + + + Scan / Paste link + Відсканувати / Вставити посилання + No comment provided by engineer. + Scan QR code Відскануйте QR-код @@ -4654,6 +6558,7 @@ Error: %@ Scan QR code from desktop + Відскануйте QR-код з робочого столу No comment provided by engineer. @@ -4678,10 +6583,17 @@ Error: %@ Search bar accepts invitation links. + Рядок пошуку приймає посилання-запрошення. No comment provided by engineer. Search or paste SimpleX link + Знайдіть або вставте посилання SimpleX + No comment provided by engineer. + + + Secondary + Вторинний No comment provided by engineer. @@ -4689,6 +6601,11 @@ Error: %@ Безпечна черга server test step + + Secured + Забезпечено + No comment provided by engineer. + Security assessment Оцінка безпеки @@ -4702,6 +6619,21 @@ Error: %@ Select Виберіть + chat item action + + + Select chat profile + Виберіть профіль чату + No comment provided by engineer. + + + Selected %lld + Вибрано %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Вибрані налаштування чату забороняють це повідомлення. No comment provided by engineer. @@ -4739,13 +6671,9 @@ Error: %@ Надсилання звітів про доставку No comment provided by engineer. - - Send direct message - Надішліть пряме повідомлення - No comment provided by engineer. - Send direct message to connect + Надішліть пряме повідомлення, щоб підключитися No comment provided by engineer. @@ -4753,6 +6681,11 @@ Error: %@ Надіслати зникаюче повідомлення No comment provided by engineer. + + Send errors + Помилки надсилання + No comment provided by engineer. + Send link previews Надіслати попередній перегляд за посиланням @@ -4763,14 +6696,28 @@ Error: %@ Надіслати живе повідомлення No comment provided by engineer. + + Send message to enable calls. + Надішліть повідомлення, щоб увімкнути дзвінки. + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + Надсилайте повідомлення напряму, якщо IP-адреса захищена, а ваш сервер або сервер призначення не підтримує приватну маршрутизацію. + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + Надсилайте повідомлення напряму, якщо ваш сервер або сервер призначення не підтримує приватну маршрутизацію. + No comment provided by engineer. + Send notifications Надсилати сповіщення No comment provided by engineer. - - Send notifications: - Надсилати сповіщення: + + Send private reports No comment provided by engineer. @@ -4790,12 +6737,13 @@ Error: %@ Send up to 100 last messages to new members. + Надішліть до 100 останніх повідомлень новим користувачам. No comment provided by engineer. Sender cancelled file transfer. Відправник скасував передачу файлу. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4852,6 +6800,11 @@ Error: %@ Надіслано за: %@ copied message info + + Sent directly + Відправлено напряму + No comment provided by engineer. + Sent file event Подія надісланого файлу @@ -4862,11 +6815,71 @@ Error: %@ Надіслано повідомлення message info title + + Sent messages + Надіслані повідомлення + No comment provided by engineer. + Sent messages will be deleted after set time. Надіслані повідомлення будуть видалені через встановлений час. No comment provided by engineer. + + Sent reply + Надіслано відповідь + No comment provided by engineer. + + + Sent total + Відправлено всього + No comment provided by engineer. + + + Sent via proxy + Відправлено через проксі + No comment provided by engineer. + + + Server + Сервер + No comment provided by engineer. + + + Server added to operator %@. + Сервер додано до оператора %@. + alert message + + + Server address + Адреса сервера + No comment provided by engineer. + + + Server address is incompatible with network settings. + Адреса сервера несумісна з налаштуваннями мережі. + srv error text. + + + Server address is incompatible with network settings: %@. + Адреса сервера несумісна з налаштуваннями мережі: %@. + No comment provided by engineer. + + + Server operator changed. + Оператор сервера змінився. + alert title + + + Server operators + Оператори серверів + No comment provided by engineer. + + + Server protocol changed. + Протокол сервера змінено. + alert title + Server requires authorization to create queues, check password Сервер вимагає авторизації для створення черг, перевірте пароль @@ -4882,13 +6895,39 @@ Error: %@ Тест сервера завершився невдало! No comment provided by engineer. + + Server type + Тип сервера + No comment provided by engineer. + + + Server version is incompatible with network settings. + Серверна версія несумісна з мережевими налаштуваннями. + srv error text + + + Server version is incompatible with your app: %@. + Версія сервера несумісна з вашим додатком: %@. + No comment provided by engineer. + Servers Сервери No comment provided by engineer. + + Servers info + Інформація про сервери + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + Статистика серверів буде скинута - це неможливо скасувати! + No comment provided by engineer. + Session code + Код сесії No comment provided by engineer. @@ -4896,11 +6935,20 @@ Error: %@ Встановити 1 день No comment provided by engineer. + + Set chat name… + No comment provided by engineer. + Set contact name… Встановити ім'я контакту… No comment provided by engineer. + + Set default theme + Встановлення теми за замовчуванням + No comment provided by engineer. + Set group preferences Встановіть налаштування групи @@ -4911,6 +6959,10 @@ Error: %@ Встановіть його замість аутентифікації системи. No comment provided by engineer. + + Set message expiration in chats. + No comment provided by engineer. + Set passcode Встановити пароль @@ -4918,6 +6970,7 @@ Error: %@ Set passphrase + Встановити парольну фразу No comment provided by engineer. @@ -4940,24 +6993,55 @@ Error: %@ Налаштування No comment provided by engineer. + + Settings were changed. + Налаштування були змінені. + alert message + + + Shape profile images + Сформуйте зображення профілю + No comment provided by engineer. + Share Поділіться - chat item action + alert action +chat item action Share 1-time link Поділитися 1-разовим посиланням No comment provided by engineer. + + Share 1-time link with a friend + Поділіться одноразовим посиланням з другом + No comment provided by engineer. + + + Share SimpleX address on social media. + Поділіться адресою SimpleX у соціальних мережах. + No comment provided by engineer. + Share address Поділитися адресою No comment provided by engineer. + + Share address publicly + Поділіться адресою публічно + No comment provided by engineer. + Share address with contacts? Поділіться адресою з контактами? + alert title + + + Share from other apps. + Діліться з інших програм. No comment provided by engineer. @@ -4965,8 +7049,19 @@ Error: %@ Поділіться посиланням No comment provided by engineer. + + Share profile + Поділіться профілем + No comment provided by engineer. + Share this 1-time invite link + Поділіться цим одноразовим посиланням-запрошенням + No comment provided by engineer. + + + Share to SimpleX + Поділіться з SimpleX No comment provided by engineer. @@ -4974,8 +7069,13 @@ Error: %@ Поділіться з контактами No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code + Показати QR-код No comment provided by engineer. @@ -4993,21 +7093,46 @@ Error: %@ Показати останні повідомлення No comment provided by engineer. + + Show message status + Показати статус повідомлення + No comment provided by engineer. + + + Show percentage + Показати відсоток + No comment provided by engineer. + Show preview Показати попередній перегляд No comment provided by engineer. + + Show → on messages sent via private routing. + Показувати → у повідомленнях, надісланих через приватну маршрутизацію. + No comment provided by engineer. + Show: Показати: No comment provided by engineer. + + SimpleX + SimpleX + No comment provided by engineer. + SimpleX Address Адреса SimpleX No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + SimpleX Chat і Flux уклали угоду про включення серверів, керованих Flux, у додаток. + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. Безпека SimpleX Chat була перевірена компанією Trail of Bits. @@ -5038,6 +7163,20 @@ Error: %@ Адреса SimpleX No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + SimpleX-адреси та одноразові посилання можна безпечно ділитися через будь-який месенджер. + No comment provided by engineer. + + + SimpleX address or 1-time link? + SimpleX адреса або одноразове посилання? + No comment provided by engineer. + + + SimpleX channel link + simplex link type + SimpleX contact address Контактна адреса SimpleX @@ -5056,6 +7195,16 @@ Error: %@ SimpleX links Посилання SimpleX + chat feature + + + SimpleX links are prohibited. + У цій групі заборонені посилання на SimpleX. + No comment provided by engineer. + + + SimpleX links not allowed + Посилання SimpleX заборонені No comment provided by engineer. @@ -5063,8 +7212,19 @@ Error: %@ Одноразове запрошення SimpleX simplex link type + + SimpleX protocols reviewed by Trail of Bits. + Протоколи SimpleX, розглянуті Trail of Bits. + No comment provided by engineer. + Simplified incognito mode + Спрощений режим інкогніто + No comment provided by engineer. + + + Size + Розмір No comment provided by engineer. @@ -5082,16 +7242,53 @@ Error: %@ Невеликі групи (максимум 20 осіб) No comment provided by engineer. + + Soft + М'який + blur media + + + Some app settings were not migrated. + Деякі налаштування програми не були перенесені. + No comment provided by engineer. + + + Some file(s) were not exported: + Деякі файли не було експортовано: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. Під час імпорту виникли деякі нефатальні помилки – ви можете переглянути консоль чату, щоб дізнатися більше. No comment provided by engineer. + + Some non-fatal errors occurred during import: + Під час імпорту виникли деякі несмертельні помилки: + No comment provided by engineer. + + + Some servers failed the test: +%@ + Деякі сервери не пройшли тестування: +%@ + alert message + Somebody Хтось notification title + + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + Квадрат, коло або щось середнє між ними. + No comment provided by engineer. + Start chat Почати чат @@ -5099,6 +7296,7 @@ Error: %@ Start chat? + Почати чат? No comment provided by engineer. @@ -5106,6 +7304,16 @@ Error: %@ Почати міграцію No comment provided by engineer. + + Starting from %@. + Починаючи з %@. + No comment provided by engineer. + + + Statistics + Статистика + No comment provided by engineer. + Stop Зупинити @@ -5118,11 +7326,7 @@ Error: %@ Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - Зупиніть чат, щоб увімкнути дії з базою даних + Припинити чат No comment provided by engineer. @@ -5153,27 +7357,62 @@ Error: %@ Stop sharing Припиніть ділитися - No comment provided by engineer. + alert action Stop sharing address? Припинити ділитися адресою? - No comment provided by engineer. + alert title Stopping chat + Зупинка чату No comment provided by engineer. + + Storage + No comment provided by engineer. + + + Strong + Сильний + blur media + Submit Надіслати No comment provided by engineer. + + Subscribed + Підписано + No comment provided by engineer. + + + Subscription errors + Помилки підписки + No comment provided by engineer. + + + Subscriptions ignored + Підписки ігноруються + No comment provided by engineer. + Support SimpleX Chat Підтримка чату SimpleX No comment provided by engineer. + + Switch audio and video during the call. + Перемикайте аудіо та відео під час дзвінка. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Переключіть профіль чату для отримання одноразових запрошень. + No comment provided by engineer. + System Система @@ -5184,11 +7423,20 @@ Error: %@ Автентифікація системи No comment provided by engineer. + + TCP connection + TCP-з'єднання + No comment provided by engineer. + TCP connection timeout Тайм-аут TCP-з'єднання No comment provided by engineer. + + TCP port for messaging + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5204,11 +7452,21 @@ Error: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + Хвіст + No comment provided by engineer. + Take picture Сфотографуйте No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + Натисніть «Створити адресу SimpleX» у меню, щоб створити її пізніше. + No comment provided by engineer. + Tap button Натисніть кнопку @@ -5216,6 +7474,7 @@ Error: %@ Tap to Connect + Натисніть, щоб підключитися No comment provided by engineer. @@ -5235,22 +7494,28 @@ Error: %@ Tap to paste link + Натисніть, щоб вставити посилання No comment provided by engineer. Tap to scan + Натисніть, щоб сканувати No comment provided by engineer. - - Tap to start a new chat - Натисніть, щоб почати новий чат - No comment provided by engineer. + + Temporary file error + Тимчасова помилка файлу + file error alert title Test failed at step %@. Тест завершився невдало на кроці %@. server test failure + + Test notifications + No comment provided by engineer. + Test server Тестовий сервер @@ -5264,7 +7529,7 @@ Error: %@ Tests failed! Тести не пройшли! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5281,11 +7546,6 @@ Error: %@ Дякуємо користувачам - зробіть свій внесок через Weblate! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - Перша платформа без жодних ідентифікаторів користувачів – приватна за дизайном. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5298,6 +7558,16 @@ It can happen because of some bug or when the connection is compromised.Додаток може сповіщати вас, коли ви отримуєте повідомлення або запити на контакт - будь ласка, відкрийте налаштування, щоб увімкнути цю функцію. No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + Додаток захищає вашу конфіденційність, використовуючи різних операторів у кожній розмові. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + Програма попросить підтвердити завантаження з невідомих файлових серверів (крім .onion). + No comment provided by engineer. + The attempt to change database passphrase was not completed. Спроба змінити пароль до бази даних не була завершена. @@ -5305,6 +7575,12 @@ It can happen because of some bug or when the connection is compromised. The code you scanned is not a SimpleX link QR code. + Відсканований вами код не є QR-кодом посилання SimpleX. + No comment provided by engineer. + + + The connection reached the limit of undelivered messages, your contact may be offline. + З'єднання досягло ліміту недоставлених повідомлень, ваш контакт може бути офлайн. No comment provided by engineer. @@ -5327,6 +7603,11 @@ It can happen because of some bug or when the connection is compromised.Шифрування працює і нова угода про шифрування не потрібна. Це може призвести до помилок з'єднання! No comment provided by engineer. + + The future of messaging + Наступне покоління приватних повідомлень + No comment provided by engineer. + The hash of the previous message is different. Хеш попереднього повідомлення відрізняється. @@ -5342,9 +7623,14 @@ It can happen because of some bug or when the connection is compromised.Повідомлення буде позначено як модероване для всіх учасників. No comment provided by engineer. - - The next generation of private messaging - Наступне покоління приватних повідомлень + + The messages will be deleted for all members. + Повідомлення будуть видалені для всіх учасників. + No comment provided by engineer. + + + The messages will be marked as moderated for all members. + Повідомлення будуть позначені як модеровані для всіх учасників. No comment provided by engineer. @@ -5352,9 +7638,14 @@ It can happen because of some bug or when the connection is compromised.Стара база даних не була видалена під час міграції, її можна видалити. No comment provided by engineer. - - The profile is only shared with your contacts. - Профіль доступний лише вашим контактам. + + The same conditions will apply to operator **%@**. + Такі ж умови діятимуть і для оператора **%@**. + No comment provided by engineer. + + + The second preset operator in the app! + Другий попередньо встановлений оператор у застосунку! No comment provided by engineer. @@ -5372,13 +7663,29 @@ It can happen because of some bug or when the connection is compromised.Сервери для нових підключень вашого поточного профілю чату **%@**. No comment provided by engineer. - - The text you pasted is not a SimpleX link. + + The servers for new files of your current chat profile **%@**. + Сервери для нових файлів вашого поточного профілю чату **%@**. No comment provided by engineer. - - Theme - Тема + + The text you pasted is not a SimpleX link. + Текст, який ви вставили, не є посиланням SimpleX. + No comment provided by engineer. + + + The uploaded database archive will be permanently removed from the servers. + Завантажений архів бази даних буде назавжди видалено з серверів. + No comment provided by engineer. + + + Themes + Теми + No comment provided by engineer. + + + These conditions will also apply for: **%@**. + Ці умови також поширюються на: **%@**. No comment provided by engineer. @@ -5401,6 +7708,10 @@ It can happen because of some bug or when the connection is compromised.Цю дію неможливо скасувати - повідомлення, надіслані та отримані раніше, ніж вибрані, будуть видалені. Це може зайняти кілька хвилин. No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. Цю дію неможливо скасувати - ваш профіль, контакти, повідомлення та файли будуть безповоротно втрачені. @@ -5408,18 +7719,22 @@ It can happen because of some bug or when the connection is compromised. This chat is protected by end-to-end encryption. + Цей чат захищений наскрізним шифруванням. E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + Цей чат захищений квантово-стійким наскрізним шифруванням. E2EE info chat item This device name + Це ім'я пристрою No comment provided by engineer. This display name is invalid. Please choose another name. + Це ім'я для відображення є недійсним. Будь ласка, виберіть інше ім'я. No comment provided by engineer. @@ -5434,10 +7749,25 @@ It can happen because of some bug or when the connection is compromised. This is your own SimpleX address! + Це ваша власна SimpleX-адреса! No comment provided by engineer. This is your own one-time link! + Це ваше власне одноразове посилання! + No comment provided by engineer. + + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + Це посилання було використано з іншого мобільного пристрою, будь ласка, створіть нове посилання на робочому столі. + No comment provided by engineer. + + + This message was deleted or not received yet. No comment provided by engineer. @@ -5445,6 +7775,11 @@ It can happen because of some bug or when the connection is compromised.Це налаштування застосовується до повідомлень у вашому поточному профілі чату **%@**. No comment provided by engineer. + + Title + Заголовок + No comment provided by engineer. + To ask any questions and to receive updates: Задати будь-які питання та отримувати новини: @@ -5457,6 +7792,7 @@ It can happen because of some bug or when the connection is compromised. To hide unwanted messages. + Приховати небажані повідомлення. No comment provided by engineer. @@ -5464,9 +7800,9 @@ It can happen because of some bug or when the connection is compromised.Щоб створити нове з'єднання No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Щоб захистити конфіденційність, замість ідентифікаторів користувачів, які використовуються на всіх інших платформах, SimpleX має ідентифікатори для черг повідомлень, окремі для кожного з ваших контактів. + + To protect against your link being replaced, you can compare contact security codes. + Щоб захиститися від заміни вашого посилання, ви можете порівняти коди безпеки контактів. No comment provided by engineer. @@ -5474,6 +7810,11 @@ It can happen because of some bug or when the connection is compromised.Для захисту часового поясу у файлах зображень/голосу використовується UTC. No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + Щоб захистити вашу IP-адресу, приватна маршрутизація використовує ваші SMP-сервери для доставки повідомлень. + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5481,6 +7822,26 @@ You will be prompted to complete authentication before this feature is enabled.< Перед увімкненням цієї функції вам буде запропоновано пройти автентифікацію. No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + Щоб захистити конфіденційність, замість ідентифікаторів користувачів, які використовуються на всіх інших платформах, SimpleX має ідентифікатори для черг повідомлень, окремі для кожного з ваших контактів. + No comment provided by engineer. + + + To receive + Щоб отримати + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + Для запису промови, будь ласка, надайте дозвіл на використання мікрофону. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Для запису відео, будь ласка, надайте дозвіл на використання камери. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Щоб записати голосове повідомлення, будь ласка, надайте дозвіл на використання мікрофону. @@ -5491,18 +7852,48 @@ You will be prompted to complete authentication before this feature is enabled.< Щоб відкрити свій прихований профіль, введіть повний пароль у поле пошуку на сторінці **Ваші профілі чату**. No comment provided by engineer. + + To send + Щоб відправити + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. Для підтримки миттєвих push-повідомлень необхідно перенести базу даних чату. No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + Щоб користуватися серверами **%@**, прийміть умови використання. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. Щоб перевірити наскрізне шифрування з вашим контактом, порівняйте (або відскануйте) код на ваших пристроях. No comment provided by engineer. + + Toggle chat list: + Перемикання списку чату: + No comment provided by engineer. + Toggle incognito when connecting. + Увімкніть інкогніто при підключенні. + No comment provided by engineer. + + + Token status: %@. + token status + + + Toolbar opacity + Непрозорість панелі інструментів + No comment provided by engineer. + + + Total + Всього No comment provided by engineer. @@ -5510,6 +7901,11 @@ You will be prompted to complete authentication before this feature is enabled.< Транспортна ізоляція No comment provided by engineer. + + Transport sessions + Транспортні сесії + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). Спроба з'єднатися з сервером, який використовується для отримання повідомлень від цього контакту (помилка: %@). @@ -5522,6 +7918,7 @@ You will be prompted to complete authentication before this feature is enabled.< Turkish interface + Турецький інтерфейс No comment provided by engineer. @@ -5541,28 +7938,33 @@ You will be prompted to complete authentication before this feature is enabled.< Unblock + Розблoкувати No comment provided by engineer. Unblock for all + Розблокування для всіх No comment provided by engineer. Unblock member + Розблокувати учасника No comment provided by engineer. Unblock member for all? + Розблокувати учасника для всіх? No comment provided by engineer. Unblock member? + Розблокувати учасника? No comment provided by engineer. - - Unexpected error: %@ - Неочікувана помилка: %@ - item status description + + Undelivered messages + Недоставлені повідомлення + No comment provided by engineer. Unexpected migration state @@ -5572,7 +7974,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unfav. Нелюб. - No comment provided by engineer. + swipe action Unhide @@ -5609,6 +8011,11 @@ You will be prompted to complete authentication before this feature is enabled.< Невідома помилка No comment provided by engineer. + + Unknown servers! + Невідомі сервери! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. Якщо ви не користуєтеся інтерфейсом виклику iOS, увімкніть режим "Не турбувати", щоб уникнути переривань. @@ -5623,10 +8030,12 @@ To connect, please ask your contact to create another connection link and check Unlink + Роз'єднати зв'язок No comment provided by engineer. Unlink desktop? + Від'єднати робочий стіл? No comment provided by engineer. @@ -5642,15 +8051,20 @@ To connect, please ask your contact to create another connection link and check Unmute Увімкнути звук - No comment provided by engineer. + notification label action Unread Непрочитане + swipe action + + + Unsupported connection link No comment provided by engineer. Up to 100 last messages are sent to new members. + Новим користувачам надсилається до 100 останніх повідомлень. No comment provided by engineer. @@ -5658,11 +8072,6 @@ To connect, please ask your contact to create another connection link and check Оновлення No comment provided by engineer. - - Update .onion hosts setting? - Оновити налаштування хостів .onion? - No comment provided by engineer. - Update database passphrase Оновити парольну фразу бази даних @@ -5673,9 +8082,13 @@ To connect, please ask your contact to create another connection link and check Оновити налаштування мережі? No comment provided by engineer. - - Update transport isolation mode? - Оновити режим транспортної ізоляції? + + Update settings? + Оновити налаштування? + No comment provided by engineer. + + + Updated conditions No comment provided by engineer. @@ -5683,18 +8096,19 @@ To connect, please ask your contact to create another connection link and check Оновлення налаштувань призведе до перепідключення клієнта до всіх серверів. No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - Оновлення цього параметра призведе до перепідключення клієнта до всіх серверів. - No comment provided by engineer. - Upgrade and open chat Оновлення та відкритий чат No comment provided by engineer. + + Upload errors + Помилки завантаження + No comment provided by engineer. + Upload failed + Не вдалося завантфжити No comment provided by engineer. @@ -5702,8 +8116,24 @@ To connect, please ask your contact to create another connection link and check Завантажити файл server test step + + Uploaded + Завантажено + No comment provided by engineer. + + + Uploaded files + Завантажені файли + No comment provided by engineer. + Uploading archive + Завантаження архіву + No comment provided by engineer. + + + Use %@ + Використовуйте %@ No comment provided by engineer. @@ -5711,11 +8141,24 @@ To connect, please ask your contact to create another connection link and check Використовуйте хости .onion No comment provided by engineer. + + Use SOCKS proxy + Використовуйте SOCKS проксі + No comment provided by engineer. + Use SimpleX Chat servers? Використовувати сервери SimpleX Chat? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat Використовуйте чат @@ -5726,6 +8169,16 @@ To connect, please ask your contact to create another connection link and check Використовувати поточний профіль No comment provided by engineer. + + Use for files + Використовуйте для файлів + No comment provided by engineer. + + + Use for messages + Використовуйте для повідомлень + No comment provided by engineer. + Use for new connections Використовуйте для нових з'єднань @@ -5733,6 +8186,7 @@ To connect, please ask your contact to create another connection link and check Use from desktop + Використання з робочого столу No comment provided by engineer. @@ -5747,6 +8201,17 @@ To connect, please ask your contact to create another connection link and check Use only local notifications? + Використовувати лише локальні сповіщення? + No comment provided by engineer. + + + Use private routing with unknown servers when IP address is not protected. + Використовуйте приватну маршрутизацію з невідомими серверами, якщо IP-адреса не захищена. + No comment provided by engineer. + + + Use private routing with unknown servers. + Використовуйте приватну маршрутизацію з невідомими серверами. No comment provided by engineer. @@ -5754,18 +8219,37 @@ To connect, please ask your contact to create another connection link and check Використовувати сервер No comment provided by engineer. + + Use servers + Використовуйте сервери + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. + Використовуйте додаток під час розмови. No comment provided by engineer. - - User profile - Профіль користувача + + Use the app with one hand. + Використовуйте додаток однією рукою. No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - Для використання хостів .onion потрібен сумісний VPN-провайдер. + + Use web port + No comment provided by engineer. + + + User selection + Вибір користувача + No comment provided by engineer. + + + Username + Ім'я користувача No comment provided by engineer. @@ -5775,10 +8259,12 @@ To connect, please ask your contact to create another connection link and check Verify code with desktop + Перевірте код на робочому столі No comment provided by engineer. Verify connection + Перевірте з'єднання No comment provided by engineer. @@ -5788,14 +8274,17 @@ To connect, please ask your contact to create another connection link and check Verify connections + Пeревірте з'єднання No comment provided by engineer. Verify database passphrase + Перевірте пароль до бази даних No comment provided by engineer. Verify passphrase + Підтвердіть парольну фразу No comment provided by engineer. @@ -5810,6 +8299,7 @@ To connect, please ask your contact to create another connection link and check Via secure quantum resistant protocol. + Через безпечний квантово-стійкий протокол. No comment provided by engineer. @@ -5832,13 +8322,24 @@ To connect, please ask your contact to create another connection link and check Відео та файли до 1 Гб No comment provided by engineer. + + View conditions + Умови перегляду + No comment provided by engineer. + View security code Переглянути код безпеки No comment provided by engineer. + + View updated conditions + Переглянути оновлені умови + No comment provided by engineer. + Visible history + Видима історія chat feature @@ -5851,11 +8352,16 @@ To connect, please ask your contact to create another connection link and check Голосові повідомлення в цьому чаті заборонені. No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. Голосові повідомлення в цій групі заборонені. No comment provided by engineer. + + Voice messages not allowed + Голосові повідомлення заборонені + No comment provided by engineer. + Voice messages prohibited! Голосові повідомлення заборонені! @@ -5868,6 +8374,7 @@ To connect, please ask your contact to create another connection link and check Waiting for desktop... + Чекаємо на десктопну версію... No comment provided by engineer. @@ -5885,8 +8392,19 @@ To connect, please ask your contact to create another connection link and check Чекаємо на відео No comment provided by engineer. + + Wallpaper accent + Акцент на шпалерах + No comment provided by engineer. + + + Wallpaper background + Фон шпалер + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures + Попередження: запуск чату на декількох пристроях не підтримується і може призвести до збоїв у доставці повідомлень No comment provided by engineer. @@ -5911,6 +8429,7 @@ To connect, please ask your contact to create another connection link and check Welcome message is too long + Привітальне повідомлення занадто довге No comment provided by engineer. @@ -5923,9 +8442,14 @@ To connect, please ask your contact to create another connection link and check За наявності No comment provided by engineer. - - When people request to connect, you can accept or reject it. - Коли люди звертаються із запитом на підключення, ви можете прийняти або відхилити його. + + When connecting audio and video calls. + При підключенні аудіо та відеодзвінків. + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. + Коли увімкнено більше одного оператора, жоден з них не має метаданих, щоб дізнатися, хто з ким спілкується. No comment provided by engineer. @@ -5933,8 +8457,24 @@ To connect, please ask your contact to create another connection link and check Коли ви ділитеся з кимось своїм профілем інкогніто, цей профіль буде використовуватися для груп, до яких вас запрошують. No comment provided by engineer. + + WiFi + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + Буде ввімкнено в прямих чатах! + No comment provided by engineer. + + + Wired ethernet + Дротова мережа Ethernet + No comment provided by engineer. + With encrypted files and media. + З зашифрованими файлами та медіа. No comment provided by engineer. @@ -5944,30 +8484,47 @@ To connect, please ask your contact to create another connection link and check With reduced battery usage. + З меншим споживанням заряду акумулятора. No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + Без Tor або VPN ваша IP-адреса буде видимою для файлових серверів. + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + Без Tor або VPN ваша IP-адреса буде видимою для цих XFTP-ретрансляторів: %@. + alert message + Wrong database passphrase Неправильний пароль до бази даних No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + Неправильний ключ або невідоме з'єднання - швидше за все, це з'єднання видалено. + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + Неправильний ключ або невідома адреса фрагмента файлу - найімовірніше, файл видалено. + file error text + Wrong passphrase! Неправильний пароль! No comment provided by engineer. - - XFTP servers - Сервери XFTP - No comment provided by engineer. - - - You - Ти + + XFTP server + XFTP-сервер No comment provided by engineer. You **must not** use the same database on two devices. + Ви **не повинні використовувати** одну і ту ж базу даних на двох пристроях. No comment provided by engineer. @@ -5990,33 +8547,46 @@ To connect, please ask your contact to create another connection link and check Ви вже підключені до %@. No comment provided by engineer. + + You are already connected with %@. + Ви вже підключені до %@. + No comment provided by engineer. + You are already connecting to %@. + Ви вже з'єднані з %@. No comment provided by engineer. You are already connecting via this one-time link! + Ви вже підключаєтеся до %@.Ви вже підключаєтеся за цим одноразовим посиланням! No comment provided by engineer. You are already in group %@. + Ви вже перебуваєте в групі %@. No comment provided by engineer. You are already joining the group %@. + Ви вже приєдналися до групи %@. No comment provided by engineer. You are already joining the group via this link! + Ви вже приєдналися до групи за цим посиланням! No comment provided by engineer. You are already joining the group via this link. + Ви вже приєдналися до групи за цим посиланням. No comment provided by engineer. You are already joining the group! Repeat join request? + Ви вже приєдналися до групи! +Повторити запит на приєднання? No comment provided by engineer. @@ -6029,11 +8599,26 @@ Repeat join request? Запрошуємо вас до групи No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + Не підключені до цих серверів. Для доставлення повідомлень до них використовується приватна маршрутизація. + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. Ви можете приймати дзвінки з екрана блокування без автентифікації пристрою та програми. No comment provided by engineer. + + You can change it in Appearance settings. + Ви можете змінити його в налаштуваннях зовнішнього вигляду. + No comment provided by engineer. + + + You can configure servers via settings. + Ви можете налаштувати сервери за допомогою налаштувань. + No comment provided by engineer. + You can create it later Ви можете створити його пізніше @@ -6051,6 +8636,7 @@ Repeat join request? You can give another try. + Ви можете спробувати ще раз. No comment provided by engineer. @@ -6060,13 +8646,24 @@ Repeat join request? You can make it visible to your SimpleX contacts via Settings. + Ви можете зробити його видимим для ваших контактів у SimpleX за допомогою налаштувань. No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ Тепер ви можете надсилати повідомлення на адресу %@ notification body + + You can send messages to %@ from Archived contacts. + Ви можете надсилати повідомлення на %@ з архівних контактів. + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + Ви можете задати ім'я з'єднання, щоб запам'ятати, з ким ви поділилися посиланням. + No comment provided by engineer. + You can set lock screen notification preview via settings. Ви можете налаштувати попередній перегляд сповіщень на екрані блокування за допомогою налаштувань. @@ -6082,16 +8679,16 @@ Repeat join request? Ви можете поділитися цією адресою зі своїми контактами, щоб вони могли зв'язатися з **%@**. No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - Ви можете поділитися своєю адресою у вигляді посилання або QR-коду - будь-хто зможе зв'язатися з вами. - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app Запустити чат можна через Налаштування програми / База даних або перезапустивши програму No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + Ви все ще можете переглянути розмову з %@ у списку чатів. + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. Увімкнути SimpleX Lock можна в Налаштуваннях. @@ -6104,35 +8701,34 @@ Repeat join request? You can view invitation link again in connection details. - No comment provided by engineer. + Ви можете переглянути посилання на запрошення ще раз у деталях підключення. + alert message You can't send messages! Ви не можете надсилати повідомлення! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - Ви контролюєте, через який(і) сервер(и) **отримувати** повідомлення, ваші контакти - сервери, які ви використовуєте для надсилання їм повідомлень. - No comment provided by engineer. - You could not be verified; please try again. Вас не вдалося верифікувати, спробуйте ще раз. No comment provided by engineer. + + You decide who can connect. + Ви вирішуєте, хто може під'єднатися. + No comment provided by engineer. + You have already requested connection via this address! + Ви вже надсилали запит на підключення за цією адресою! No comment provided by engineer. You have already requested connection! Repeat connection request? - No comment provided by engineer. - - - You have no chats - У вас немає чатів + Ви вже надіслали запит на підключення! +Повторити запит на підключення? No comment provided by engineer. @@ -6155,11 +8751,26 @@ Repeat connection request? Ви приєдналися до цієї групи. Підключення до запрошеного учасника групи. No comment provided by engineer. + + You may migrate the exported database. + Ви можете мігрувати експортовану базу даних. + No comment provided by engineer. + + + You may save the exported archive. + Ви можете зберегти експортований архів. + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. Ви повинні використовувати найновішу версію бази даних чату ТІЛЬКИ на одному пристрої, інакше ви можете перестати отримувати повідомлення від деяких контактів. No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + Щоб мати змогу зателефонувати контакту, вам потрібно дозволити йому зателефонувати. + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. Щоб мати змогу надсилати голосові повідомлення, вам потрібно дозволити контакту надсилати їх. @@ -6175,6 +8786,10 @@ Repeat connection request? Ви надіслали запрошення до групи No comment provided by engineer. + + You should receive notifications. + token info + You will be connected to group when the group host's device is online, please wait or check later! Ви будете підключені до групи, коли пристрій господаря групи буде в мережі, будь ласка, зачекайте або перевірте пізніше! @@ -6182,6 +8797,7 @@ Repeat connection request? You will be connected when group link host's device is online, please wait or check later! + Ви будете підключені, коли пристрій хоста групового посилання буде онлайн, будь ласка, зачекайте або перевірте пізніше! No comment provided by engineer. @@ -6201,6 +8817,7 @@ Repeat connection request? You will connect to all group members. + Ви з'єднаєтеся з усіма учасниками групи. No comment provided by engineer. @@ -6208,6 +8825,11 @@ Repeat connection request? Ви все одно отримуватимете дзвінки та сповіщення від вимкнених профілів, якщо вони активні. No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + Ви більше не будете отримувати повідомлення з цього чату. Історія чату буде збережена. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. Ви перестанете отримувати повідомлення від цієї групи. Історія чату буде збережена. @@ -6228,31 +8850,16 @@ Repeat connection request? Ви використовуєте профіль інкогніто для цієї групи - щоб запобігти поширенню вашого основного профілю, запрошення контактів заборонено No comment provided by engineer. - - Your %@ servers - Ваші сервери %@ - No comment provided by engineer. - Your ICE servers Ваші сервери ICE No comment provided by engineer. - - Your SMP servers - Ваші SMP-сервери - No comment provided by engineer. - Your SimpleX address Ваша адреса SimpleX No comment provided by engineer. - - Your XFTP servers - Ваші XFTP-сервери - No comment provided by engineer. - Your calls Твої дзвінки @@ -6268,16 +8875,19 @@ Repeat connection request? Ваша база даних чату не зашифрована - встановіть ключову фразу, щоб зашифрувати її. No comment provided by engineer. + + Your chat preferences + Ваші налаштування чату + alert title + Your chat profiles Ваші профілі чату No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - Для завершення з'єднання ваш контакт має бути онлайн. -Ви можете скасувати це з'єднання і видалити контакт (і спробувати пізніше з новим посиланням). + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Ваше з'єднання було переміщено на %@, але під час перенаправлення на профіль сталася несподівана помилка. No comment provided by engineer. @@ -6295,6 +8905,11 @@ You can cancel this connection and remove the contact (and try later with a new Ваші контакти залишаться на зв'язку. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Ваші облікові дані можуть бути надіслані незашифрованими. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Ваша поточна база даних чату буде ВИДАЛЕНА і ЗАМІНЕНА імпортованою. @@ -6317,6 +8932,7 @@ You can cancel this connection and remove the contact (and try later with a new Your profile + Ваш профіль No comment provided by engineer. @@ -6324,33 +8940,36 @@ You can cancel this connection and remove the contact (and try later with a new Ваш профіль **%@** буде опублікований. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Ваш профіль зберігається на вашому пристрої і доступний лише вашим контактам. -Сервери SimpleX не бачать ваш профіль. + + Your profile is stored on your device and only shared with your contacts. + Профіль доступний лише вашим контактам. No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - Ваш профіль, контакти та доставлені повідомлення зберігаються на вашому пристрої. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Ваш профіль зберігається на вашому пристрої і доступний лише вашим контактам. Сервери SimpleX не бачать ваш профіль. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Ваш профіль було змінено. Якщо ви збережете його, оновлений профіль буде надіслано всім вашим контактам. + alert message + Your random profile Ваш випадковий профіль No comment provided by engineer. - - Your server - Ваш сервер - No comment provided by engineer. - Your server address Адреса вашого сервера No comment provided by engineer. + + Your servers + Ваші сервери + No comment provided by engineer. + Your settings Ваші налаштування @@ -6391,11 +9010,21 @@ SimpleX servers cannot see your profile. прийнято виклик call status + + accepted invitation + прийняте запрошення + chat list item title + admin адмін member role + + admins + адміністратори + feature role + agreeing encryption for %@… узгодження шифрування для %@… @@ -6406,6 +9035,11 @@ SimpleX servers cannot see your profile. узгодження шифрування… chat item text + + all members + всі учасники + feature role + always завжди @@ -6413,6 +9047,16 @@ SimpleX servers cannot see your profile. and %lld other events + та %lld інших подій + No comment provided by engineer. + + + archived report + No comment provided by engineer. + + + attempts + спроби No comment provided by engineer. @@ -6422,6 +9066,7 @@ SimpleX servers cannot see your profile. author + автор member role @@ -6436,21 +9081,30 @@ SimpleX servers cannot see your profile. blocked + заблоковано marked deleted chat item preview text blocked %@ + заблоковано %@ rcv group event chat item blocked by admin - marked deleted chat item preview text + заблоковано адміністратором + blocked chat item +marked deleted chat item preview text bold жирний No comment provided by engineer. + + call + дзвонити + No comment provided by engineer. + call error помилка дзвінка @@ -6518,6 +9172,7 @@ SimpleX servers cannot see your profile. connected directly + з'єднані безпосередньо rcv group event chat item @@ -6553,7 +9208,7 @@ SimpleX servers cannot see your profile. connecting… з'єднання… - chat list item title + No comment provided by engineer. connection established @@ -6567,6 +9222,7 @@ SimpleX servers cannot see your profile. contact %1$@ changed to %2$@ + контакт %1$@ змінено на %2$@ profile update event chat item @@ -6599,10 +9255,16 @@ SimpleX servers cannot see your profile. днів time unit + + decryption errors + помилки розшифровки + No comment provided by engineer. + default (%@) за замовчуванням (%@) - pref value + delete after time +pref value default (no) @@ -6621,6 +9283,7 @@ SimpleX servers cannot see your profile. deleted contact + видалений контакт rcv direct event chat item @@ -6648,6 +9311,11 @@ SimpleX servers cannot see your profile. дублююче повідомлення integrity error chat item + + duplicates + дублікати + No comment provided by engineer. + e2e encrypted e2e зашифрований @@ -6723,9 +9391,14 @@ SimpleX servers cannot see your profile. помилка No comment provided by engineer. - - event happened - відбулася подія + + expired + закінчився + No comment provided by engineer. + + + forwarded + переслано No comment provided by engineer. @@ -6753,6 +9426,11 @@ SimpleX servers cannot see your profile. Пароль бази даних буде безпечно збережено в iOS Keychain після запуску чату або зміни пароля - це дасть змогу отримувати миттєві повідомлення. No comment provided by engineer. + + inactive + неактивний + No comment provided by engineer. + incognito via contact address link інкогніто за посиланням на контактну адресу @@ -6793,6 +9471,11 @@ SimpleX servers cannot see your profile. запрошення до групи %@ group name + + invite + запросити + No comment provided by engineer. + invited запрошені @@ -6840,6 +9523,7 @@ SimpleX servers cannot see your profile. member %1$@ changed to %2$@ + учасника %1$@ змінено на %2$@ profile update event chat item @@ -6847,6 +9531,11 @@ SimpleX servers cannot see your profile. з'єднаний rcv group event chat item + + message + повідомлення + No comment provided by engineer. + message received повідомлення отримано @@ -6872,6 +9561,10 @@ SimpleX servers cannot see your profile. модерується %@ marked deleted chat item preview text + + moderator + member role + months місяців @@ -6880,7 +9573,7 @@ SimpleX servers cannot see your profile. never ніколи - No comment provided by engineer. + delete after time new message @@ -6911,8 +9604,8 @@ SimpleX servers cannot see your profile. off вимкнено enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -6926,21 +9619,45 @@ SimpleX servers cannot see your profile. on - увімкнено + увімкненo group pref value + + other + інший + No comment provided by engineer. + + + other errors + інші помилки + No comment provided by engineer. + owner власник member role + + owners + власники + feature role + peer-to-peer одноранговий No comment provided by engineer. + + pending + No comment provided by engineer. + + + pending approval + No comment provided by engineer. + quantum resistant e2e encryption + квантово-стійке шифрування e2e chat item text @@ -6953,6 +9670,10 @@ SimpleX servers cannot see your profile. отримали підтвердження… No comment provided by engineer. + + rejected + No comment provided by engineer. + rejected call відхилений виклик @@ -6970,10 +9691,12 @@ SimpleX servers cannot see your profile. removed contact address + видалено контактну адресу profile update event chat item removed profile picture + видалено зображення профілю profile update event chat item @@ -6981,6 +9704,26 @@ SimpleX servers cannot see your profile. прибрали вас rcv group event chat item + + requested to connect + запит на підключення + chat list item title + + + saved + збережено + No comment provided by engineer. + + + saved from %@ + збережено з %@ + No comment provided by engineer. + + + search + пошук + No comment provided by engineer. + sec сек @@ -7003,18 +9746,31 @@ SimpleX servers cannot see your profile. send direct message + надіслати пряме повідомлення No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + інформація про чергу на сервері: %1$@ + +останнє отримане повідомлення: %2$@ + queue info + set new contact address + встановити нову контактну адресу profile update event chat item set new profile picture + встановити нове зображення профілю profile update event chat item standard end-to-end encryption + стандартне наскрізне шифрування chat item text @@ -7034,6 +9790,7 @@ SimpleX servers cannot see your profile. unblocked %@ + розблоковано %@ rcv group event chat item @@ -7041,8 +9798,19 @@ SimpleX servers cannot see your profile. невідомий connection info + + unknown servers + невідомі реле + No comment provided by engineer. + unknown status + невідомий статус + No comment provided by engineer. + + + unprotected + незахищені No comment provided by engineer. @@ -7052,10 +9820,12 @@ SimpleX servers cannot see your profile. updated profile + оновлений профіль profile update event chat item v%@ + v%@ No comment provided by engineer. @@ -7083,6 +9853,11 @@ SimpleX servers cannot see your profile. за допомогою ретранслятора No comment provided by engineer. + + video + відео + No comment provided by engineer. + video call (not e2e encrypted) відеодзвінок (без шифрування e2e) @@ -7108,11 +9883,21 @@ SimpleX servers cannot see your profile. тижнів time unit + + when IP hidden + коли IP приховано + No comment provided by engineer. + yes так pref value + + you + ти + No comment provided by engineer. + you are invited to group вас запрошують до групи @@ -7125,6 +9910,7 @@ SimpleX servers cannot see your profile. you blocked %@ + ви заблокували %@ snd group event chat item @@ -7169,6 +9955,7 @@ SimpleX servers cannot see your profile. you unblocked %@ + ви розблокували %@ snd group event chat item @@ -7185,7 +9972,7 @@ SimpleX servers cannot see your profile.
- +
@@ -7205,6 +9992,7 @@ SimpleX servers cannot see your profile. SimpleX uses local network access to allow using user chat profile via desktop app on the same network. + SimpleX використовує доступ до локальної мережі, щоб дозволити користувачеві користуватися профілем чату через десктопну програму в тій же мережі. Privacy - Local Network Usage Description @@ -7221,7 +10009,7 @@ SimpleX servers cannot see your profile.
- +
@@ -7241,4 +10029,249 @@ SimpleX servers cannot see your profile.
+ +
+ +
+ + + %d new events + %d нових подій + notification body + + + From %d chat(s) + notification body + + + From: %@ + Від: %@ + notification body + + + New events + Нові події + notification + + + New messages + Нові повідомлення + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright © 2024 SimpleX Chat. Всі права захищені. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + Додаток заблоковано! + No comment provided by engineer. + + + Cancel + Скасувати + No comment provided by engineer. + + + Cannot access keychain to save database password + Не вдається отримати доступ до зв'язки ключів для збереження пароля до бази даних + No comment provided by engineer. + + + Cannot forward message + Неможливо переслати повідомлення + No comment provided by engineer. + + + Comment + Коментар + No comment provided by engineer. + + + Currently maximum supported file size is %@. + Наразі максимальний підтримуваний розмір файлу - %@. + No comment provided by engineer. + + + Database downgrade required + Потрібне оновлення бази даних + No comment provided by engineer. + + + Database encrypted! + База даних зашифрована! + No comment provided by engineer. + + + Database error + Помилка в базі даних + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + Парольна фраза бази даних відрізняється від збереженої у в’язці ключів. + No comment provided by engineer. + + + Database passphrase is required to open chat. + Для відкриття чату потрібно ввести пароль до бази даних. + No comment provided by engineer. + + + Database upgrade required + Потрібне оновлення бази даних + No comment provided by engineer. + + + Error preparing file + Помилка підготовки файлу + No comment provided by engineer. + + + Error preparing message + Повідомлення про підготовку до помилки + No comment provided by engineer. + + + Error: %@ + Помилка: %@ + No comment provided by engineer. + + + File error + Помилка файлу + No comment provided by engineer. + + + Incompatible database version + Несумісна версія бази даних + No comment provided by engineer. + + + Invalid migration confirmation + Недійсне підтвердження міграції + No comment provided by engineer. + + + Keychain error + Помилка зв'язки ключів + No comment provided by engineer. + + + Large file! + Великий файл! + No comment provided by engineer. + + + No active profile + Немає активного профілю + No comment provided by engineer. + + + Ok + Гаразд + No comment provided by engineer. + + + Open the app to downgrade the database. + Відкрийте програму, щоб знизити версію бази даних. + No comment provided by engineer. + + + Open the app to upgrade the database. + Відкрийте програму, щоб оновити базу даних. + No comment provided by engineer. + + + Passphrase + Парольна фраза + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Будь ласка, створіть профіль у додатку SimpleX + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + Вибрані налаштування чату забороняють це повідомлення. + No comment provided by engineer. + + + Sending a message takes longer than expected. + Надсилання повідомлення займає більше часу, ніж очікувалося. + No comment provided by engineer. + + + Sending message… + Надсилаю повідомлення… + No comment provided by engineer. + + + Share + Поділіться + No comment provided by engineer. + + + Slow network? + Повільна мережа? + No comment provided by engineer. + + + Unknown database error: %@ + Невідома помилка бази даних: %@ + No comment provided by engineer. + + + Unsupported format + Непідтримуваний формат + No comment provided by engineer. + + + Wait + Зачекай + No comment provided by engineer. + + + Wrong database passphrase + Неправильна ключова фраза до бази даних + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + Ви можете дозволити спільний доступ у налаштуваннях Конфіденційність і безпека / SimpleX Lock. + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/contents.json b/apps/ios/SimpleX Localizations/uk.xcloc/contents.json index 6c122f11ab..a93c702952 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/uk.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "uk", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff index 3bf27bff9c..d5411f86e3 100644 --- a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff @@ -2,36 +2,9 @@
- +
- - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - - - No comment provided by engineer. - - - ( - ( - No comment provided by engineer. - (can be copied) (可复制) @@ -109,6 +82,7 @@ %@ downloaded + %@ 已下载 No comment provided by engineer. @@ -126,13 +100,19 @@ %@ 已认证 No comment provided by engineer. + + %@ server + 服务器 + No comment provided by engineer. + %@ servers - %@ 服务器 + 服务器 No comment provided by engineer. %@ uploaded + %@ 已上传 No comment provided by engineer. @@ -140,6 +120,11 @@ %@ 要连接! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ 和 %lld 成员 @@ -160,11 +145,36 @@ %d 天 time interval + + %d file(s) are still being downloaded. + 仍在下载 %d 个文件。 + forward confirmation reason + + + %d file(s) failed to download. + %d 个文件下载失败。 + forward confirmation reason + + + %d file(s) were deleted. + 已刪除 %d 个文件。 + forward confirmation reason + + + %d file(s) were not downloaded. + 未能下载 %d 个文件。 + forward confirmation reason + %d hours %d 小时 time interval + + %d messages not forwarded + 未转发 %d 条消息 + alert title + %d min %d 分钟 @@ -180,9 +190,14 @@ %d 秒 time interval + + %d seconds(s) + %d 秒 + delete after time + %d skipped message(s) - %d 跳过消息 + 跳过的 %d 条消息 integrity error chat item @@ -227,14 +242,17 @@ %lld messages blocked by admin + %lld 被管理员阻止的消息 No comment provided by engineer. %lld messages marked deleted + %lld 标记为已删除的消息 No comment provided by engineer. %lld messages moderated by %@ + %lld 审核的留言 by %@ No comment provided by engineer. @@ -247,11 +265,6 @@ %lld 种新的界面语言 No comment provided by engineer. - - %lld second(s) - %lld 秒 - No comment provided by engineer. - %lld seconds %lld 秒 @@ -302,49 +315,39 @@ 已跳过 %u 条消息。 No comment provided by engineer. - - ( - ( - No comment provided by engineer. - (new) + (新) No comment provided by engineer. (this device v%@) + (此设备 v%@) No comment provided by engineer. - - ) - ) - No comment provided by engineer. - - - **Add contact**: to create a new invitation link, or connect via a link you received. - No comment provided by engineer. - - - **Add new contact**: to create your one-time QR Code or link for your contact. - **添加新联系人**:为您的联系人创建一次性二维码或者链接。 + + **Create 1-time link**: to create and share a new invitation link. + **添加联系人**: 创建新的邀请链接,或通过您收到的链接进行连接. No comment provided by engineer. **Create group**: to create a new group. + **创建群组**: 创建一个新群组. No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **更私密**:每20分钟检查新消息。设备令牌和 SimpleX Chat 服务器共享,但是不会共享有您有多少联系人或者消息。 No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **最私密**:不使用 SimpleX Chat 通知服务器,在后台定期检查消息(取决于您多经常使用应用程序)。 No comment provided by engineer. **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **请注意**: 在两台设备上使用相同的数据库将破坏来自您的连接的消息解密,作为一种安全保护. No comment provided by engineer. @@ -352,11 +355,16 @@ **请注意**:如果您丢失密码,您将无法恢复或者更改密码。 No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **推荐**:设备令牌和通知会发送至 SimpleX Chat 通知服务器,但是消息内容、大小或者发送人不会。 No comment provided by engineer. + + **Scan / Paste link**: to connect via a link you received. + **扫描/粘贴链接**:用您收到的链接连接。 + No comment provided by engineer. + **Warning**: Instant push notifications require passphrase saved in Keychain. **警告**:及时推送通知需要保存在钥匙串的密码。 @@ -364,6 +372,7 @@ **Warning**: the archive will be removed. + **警告**: 存档将被删除. No comment provided by engineer. @@ -381,11 +390,6 @@ \*加粗* No comment provided by engineer. - - , - , - No comment provided by engineer. - - connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)! - delivery receipts (up to 20 members). @@ -422,13 +426,9 @@ - 编辑消息历史。 No comment provided by engineer. - - . - . - No comment provided by engineer. - 0 sec + 0 秒 time to disappear @@ -439,7 +439,8 @@ 1 day 1天 - time interval + delete after time +time interval 1 hour @@ -454,12 +455,29 @@ 1 month 1月 - time interval + delete after time +time interval 1 week 1周 - time interval + delete after time +time interval + + + 1 year + 1 年 + delete after time + + + 1-time link + 一次性链接 + No comment provided by engineer. + + + 1-time link can be used *with one contact only* - share in person or via any messenger. + 一次性链接*只能给一名联系人*使用。当面或使用聊天应用分享链接。 + No comment provided by engineer. 5 minutes @@ -476,11 +494,6 @@ 30秒 No comment provided by engineer. - - : - : - No comment provided by engineer. - <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> @@ -530,31 +543,32 @@ 中止地址更改? No comment provided by engineer. - - About SimpleX - 关于SimpleX - No comment provided by engineer. - About SimpleX Chat 关于SimpleX Chat No comment provided by engineer. - - About SimpleX address - 关于 SimpleX 地址 + + About operators + 关于运营方 No comment provided by engineer. - - Accent color - 色调 + + Accent + 强调 No comment provided by engineer. Accept 接受 accept contact request via notification - accept incoming call via notification +accept incoming call via notification +swipe action + + + Accept conditions + 接受条款 + No comment provided by engineer. Accept connection request? @@ -569,21 +583,47 @@ Accept incognito 接受隐身聊天 - accept contact request via notification + accept contact request via notification +swipe action + + + Accepted conditions + 已接受的条款 + No comment provided by engineer. + + + Acknowledged + 确认 + No comment provided by engineer. + + + Acknowledgement errors + 确认错误 + No comment provided by engineer. + + + Active + 活跃 + token status text + + + Active connections + 活动连接 + No comment provided by engineer. Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts. 将地址添加到您的个人资料,以便您的联系人可以与其他人共享。个人资料更新将发送给您的联系人。 No comment provided by engineer. - - Add contact - 添加联系人 + + Add friends + 添加好友 No comment provided by engineer. - - Add preset servers - 添加预设服务器 + + Add list + 添加列表 No comment provided by engineer. @@ -591,14 +631,19 @@ 添加个人资料 No comment provided by engineer. + + Add server + 添加服务器 + No comment provided by engineer. + Add servers by scanning QR codes. 扫描二维码来添加服务器。 No comment provided by engineer. - - Add server… - 添加服务器… + + Add team members + 添加团队成员 No comment provided by engineer. @@ -606,11 +651,46 @@ 添加另一设备 No comment provided by engineer. + + Add to list + 添加到列表 + No comment provided by engineer. + Add welcome message 添加欢迎信息 No comment provided by engineer. + + Add your team members to the conversations. + 将你的团队成员加入对话。 + No comment provided by engineer. + + + Added media & file servers + 已添加媒体和文件服务器 + No comment provided by engineer. + + + Added message servers + 已添加消息服务器 + No comment provided by engineer. + + + Additional accent + 附加重音 + No comment provided by engineer. + + + Additional accent 2 + 附加重音 2 + No comment provided by engineer. + + + Additional secondary + 附加二级 + No comment provided by engineer. + Address 地址 @@ -621,8 +701,19 @@ 将中止地址更改。将使用旧接收地址。 No comment provided by engineer. + + Address or 1-time link? + 地址还是一次性链接? + No comment provided by engineer. + + + Address settings + 地址设置 + No comment provided by engineer. + Admins can block a member for all. + 管理员可以为所有人封禁一名成员。 No comment provided by engineer. @@ -635,6 +726,16 @@ 高级网络设置 No comment provided by engineer. + + Advanced settings + 高级设置 + No comment provided by engineer. + + + All + 全部 + No comment provided by engineer. + All app data is deleted. 已删除所有应用程序数据。 @@ -645,16 +746,31 @@ 所有聊天记录和消息将被删除——这一行为无法撤销! No comment provided by engineer. + + All chats will be removed from the list %@, and the list deleted. + 列表 %@ 和其中全部聊天将被删除。 + alert message + All data is erased when it is entered. 所有数据在输入后将被删除。 No comment provided by engineer. + + All data is kept private on your device. + 所有数据都是您设备的私有数据. + No comment provided by engineer. + All group members will remain connected. 所有群组成员将保持连接。 No comment provided by engineer. + + All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages. + 所有消息和文件均通过**端到端加密**发送;私信以量子安全方式发送。 + No comment provided by engineer. + All messages will be deleted - this cannot be undone! 所有消息都将被删除 - 这无法被撤销! @@ -667,6 +783,22 @@ All new messages from %@ will be hidden! + 来自 %@ 的所有新消息都将被隐藏! + No comment provided by engineer. + + + All profiles + 所有配置文件 + profile dropdown + + + All reports will be archived for you. + 将为你存档所有举报。 + No comment provided by engineer. + + + All servers + 全部服务器 No comment provided by engineer. @@ -681,6 +813,7 @@ All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + 你的所有联系人、对话和文件将被安全加密并分块上传到配置的 XFTP 中继。 No comment provided by engineer. @@ -693,14 +826,24 @@ 仅当您的联系人允许时才允许呼叫。 No comment provided by engineer. + + Allow calls? + 允许通话? + No comment provided by engineer. + Allow disappearing messages only if your contact allows it to you. 仅当您的联系人允许时才允许限时消息。 No comment provided by engineer. + + Allow downgrade + 允许降级 + No comment provided by engineer. + Allow irreversible message deletion only if your contact allows it to you. (24 hours) - 仅有您的联系人许可后才允许不可撤回消息移除。 + 仅有您的联系人许可后才允许不可撤回消息移除 No comment provided by engineer. @@ -723,9 +866,24 @@ 允许发送限时消息。 No comment provided by engineer. + + Allow sharing + 允许共享 + No comment provided by engineer. + Allow to irreversibly delete sent messages. (24 hours) - 允许不可撤回地删除已发送消息。 + 允许不可撤回地删除已发送消息 + No comment provided by engineer. + + + Allow to report messsages to moderators. + 允许向 moderators 举报消息。 + No comment provided by engineer. + + + Allow to send SimpleX links. + 允许发送 SimpleX 链接。 No comment provided by engineer. @@ -760,7 +918,7 @@ Allow your contacts to irreversibly delete sent messages. (24 hours) - 允许您的联系人不可撤回地删除已发送消息。 + 允许您的联系人不可撤回地删除已发送消息 No comment provided by engineer. @@ -788,6 +946,11 @@ 已经加入了该群组! No comment provided by engineer. + + Always use private routing. + 始终使用私有路由。 + No comment provided by engineer. + Always use relay 一直使用中继 @@ -798,11 +961,21 @@ 已创建一个包含所提供名字的空白聊天资料,应用程序照常打开。 No comment provided by engineer. + + Another reason + 另一个理由 + report reason + Answer call 接听来电 No comment provided by engineer. + + Anybody can host servers. + 任何人都可以托管服务器。 + No comment provided by engineer. + App build: %@ 应用程序构建:%@ @@ -810,6 +983,7 @@ App data migration + 应用数据迁移 No comment provided by engineer. @@ -817,6 +991,11 @@ 应用程序为新的本地文件(视频除外)加密。 No comment provided by engineer. + + App group: + 应用组: + No comment provided by engineer. + App icon 应用程序图标 @@ -832,6 +1011,11 @@ 应用程序密码被替换为自毁密码。 No comment provided by engineer. + + App session + 应用会话 + No comment provided by engineer. + App version 应用程序版本 @@ -849,14 +1033,62 @@ Apply + 应用 + No comment provided by engineer. + + + Apply to + 应用于 + No comment provided by engineer. + + + Archive + 存档 + No comment provided by engineer. + + + Archive %lld reports? + 存档 %lld 个举报? + No comment provided by engineer. + + + Archive all reports? + 存档所有举报? No comment provided by engineer. Archive and upload + 存档和上传 + No comment provided by engineer. + + + Archive contacts to chat later. + 存档联系人以便稍后聊天. + No comment provided by engineer. + + + Archive report + 存档举报 + No comment provided by engineer. + + + Archive report? + 存档举报? + No comment provided by engineer. + + + Archive reports + 存档举报 + swipe action + + + Archived contacts + 已存档的联系人 No comment provided by engineer. Archiving database + 正在存档数据库 No comment provided by engineer. @@ -919,11 +1151,21 @@ 自动接受图片 No comment provided by engineer. + + Auto-accept settings + 自动接受设置 + alert title + Back 返回 No comment provided by engineer. + + Background + 背景 + No comment provided by engineer. + Bad desktop address 糟糕的桌面地址 @@ -939,16 +1181,61 @@ 错误消息散列 No comment provided by engineer. + + Better calls + 更佳的通话 + No comment provided by engineer. + Better groups 更佳的群组 No comment provided by engineer. + + Better groups performance + 更好的群性能 + No comment provided by engineer. + + + Better message dates. + 更好的消息日期。 + No comment provided by engineer. + Better messages 更好的消息 No comment provided by engineer. + + Better networking + 更好的网络 + No comment provided by engineer. + + + Better notifications + 更佳的通知 + No comment provided by engineer. + + + Better privacy and security + 更好的隐私和安全 + No comment provided by engineer. + + + Better security ✅ + 更佳的安全性✅ + No comment provided by engineer. + + + Better user experience + 更佳的使用体验 + No comment provided by engineer. + + + Black + 黑色 + No comment provided by engineer. + Block 封禁 @@ -984,6 +1271,16 @@ 由管理员封禁 No comment provided by engineer. + + Blur for better privacy. + 模糊处理,提高私密性. + No comment provided by engineer. + + + Blur media + 模糊媒体 + No comment provided by engineer. + Both you and your contact can add message reactions. 您和您的联系人都可以添加消息回应。 @@ -991,7 +1288,7 @@ Both you and your contact can irreversibly delete sent messages. (24 hours) - 您和您的联系人都可以不可逆转地删除已发送的消息。 + 您和您的联系人都可以不可逆转地删除已发送的消息 No comment provided by engineer. @@ -1014,11 +1311,35 @@ 保加利亚语、芬兰语、泰语和乌克兰语——感谢用户和[Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! No comment provided by engineer. + + Business address + 企业地址 + No comment provided by engineer. + + + Business chats + 企业聊天 + No comment provided by engineer. + + + Businesses + 企业 + No comment provided by engineer. + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). 通过聊天资料(默认)或者[通过连接](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)。 No comment provided by engineer. + + By using SimpleX Chat you agree to: +- send only legal content in public groups. +- respect other users – no spam. + 使用 SimpleX Chat 代表您同意: +- 在公开群中只发送合法内容 +- 尊重其他用户 – 没有垃圾信息。 + No comment provided by engineer. + Call already ended! 通话已结束! @@ -1029,11 +1350,26 @@ 通话 No comment provided by engineer. + + Calls prohibited! + 禁止来电! + No comment provided by engineer. + Camera not available 相机不可用 No comment provided by engineer. + + Can't call contact + 无法呼叫联系人 + No comment provided by engineer. + + + Can't call member + 无法呼叫成员 + No comment provided by engineer. + Can't invite contact! 无法邀请联系人! @@ -1044,13 +1380,20 @@ 无法邀请联系人! No comment provided by engineer. + + Can't message member + 无法向成员发送消息 + No comment provided by engineer. + Cancel 取消 - No comment provided by engineer. + alert action +alert button Cancel migration + 取消迁移 No comment provided by engineer. @@ -1058,9 +1401,24 @@ 无法访问钥匙串以保存数据库密码 No comment provided by engineer. + + Cannot forward message + 无法转发消息 + No comment provided by engineer. + Cannot receive file 无法接收文件 + alert title + + + Capacity exceeded - recipient did not receive previously sent messages. + 超出容量-收件人未收到以前发送的邮件。 + snd error text + + + Cellular + 移动网络 No comment provided by engineer. @@ -1068,6 +1426,16 @@ 更改 No comment provided by engineer. + + Change automatic message deletion? + 更改消息自动删除设置? + alert title + + + Change chat profiles + 更改聊天资料 + authentication reason + Change database passphrase? 更改数据库密码? @@ -1112,11 +1480,26 @@ Change self-destruct passcode 更改自毁密码 authentication reason - set passcode view +set passcode view - - Chat archive - 聊天档案 + + Chat + 聊天 + No comment provided by engineer. + + + Chat already exists + 聊天已存在 + No comment provided by engineer. + + + Chat already exists! + 聊天已存在! + No comment provided by engineer. + + + Chat colors + 聊天颜色 No comment provided by engineer. @@ -1134,6 +1517,11 @@ 聊天数据库已删除 No comment provided by engineer. + + Chat database exported + 导出的聊天数据库 + No comment provided by engineer. + Chat database imported 聊天数据库已导入 @@ -1154,8 +1542,14 @@ 聊天已停止。如果你已经在另一台设备商使用过此数据库,你应该在启动聊天前将数据库传输回来。 No comment provided by engineer. + + Chat list + 聊天列表 + No comment provided by engineer. + Chat migrated! + 已迁移聊天! No comment provided by engineer. @@ -1163,15 +1557,50 @@ 聊天偏好设置 No comment provided by engineer. + + Chat preferences were changed. + 聊天偏好设置已修改。 + alert message + + + Chat profile + 用户资料 + No comment provided by engineer. + + + Chat theme + 聊天主题 + No comment provided by engineer. + + + Chat will be deleted for all members - this cannot be undone! + 将为所有成员删除聊天 - 此操作无法撤销! + No comment provided by engineer. + + + Chat will be deleted for you - this cannot be undone! + 将为你删除聊天 - 此操作无法撤销! + No comment provided by engineer. + Chats 聊天 No comment provided by engineer. + + Check messages every 20 min. + 每 20 分钟检查消息。 + No comment provided by engineer. + + + Check messages when allowed. + 在被允许时检查消息。 + No comment provided by engineer. + Check server address and try again. 检查服务器地址并再试一次。 - No comment provided by engineer. + alert title Chinese and Spanish interface @@ -1180,6 +1609,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + 在新设备上选择“从另一个设备迁移”并扫描二维码。 No comment provided by engineer. @@ -1192,10 +1622,25 @@ 从库中选择 No comment provided by engineer. + + Chunks deleted + 已删除的块 + No comment provided by engineer. + + + Chunks downloaded + 下载的块 + No comment provided by engineer. + + + Chunks uploaded + 已下载的区块 + No comment provided by engineer. + Clear 清除 - No comment provided by engineer. + swipe action Clear conversation @@ -1207,6 +1652,16 @@ 清除对话吗? No comment provided by engineer. + + Clear group? + 清除群? + No comment provided by engineer. + + + Clear or delete group? + 清除还是删除群? + No comment provided by engineer. + Clear private notes? 清除私密笔记? @@ -1217,11 +1672,21 @@ 清除验证 No comment provided by engineer. - - Colors - 颜色 + + Color chats with the new themes. + 使用新主题为聊天着色。 No comment provided by engineer. + + Color mode + 颜色模式 + No comment provided by engineer. + + + Community guidelines violation + 违反社区指导方针 + report reason + Compare file 对比文件 @@ -1232,11 +1697,56 @@ 与您的联系人比较安全码。 No comment provided by engineer. + + Completed + 已完成 + No comment provided by engineer. + + + Conditions accepted on: %@. + 已于 %@ 接受条款。 + No comment provided by engineer. + + + Conditions are accepted for the operator(s): **%@**. + 已接受运营方 **%@** 的条款。 + No comment provided by engineer. + + + Conditions are already accepted for these operator(s): **%@**. + 已经接受下列运营方的条款:**%@**。 + No comment provided by engineer. + + + Conditions of use + 使用条款 + No comment provided by engineer. + + + Conditions will be accepted for the operator(s): **%@**. + 将接受下列运营方的条款:**%@**。 + No comment provided by engineer. + + + Conditions will be accepted on: %@. + 将于 %@ 接受条款。 + No comment provided by engineer. + + + Conditions will be automatically accepted for enabled operators on: %@. + 将在 %@ 自动接受启用的运营方的条款。 + No comment provided by engineer. + Configure ICE servers 配置 ICE 服务器 No comment provided by engineer. + + Configure server operators + 配置服务器运营方 + No comment provided by engineer. + Confirm 确认 @@ -1247,13 +1757,24 @@ 确认密码 No comment provided by engineer. + + Confirm contact deletion? + 确认删除联系人? + No comment provided by engineer. + Confirm database upgrades 确认数据库升级 No comment provided by engineer. + + Confirm files from unknown servers. + 确认来自未知服务器的文件。 + No comment provided by engineer. + Confirm network settings + 确认网络设置 No comment provided by engineer. @@ -1268,12 +1789,19 @@ Confirm that you remember database passphrase to migrate it. + 请在迁移前确认你记得数据库的密码短语。 No comment provided by engineer. Confirm upload + 确认上传 No comment provided by engineer. + + Confirmed + 已确定 + token status text + Connect 连接 @@ -1294,6 +1822,11 @@ 连接到桌面 No comment provided by engineer. + + Connect to your friends faster. + 更快地与您的朋友联系。 + No comment provided by engineer. + Connect to yourself? 连接到你自己? @@ -1302,15 +1835,20 @@ Connect to yourself? This is your own SimpleX address! + 与自己建立联系? +这是您自己的 SimpleX 地址! No comment provided by engineer. Connect to yourself? This is your own one-time link! + 与自己建立联系? +这是您自己的一次性链接! No comment provided by engineer. Connect via contact address + 通过联系地址连接 No comment provided by engineer. @@ -1325,6 +1863,12 @@ This is your own one-time link! Connect with %@ + 与 %@连接 + No comment provided by engineer. + + + Connected + 已连接 No comment provided by engineer. @@ -1332,11 +1876,21 @@ This is your own one-time link! 已连接的桌面 No comment provided by engineer. + + Connected servers + 已连接的服务器 + No comment provided by engineer. + Connected to desktop 已连接到桌面 No comment provided by engineer. + + Connecting + 正在连接 + No comment provided by engineer. + Connecting to server… 连接服务器中…… @@ -1347,6 +1901,11 @@ This is your own one-time link! 连接服务器中……(错误:%@) No comment provided by engineer. + + Connecting to contact, please wait or check later! + 正在连接到联系人,请稍候或稍后检查! + No comment provided by engineer. + Connecting to desktop 正连接到桌面 @@ -1357,6 +1916,16 @@ This is your own one-time link! 连接 No comment provided by engineer. + + Connection and servers status. + 连接和服务器状态。 + No comment provided by engineer. + + + Connection blocked + 连接被阻止 + No comment provided by engineer. + Connection error 连接错误 @@ -1367,11 +1936,37 @@ This is your own one-time link! 连接错误(AUTH) No comment provided by engineer. + + Connection is blocked by server operator: +%@ + 连接被运营方 %@ 阻止 + No comment provided by engineer. + + + Connection not ready. + 连接未就绪。 + No comment provided by engineer. + + + Connection notifications + 连接通知 + No comment provided by engineer. + Connection request sent! 已发送连接请求! No comment provided by engineer. + + Connection requires encryption renegotiation. + 连接需要加密重协商。 + No comment provided by engineer. + + + Connection security + 连接安全性 + No comment provided by engineer. + Connection terminated 连接被终止 @@ -1382,6 +1977,16 @@ This is your own one-time link! 连接超时 No comment provided by engineer. + + Connection with desktop stopped + 与桌面的连接已停止 + No comment provided by engineer. + + + Connections + 连接 + No comment provided by engineer. + Contact allows 联系人允许 @@ -1392,6 +1997,11 @@ This is your own one-time link! 联系人已存在 No comment provided by engineer. + + Contact deleted! + 联系人已删除! + No comment provided by engineer. + Contact hidden: 联系人已隐藏: @@ -1402,9 +2012,9 @@ This is your own one-time link! 联系已连接 notification - - Contact is not connected yet! - 联系人尚未连接! + + Contact is deleted. + 联系人被删除。 No comment provided by engineer. @@ -1417,6 +2027,11 @@ This is your own one-time link! 联系人偏好设置 No comment provided by engineer. + + Contact will be deleted - this cannot be undone! + 联系人将被删除-这是无法撤消的! + No comment provided by engineer. + Contacts 联系人 @@ -1427,23 +2042,44 @@ This is your own one-time link! 联系人可以将信息标记为删除;您将可以查看这些信息。 No comment provided by engineer. + + Content violates conditions of use + 内容违反使用条款 + blocking reason + Continue 继续 No comment provided by engineer. + + Conversation deleted! + 对话已删除! + No comment provided by engineer. + Copy 复制 - chat item action + No comment provided by engineer. + + + Copy error + 复制错误 + No comment provided by engineer. Core version: v%@ 核心版本: v%@ No comment provided by engineer. + + Corner + 拐角 + No comment provided by engineer. + Correct name to %@? + 将名称更正为 %@? No comment provided by engineer. @@ -1451,6 +2087,11 @@ This is your own one-time link! 创建 No comment provided by engineer. + + Create 1-time link + 创建一次性链接 + No comment provided by engineer. + Create SimpleX address 创建 SimpleX 地址 @@ -1458,12 +2099,7 @@ This is your own one-time link! Create a group using a random profile. - 使用随机身份创建群组 - No comment provided by engineer. - - - Create an address to let people connect with you. - 创建一个地址,让人们与您联系。 + 使用随机身份创建群组. No comment provided by engineer. @@ -1486,6 +2122,11 @@ This is your own one-time link! 创建链接 No comment provided by engineer. + + Create list + 创建列表 + No comment provided by engineer. + Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 在[桌面应用程序](https://simplex.chat/downloads/)中创建新的个人资料。 💻 @@ -1511,6 +2152,11 @@ This is your own one-time link! 创建您的资料 No comment provided by engineer. + + Created + 已创建 + No comment provided by engineer. + Created at 创建于 @@ -1518,15 +2164,12 @@ This is your own one-time link! Created at: %@ + 创建于:%@ copied message info - - Created on %@ - 创建于 %@ - No comment provided by engineer. - Creating archive link + 正在创建存档链接 No comment provided by engineer. @@ -1539,11 +2182,21 @@ This is your own one-time link! 当前密码 No comment provided by engineer. + + Current conditions text couldn't be loaded, you can review conditions via this link: + 无法加载当前条款文本,你可以通过此链接审阅条款: + No comment provided by engineer. + Current passphrase… 现有密码…… No comment provided by engineer. + + Current profile + 当前配置文件 + No comment provided by engineer. + Currently maximum supported file size is %@. 目前支持的最大文件大小为 %@。 @@ -1554,11 +2207,26 @@ This is your own one-time link! 自定义时间 No comment provided by engineer. + + Customizable message shape. + 可自定义消息形状。 + No comment provided by engineer. + + + Customize theme + 自定义主题 + No comment provided by engineer. + Dark 深色 No comment provided by engineer. + + Dark mode colors + 深色模式颜色 + No comment provided by engineer. + Database ID 数据库 ID @@ -1657,6 +2325,11 @@ This is your own one-time link! 应用程序重新启动时将迁移数据库 No comment provided by engineer. + + Debug delivery + 调试交付 + No comment provided by engineer. + Decentralized 分散式 @@ -1670,15 +2343,17 @@ This is your own one-time link! Delete 删除 - chat item action + alert action +swipe action + + + Delete %lld messages of members? + 删除成员的 %lld 消息? + No comment provided by engineer. Delete %lld messages? - No comment provided by engineer. - - - Delete Contact - 删除联系人 + 删除 %lld 消息? No comment provided by engineer. @@ -1706,14 +2381,14 @@ This is your own one-time link! 删除并通知联系人 No comment provided by engineer. - - Delete archive - 删除档案 + + Delete chat + 删除聊天 No comment provided by engineer. - - Delete chat archive? - 删除聊天档案? + + Delete chat messages from your device. + 从你的设备删除聊天消息。 No comment provided by engineer. @@ -1726,6 +2401,11 @@ This is your own one-time link! 删除聊天资料? No comment provided by engineer. + + Delete chat? + 删除聊天? + No comment provided by engineer. + Delete connection 删除连接 @@ -1736,9 +2416,9 @@ This is your own one-time link! 删除联系人 No comment provided by engineer. - - Delete contact? -This cannot be undone! + + Delete contact? + 删除联系人? No comment provided by engineer. @@ -1748,6 +2428,7 @@ This cannot be undone! Delete database from this device + 从这部设备上删除数据库 No comment provided by engineer. @@ -1800,6 +2481,11 @@ This cannot be undone! 删除链接? No comment provided by engineer. + + Delete list? + 删除列表? + alert title + Delete member message? 删除成员消息? @@ -1813,7 +2499,7 @@ This cannot be undone! Delete messages 删除消息 - No comment provided by engineer. + alert button Delete messages after @@ -1830,9 +2516,9 @@ This cannot be undone! 删除旧数据库吗? No comment provided by engineer. - - Delete pending connection - 删除挂起连接 + + Delete or moderate up to 200 messages. + 允许自行删除或管理员移除最多200条消息。 No comment provided by engineer. @@ -1850,11 +2536,31 @@ This cannot be undone! 删除队列 server test step + + Delete report + 删除举报 + No comment provided by engineer. + + + Delete up to 20 messages at once. + 一次最多删除 20 条信息。 + No comment provided by engineer. + Delete user profile? 删除用户资料? No comment provided by engineer. + + Delete without notification + 删除而不通知 + No comment provided by engineer. + + + Deleted + 已删除 + No comment provided by engineer. + Deleted at 已删除于 @@ -1865,6 +2571,16 @@ This cannot be undone! 已删除于:%@ copied message info + + Deletion errors + 删除错误 + No comment provided by engineer. + + + Delivered even when Apple drops them. + 已送达,即使苹果已将其删除。 + No comment provided by engineer. + Delivery 传送 @@ -1892,6 +2608,7 @@ This cannot be undone! Desktop app version %@ is not compatible with this app. + 桌面应用程序版本 %@ 与此应用程序不兼容。 No comment provided by engineer. @@ -1899,11 +2616,41 @@ This cannot be undone! 桌面设备 No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + 目标服务器地址 %@ 与转发服务器 %@ 设置不兼容。 + No comment provided by engineer. + + + Destination server error: %@ + 目标服务器错误:%@ + snd error text + + + Destination server version of %@ is incompatible with forwarding server %@. + 目标服务器版本 %@ 与转发服务器 %@ 不兼容。 + No comment provided by engineer. + + + Detailed statistics + 详细的统计数据 + No comment provided by engineer. + + + Details + 详细信息 + No comment provided by engineer. + Develop 开发 No comment provided by engineer. + + Developer options + 开发者选项 + No comment provided by engineer. + Developer tools 开发者工具 @@ -1916,7 +2663,7 @@ This cannot be undone! Device authentication is disabled. Turning off SimpleX Lock. - 设备验证被禁用。关闭 SimpleX 锁定。 + 设备验证已禁用。 SimpleX 已解锁。 No comment provided by engineer. @@ -1934,9 +2681,14 @@ This cannot be undone! 私信 chat feature - - Direct messages between members are prohibited in this group. - 此群中禁止成员之间私信。 + + Direct messages between members are prohibited in this chat. + 此群禁止成员间私信。 + No comment provided by engineer. + + + Direct messages between members are prohibited. + 此群禁止成员间私信。 No comment provided by engineer. @@ -1949,11 +2701,26 @@ This cannot be undone! 禁用 SimpleX 锁定 authentication reason + + Disable automatic message deletion? + 禁用消息自动销毁? + alert title + + + Disable delete messages + 停用消息删除 + alert button + Disable for all 全部禁用 No comment provided by engineer. + + Disabled + 禁用 + No comment provided by engineer. + Disappearing message 限时消息 @@ -1969,8 +2736,8 @@ This cannot be undone! 此聊天中禁止显示限时消息。 No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. 该组禁止限时消息。 No comment provided by engineer. @@ -2004,11 +2771,21 @@ This cannot be undone! 通过本地网络发现 No comment provided by engineer. + + Do NOT send messages directly, even if your or destination server does not support private routing. + 请勿直接发送消息,即使您的服务器或目标服务器不支持私有路由。 + No comment provided by engineer. + Do NOT use SimpleX for emergency calls. 请勿使用 SimpleX 进行紧急通话。 No comment provided by engineer. + + Do NOT use private routing. + 不要使用私有路由。 + No comment provided by engineer. + Do it later 稍后再做 @@ -2019,6 +2796,16 @@ This cannot be undone! 不给新成员发送历史消息。 No comment provided by engineer. + + Do not use credentials with proxy. + 代理不使用身份验证凭据。 + No comment provided by engineer. + + + Documents: + 文档: + No comment provided by engineer. + Don't create address 不创建地址 @@ -2029,18 +2816,40 @@ This cannot be undone! 不要启用 No comment provided by engineer. + + Don't miss important messages. + 不错过重要消息。 + No comment provided by engineer. + Don't show again 不再显示 No comment provided by engineer. + + Done + 完成 + No comment provided by engineer. + Downgrade and open chat 降级并打开聊天 No comment provided by engineer. + + Download + 下载 + alert button +chat item action + + + Download errors + 下载错误 + No comment provided by engineer. + Download failed + 下载失败了 No comment provided by engineer. @@ -2048,12 +2857,29 @@ This cannot be undone! 下载文件 server test step + + Download files + 下载文件 + alert action + + + Downloaded + 已下载 + No comment provided by engineer. + + + Downloaded files + 下载的文件 + No comment provided by engineer. + Downloading archive + 正在下载存档 No comment provided by engineer. Downloading link details + 正在下载链接详情 No comment provided by engineer. @@ -2066,6 +2892,11 @@ This cannot be undone! 时长 No comment provided by engineer. + + E2E encrypted notifications. + 端到端加密的通知。 + No comment provided by engineer. + Edit 编辑 @@ -2086,6 +2917,11 @@ This cannot be undone! 启用(保持覆盖) No comment provided by engineer. + + Enable Flux in Network & servers settings for better metadata privacy. + 在“网络&服务器”设置中启用 Flux,更好地保护元数据隐私。 + No comment provided by engineer. + Enable SimpleX Lock 启用 SimpleX 锁定 @@ -2099,7 +2935,7 @@ This cannot be undone! Enable automatic message deletion? 启用自动删除消息? - No comment provided by engineer. + alert title Enable camera access @@ -2113,6 +2949,7 @@ This cannot be undone! Enable in direct chats (BETA)! + 在私聊中开启(公测)! No comment provided by engineer. @@ -2145,6 +2982,16 @@ This cannot be undone! 启用自毁密码 set passcode view + + Enabled + 已启用 + No comment provided by engineer. + + + Enabled for + 启用对象 + No comment provided by engineer. + Encrypt 加密 @@ -2177,6 +3024,7 @@ This cannot be undone! Encrypted message: app is stopped + 加密消息:应用程序已停止 notification @@ -2214,6 +3062,11 @@ This cannot be undone! 加密重协商失败了。 No comment provided by engineer. + + Encryption renegotiation in progress. + 正进行加密重协商。 + No comment provided by engineer. + Enter Passcode 输入密码 @@ -2226,10 +3079,12 @@ This cannot be undone! Enter group name… + 输入组名称… No comment provided by engineer. Enter passphrase + 输入密码短语 No comment provided by engineer. @@ -2264,6 +3119,7 @@ This cannot be undone! Enter your name… + 请输入您的姓名… No comment provided by engineer. @@ -2276,30 +3132,36 @@ This cannot be undone! 中止地址更改错误 No comment provided by engineer. + + Error accepting conditions + 接受条款出错 + alert title + Error accepting contact request 接受联系人请求错误 No comment provided by engineer. - - Error accessing database file - 访问数据库文件错误 - No comment provided by engineer. - Error adding member(s) 添加成员错误 No comment provided by engineer. - - Error allowing contact PQ encryption - No comment provided by engineer. + + Error adding server + 添加服务器出错 + alert title Error changing address 更改地址错误 No comment provided by engineer. + + Error changing connection profile + 更改连接资料出错 + No comment provided by engineer. + Error changing role 更改角色错误 @@ -2310,6 +3172,20 @@ This cannot be undone! 更改设置错误 No comment provided by engineer. + + Error changing to incognito! + 切换至隐身聊天出错! + No comment provided by engineer. + + + Error checking token status + No comment provided by engineer. + + + Error connecting to forwarding server %@. Please try later. + 连接到转发服务器 %@ 时出错。请稍后尝试。 + No comment provided by engineer. + Error creating address 创建地址错误 @@ -2325,6 +3201,11 @@ This cannot be undone! 创建群组链接错误 No comment provided by engineer. + + Error creating list + 创建列表出错 + alert title + Error creating member contact 创建成员联系人时出错 @@ -2340,6 +3221,11 @@ This cannot be undone! 创建资料错误! No comment provided by engineer. + + Error creating report + 创建举报出错 + No comment provided by engineer. + Error decrypting file 解密文件时出错 @@ -2360,11 +3246,6 @@ This cannot be undone! 删除连接错误 No comment provided by engineer. - - Error deleting contact - 删除联系人错误 - No comment provided by engineer. - Error deleting database 删除数据库错误 @@ -2387,6 +3268,7 @@ This cannot be undone! Error downloading the archive + 下载存档出错 No comment provided by engineer. @@ -2409,6 +3291,11 @@ This cannot be undone! 导出聊天数据库错误 No comment provided by engineer. + + Error exporting theme: %@ + 导出主题时出错: %@ + No comment provided by engineer. + Error importing chat database 导入聊天数据库错误 @@ -2419,28 +3306,54 @@ This cannot be undone! 加入群组错误 No comment provided by engineer. - - Error loading %@ servers - 加载 %@ 服务器错误 + + Error loading servers + 加载服务器出错 + alert title + + + Error migrating settings + 迁移设置出错 No comment provided by engineer. Error opening chat + 打开聊天时出错 No comment provided by engineer. Error receiving file 接收文件错误 + alert title + + + Error reconnecting server + 重新连接服务器时出错 No comment provided by engineer. + + Error reconnecting servers + 重新连接服务器时出错 + No comment provided by engineer. + + + Error registering for notifications + 注册消息推送出错 + alert title + Error removing member 删除成员错误 No comment provided by engineer. - - Error saving %@ servers - 保存 %@ 服务器错误 + + Error reordering lists + 重排列表出错 + alert title + + + Error resetting statistics + 重置统计信息时出错 No comment provided by engineer. @@ -2448,6 +3361,11 @@ This cannot be undone! 保存 ICE 服务器错误 No comment provided by engineer. + + Error saving chat list + 保存聊天列表出错 + alert title + Error saving group profile 保存群组资料错误 @@ -2463,8 +3381,14 @@ This cannot be undone! 保存密码到钥匙串错误 No comment provided by engineer. + + Error saving servers + 保存服务器出错 + alert title + Error saving settings + 保存设置出错 when migrating @@ -2474,6 +3398,7 @@ This cannot be undone! Error scanning code: %@ + 扫描代码时出错:%@ No comment provided by engineer. @@ -2506,16 +3431,26 @@ This cannot be undone! 停止聊天错误 No comment provided by engineer. + + Error switching profile + 切换配置文件出错 + No comment provided by engineer. + Error switching profile! 切换资料错误! - No comment provided by engineer. + alertTitle Error synchronizing connection 同步连接错误 No comment provided by engineer. + + Error testing server connection + 检验服务器连接出错 + No comment provided by engineer. + Error updating group link 更新群组链接错误 @@ -2526,6 +3461,11 @@ This cannot be undone! 更新消息错误 No comment provided by engineer. + + Error updating server + 更新服务器出错 + alert title + Error updating settings 更新设置错误 @@ -2538,10 +3478,12 @@ This cannot be undone! Error uploading the archive + 上传存档出错 No comment provided by engineer. Error verifying passphrase: + 验证密码短语出错: No comment provided by engineer. @@ -2552,7 +3494,9 @@ This cannot be undone! Error: %@ 错误: %@ - No comment provided by engineer. + alert message +file error text +snd error text Error: URL is invalid @@ -2564,6 +3508,16 @@ This cannot be undone! 错误:没有数据库文件 No comment provided by engineer. + + Errors + 错误 + No comment provided by engineer. + + + Errors in servers configuration. + 服务器配置有错误。 + servers error + Even when disabled in the conversation. 即使在对话中被禁用。 @@ -2579,6 +3533,11 @@ This cannot be undone! 展开 chat item action + + Expired + 已过期 + token status text + Export database 导出数据库 @@ -2589,6 +3548,11 @@ This cannot be undone! 导出错误: No comment provided by engineer. + + Export theme + 导出主题 + No comment provided by engineer. + Exported database archive. 导出数据库归档。 @@ -2596,6 +3560,7 @@ This cannot be undone! Exported file doesn't exist + 导出的文件不存在 No comment provided by engineer. @@ -2613,16 +3578,70 @@ This cannot be undone! 快速且无需等待发件人在线! No comment provided by engineer. + + Faster deletion of groups. + 更快地删除群。 + No comment provided by engineer. + Faster joining and more reliable messages. 加入速度更快、信息更可靠。 No comment provided by engineer. + + Faster sending messages. + 更快发送消息。 + No comment provided by engineer. + Favorite 最喜欢 + swipe action + + + Favorites + 收藏 No comment provided by engineer. + + File error + 文件错误 + file error alert title + + + File errors: +%@ + 文件错误: +%@ + alert message + + + File is blocked by server operator: +%@. + 文件被服务器运营方阻止: +%@。 + file error text + + + File not found - most likely file was deleted or cancelled. + 找不到文件 - 很可能文件已被删除或取消。 + file error text + + + File server error: %@ + 文件服务器错误:%@ + file error text + + + File status + 文件状态 + No comment provided by engineer. + + + File status: %@ + 文件状态:%@ + copied message info + File will be deleted from servers. 文件将从服务器中删除。 @@ -2643,6 +3662,11 @@ This cannot be undone! 文件:%@ No comment provided by engineer. + + Files + 文件 + No comment provided by engineer. + Files & media 文件和媒体 @@ -2653,11 +3677,16 @@ This cannot be undone! 文件和媒体 chat feature - - Files and media are prohibited in this group. + + Files and media are prohibited. 此群组中禁止文件和媒体。 No comment provided by engineer. + + Files and media not allowed + 不允许文件和媒体 + No comment provided by engineer. + Files and media prohibited! 禁止文件和媒体! @@ -2670,10 +3699,12 @@ This cannot be undone! Finalize migration + 完成迁移 No comment provided by engineer. Finalize migration on another device. + 在另一部设备上完成迁移. No comment provided by engineer. @@ -2716,11 +3747,115 @@ This cannot be undone! 修复群组成员不支持的问题 No comment provided by engineer. + + For all moderators + 所有 moderators + No comment provided by engineer. + + + For chat profile %@: + 为聊天资料 %@: + servers error + For console 用于控制台 No comment provided by engineer. + + For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server. + 比如,如果你通过 SimpleX 服务器收到消息,应用会通过 Flux 服务器传送它们。 + No comment provided by engineer. + + + For me + 仅自己 + No comment provided by engineer. + + + For private routing + 用于私密路由 + No comment provided by engineer. + + + For social media + 用于社交媒体 + No comment provided by engineer. + + + Forward + 转发 + chat item action + + + Forward %d message(s)? + 转发 %d 条消息? + alert title + + + Forward and save messages + 转发并保存消息 + No comment provided by engineer. + + + Forward messages + 已转发的消息 + alert action + + + Forward messages without files? + 仅转发消息不转发文件? + alert message + + + Forward up to 20 messages at once. + 一次转发最多20条消息。 + No comment provided by engineer. + + + Forwarded + 已转发 + No comment provided by engineer. + + + Forwarded from + 转发自 + No comment provided by engineer. + + + Forwarding %lld messages + 正在转发 %lld 条消息 + No comment provided by engineer. + + + Forwarding server %@ failed to connect to destination server %@. Please try later. + 转发服务器 %@ 无法连接到目标服务器 %@。请稍后尝试。 + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + 转发服务器地址与网络设置不兼容:%@。 + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + 转发服务器版本与网络设置不兼容:%@。 + No comment provided by engineer. + + + Forwarding server: %1$@ +Destination server error: %2$@ + 转发服务器: %1$@ +目标服务器错误: %2$@ + snd error text + + + Forwarding server: %1$@ +Error: %2$@ + 转发服务器: %1$@ +错误: %2$@ + snd error text + Found desktop 找到了桌面 @@ -2741,11 +3876,6 @@ This cannot be undone! 全名(可选) No comment provided by engineer. - - Full name: - 全名: - No comment provided by engineer. - Fully decentralized – visible only to members. 完全去中心化 - 仅对成员可见。 @@ -2766,6 +3896,21 @@ This cannot be undone! GIF 和贴纸 No comment provided by engineer. + + Get notified when mentioned. + 被提及时收到通知。 + No comment provided by engineer. + + + Good afternoon! + 下午好! + message preview + + + Good morning! + 早上好! + message preview + Group 群组 @@ -2773,6 +3918,7 @@ This cannot be undone! Group already exists + 群组已存在 No comment provided by engineer. @@ -2820,36 +3966,6 @@ This cannot be undone! 群组链接 No comment provided by engineer. - - Group members can add message reactions. - 群组成员可以添加信息回应。 - No comment provided by engineer. - - - Group members can irreversibly delete sent messages. (24 hours) - 群组成员可以不可撤回地删除已发送的消息。 - No comment provided by engineer. - - - Group members can send direct messages. - 群组成员可以私信。 - No comment provided by engineer. - - - Group members can send disappearing messages. - 群组成员可以发送限时消息。 - No comment provided by engineer. - - - Group members can send files and media. - 群组成员可以发送文件和媒体。 - No comment provided by engineer. - - - Group members can send voice messages. - 群组成员可以发送语音消息。 - No comment provided by engineer. - Group message: 群组消息: @@ -2890,11 +4006,21 @@ This cannot be undone! 将为您删除群组——此操作无法撤消! No comment provided by engineer. + + Groups + + No comment provided by engineer. + Help 帮助 No comment provided by engineer. + + Help admins moderating their groups. + 帮助管理员管理群组。 + No comment provided by engineer. + Hidden 隐藏 @@ -2945,10 +4071,20 @@ This cannot be undone! SimpleX的工作原理 No comment provided by engineer. + + How it affects privacy + 它如何影响隐私 + No comment provided by engineer. + + + How it helps privacy + 它如何帮助隐私 + No comment provided by engineer. + How it works 工作原理 - No comment provided by engineer. + alert button How to @@ -2967,6 +4103,7 @@ This cannot be undone! Hungarian interface + 匈牙利语界面 No comment provided by engineer. @@ -2974,6 +4111,11 @@ This cannot be undone! ICE 服务器(每行一个) No comment provided by engineer. + + IP address + IP 地址 + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. 如果您不能亲自见面,可以在视频通话中展示二维码,或分享链接。 @@ -3014,8 +4156,8 @@ This cannot be undone! 立即 No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam 不受垃圾和骚扰消息影响 No comment provided by engineer. @@ -3036,10 +4178,24 @@ This cannot be undone! Import failed + 导入失败了 + No comment provided by engineer. + + + Import theme + 导入主题 No comment provided by engineer. Importing archive + 正在导入存档 + No comment provided by engineer. + + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + 改善传送,降低流量使用。 +更多改进即将推出! No comment provided by engineer. @@ -3059,6 +4215,7 @@ This cannot be undone! In order to continue, chat should be stopped. + 必须停止聊天才能继续。 No comment provided by engineer. @@ -3066,6 +4223,21 @@ This cannot be undone! 答复 No comment provided by engineer. + + In-call sounds + 通话声音 + No comment provided by engineer. + + + Inappropriate content + 不当内容 + report reason + + + Inappropriate profile + 不当个人资料 + report reason + Incognito 隐身聊天 @@ -3136,6 +4308,11 @@ This cannot be undone! 安装[用于终端的 SimpleX Chat](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. + + Instant + 即时 + No comment provided by engineer. + Instant push notifications will be hidden! @@ -3143,16 +4320,41 @@ This cannot be undone! No comment provided by engineer. - - Instantly - 即时 - No comment provided by engineer. - Interface 界面 No comment provided by engineer. + + Interface colors + 界面颜色 + No comment provided by engineer. + + + Invalid + 无效 + token status text + + + Invalid (bad token) + Token 无效 + token status text + + + Invalid (expired) + 无效(已过期) + token status text + + + Invalid (unregistered) + 无效(未注册) + token status text + + + Invalid (wrong topic) + 无效(话题有误) + token status text + Invalid QR code 无效的二维码 @@ -3170,10 +4372,12 @@ This cannot be undone! Invalid link + 无效链接 No comment provided by engineer. Invalid migration confirmation + 迁移确认无效 No comment provided by engineer. @@ -3183,12 +4387,13 @@ This cannot be undone! Invalid response + 无效的响应 No comment provided by engineer. Invalid server address! 无效的服务器地址! - No comment provided by engineer. + alert title Invalid status @@ -3210,6 +4415,11 @@ This cannot be undone! 邀请成员 No comment provided by engineer. + + Invite to chat + 邀请加入聊天 + No comment provided by engineer. + Invite to group 邀请加入群组 @@ -3225,8 +4435,8 @@ This cannot be undone! 此聊天中禁止不可撤回消息移除。 No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. 此群组中禁止不可撤回消息移除。 No comment provided by engineer. @@ -3251,6 +4461,11 @@ This cannot be undone! 3.连接被破坏。 No comment provided by engineer. + + It protects your IP address and connections. + 它可以保护您的 IP 地址和连接。 + No comment provided by engineer. + It seems like you are already connected via this link. If it is not the case, there was an error (%@). 您似乎已经通过此链接连接。如果不是这样,则有一个错误 (%@)。 @@ -3269,7 +4484,7 @@ This cannot be undone! Join 加入 - No comment provided by engineer. + swipe action Join group @@ -3293,11 +4508,14 @@ This cannot be undone! Join with current profile + 使用当前档案加入 No comment provided by engineer. Join your group? This is your link for group %@! + 加入您的群组? +这是您组 %@ 的链接! No comment provided by engineer. @@ -3308,16 +4526,22 @@ This is your link for group %@! Keep 保留 + alert action + + + Keep conversation + 保持对话 No comment provided by engineer. Keep the app open to use it from desktop + 保持应用程序打开状态以从桌面使用它 No comment provided by engineer. Keep unused invitation? 保留未使用的邀请吗? - No comment provided by engineer. + alert title Keep your connections @@ -3352,6 +4576,16 @@ This is your link for group %@! Leave 离开 + swipe action + + + Leave chat + 离开聊天 + No comment provided by engineer. + + + Leave chat? + 离开聊天? No comment provided by engineer. @@ -3394,6 +4628,21 @@ This is your link for group %@! 已链接桌面 No comment provided by engineer. + + List + 列表 + swipe action + + + List name and emoji should be different for all lists. + 所有列表的名称和表情符号都应不同。 + No comment provided by engineer. + + + List name... + 列表名… + No comment provided by engineer. + Live message! 实时消息! @@ -3404,11 +4653,6 @@ This is your link for group %@! 实时消息 No comment provided by engineer. - - Local - 本地 - No comment provided by engineer. - Local name 本地名称 @@ -3429,11 +4673,6 @@ This is your link for group %@! 锁定模式 No comment provided by engineer. - - Make a private connection - 建立私密连接 - No comment provided by engineer. - Make one message disappear 使一条消息消失 @@ -3444,21 +4683,11 @@ This is your link for group %@! 将个人资料设为私密! No comment provided by engineer. - - Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - 请确保 %@服 务器地址格式正确,每行一个地址并且不重复 (%@)。 - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. 确保 WebRTC ICE 服务器地址格式正确、每行分开且不重复。 No comment provided by engineer. - - Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - 许多人问: *如果SimpleX没有用户标识符,它怎么传递信息?* - No comment provided by engineer. - Mark deleted for everyone 标记为所有人已删除 @@ -3484,11 +4713,36 @@ This is your link for group %@! 最长30秒,立即接收。 No comment provided by engineer. + + Media & file servers + Media & file servers + No comment provided by engineer. + + + Medium + 中等 + blur media + Member 成员 No comment provided by engineer. + + Member inactive + 成员不活跃 + item status text + + + Member reports + 成员举报 + chat feature + + + Member role will be changed to "%@". All chat members will be notified. + 将变更成员角色为“%@”。所有成员都会收到通知。 + No comment provided by engineer. + Member role will be changed to "%@". All group members will be notified. 成员角色将更改为 "%@"。所有群成员将收到通知。 @@ -3499,11 +4753,66 @@ This is your link for group %@! 成员角色将更改为 "%@"。该成员将收到一份新的邀请。 No comment provided by engineer. + + Member will be removed from chat - this cannot be undone! + 将从聊天中删除成员 - 此操作无法撤销! + No comment provided by engineer. + Member will be removed from group - this cannot be undone! 成员将被移出群组——此操作无法撤消! No comment provided by engineer. + + Members can add message reactions. + 群组成员可以添加信息回应。 + No comment provided by engineer. + + + Members can irreversibly delete sent messages. (24 hours) + 群组成员可以不可撤回地删除已发送的消息 + No comment provided by engineer. + + + Members can report messsages to moderators. + 成员可以向 moderators 举报消息。 + No comment provided by engineer. + + + Members can send SimpleX links. + 群成员可发送 SimpleX 链接。 + No comment provided by engineer. + + + Members can send direct messages. + 群组成员可以私信。 + No comment provided by engineer. + + + Members can send disappearing messages. + 群组成员可以发送限时消息。 + No comment provided by engineer. + + + Members can send files and media. + 群组成员可以发送文件和媒体。 + No comment provided by engineer. + + + Members can send voice messages. + 群组成员可以发送语音消息。 + No comment provided by engineer. + + + Mention members 👋 + 提及成员👋 + No comment provided by engineer. + + + Menus + 菜单 + No comment provided by engineer. + Message delivery error 消息传递错误 @@ -3514,11 +4823,31 @@ This is your link for group %@! 消息送达回执! No comment provided by engineer. + + Message delivery warning + 消息传递警告 + item status text + Message draft 消息草稿 No comment provided by engineer. + + Message forwarded + 消息已转发 + item status text + + + Message may be delivered later if member becomes active. + 如果 member 变为活动状态,则稍后可能会发送消息。 + item status description + + + Message queue info + 消息队列信息 + No comment provided by engineer. + Message reactions 消息回应 @@ -3529,11 +4858,41 @@ This is your link for group %@! 该聊天禁用了消息回应。 No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. 该群组禁用了消息回应。 No comment provided by engineer. + + Message reception + 消息接收 + No comment provided by engineer. + + + Message servers + 消息服务器 + No comment provided by engineer. + + + Message shape + 消息形状 + No comment provided by engineer. + + + Message source remains private. + 消息来源保持私密。 + No comment provided by engineer. + + + Message status + 消息状态 + No comment provided by engineer. + + + Message status: %@ + 消息状态:%@ + copied message info + Message text 消息正文 @@ -3541,6 +4900,7 @@ This is your link for group %@! Message too large + 消息太大了 No comment provided by engineer. @@ -3555,38 +4915,67 @@ This is your link for group %@! Messages from %@ will be shown! + 将显示来自 %@ 的消息! No comment provided by engineer. + + Messages in this chat will never be deleted. + 此聊天中的消息永远不会被删除。 + alert message + + + Messages received + 收到的消息 + No comment provided by engineer. + + + Messages sent + 已发送的消息 + No comment provided by engineer. + + + Messages were deleted after you selected them. + 在你选中消息后这些消息已被删除。 + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + 消息、文件和通话受到 **端到端加密** 的保护,具有完全正向保密、否认和闯入恢复。 No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + 消息、文件和通话受到 **抗量子 e2e 加密** 的保护,具有完全正向保密、否认和闯入恢复。 No comment provided by engineer. Migrate device + 迁移设备 No comment provided by engineer. Migrate from another device + 从另一台设备迁移 No comment provided by engineer. Migrate here + 迁移到此处 No comment provided by engineer. Migrate to another device + 迁移到另一部设备 No comment provided by engineer. Migrate to another device via QR code. + 通过二维码迁移到另一部设备。 No comment provided by engineer. Migrating + 迁移中 No comment provided by engineer. @@ -3596,6 +4985,7 @@ This is your link for group %@! Migration complete + 迁移完毕 No comment provided by engineer. @@ -3613,9 +5003,9 @@ This is your link for group %@! 迁移完成 No comment provided by engineer. - - Migrations: %@ - 迁移:%@ + + Migrations: + 迁移 No comment provided by engineer. @@ -3633,21 +5023,31 @@ This is your link for group %@! 已被管理员移除于:%@ copied message info + + More + 更多 + swipe action + More improvements are coming soon! 更多改进即将推出! No comment provided by engineer. + + More reliable network connection. + 更可靠的网络连接。 + No comment provided by engineer. + + + More reliable notifications + 更可靠的通知 + No comment provided by engineer. + Most likely this connection is deleted. 此连接很可能已被删除。 item status description - - Most likely this contact has deleted the connection with you. - 很可能此联系人已经删除了与您的联系。 - No comment provided by engineer. - Multiple chat profiles 多个聊天资料 @@ -3656,7 +5056,12 @@ This is your link for group %@! Mute 静音 - No comment provided by engineer. + notification label action + + + Mute all + 全部静音 + notification label action Muted when inactive! @@ -3666,13 +5071,38 @@ This is your link for group %@! Name 名称 - No comment provided by engineer. + swipe action Network & servers 网络和服务器 No comment provided by engineer. + + Network connection + 网络连接 + No comment provided by engineer. + + + Network decentralization + 网络去中心化 + No comment provided by engineer. + + + Network issues - message expired after many attempts to send it. + 网络问题 - 消息在多次尝试发送后过期。 + snd error text + + + Network management + 网络管理 + No comment provided by engineer. + + + Network operator + 网络运营方 + No comment provided by engineer. + Network settings 网络设置 @@ -3683,16 +5113,36 @@ This is your link for group %@! 网络状态 No comment provided by engineer. + + New + + token status text + New Passcode 新密码 No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + 每次启动应用都会使用新的 SOCKS 凭据。 + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + 每个服务器都会使用新的 SOCKS 凭据。 + No comment provided by engineer. + New chat 新聊天 No comment provided by engineer. + + New chat experience 🎉 + 新的聊天体验 🎉 + No comment provided by engineer. + New contact request 新联系人请求 @@ -3703,11 +5153,6 @@ This is your link for group %@! 新联系人: notification - - New database archive - 新数据库存档 - No comment provided by engineer. - New desktop app! 全新桌面应用! @@ -3718,11 +5163,21 @@ This is your link for group %@! 新显示名 No comment provided by engineer. + + New events + 新事件 + notification + New in %@ %@ 的新内容 No comment provided by engineer. + + New media options + 新媒体选项 + No comment provided by engineer. + New member role 新成员角色 @@ -3738,6 +5193,11 @@ This is your link for group %@! 新密码…… No comment provided by engineer. + + New server + 新服务器 + No comment provided by engineer. + No @@ -3748,6 +5208,21 @@ This is your link for group %@! 没有应用程序密码 Authentication unavailable + + No chats + 无聊天 + No comment provided by engineer. + + + No chats found + 找不到聊天 + No comment provided by engineer. + + + No chats in list %@ + 列表 %@ 中无聊天 + No comment provided by engineer. + No contacts selected 未选择联系人 @@ -3768,6 +5243,11 @@ This is your link for group %@! 无设备令牌! No comment provided by engineer. + + No direct connection yet, message is forwarded by admin. + 还没有直接连接,消息由管理员转发。 + item status description + No filtered chats 无过滤聊天 @@ -3783,21 +5263,111 @@ This is your link for group %@! 无历史记录 No comment provided by engineer. + + No info, try to reload + 无信息,尝试重新加载 + No comment provided by engineer. + + + No media & file servers. + 无媒体和文件服务器。 + servers error + + + No message + 无消息 + No comment provided by engineer. + + + No message servers. + 无消息服务器。 + servers error + + + No network connection + 无网络连接 + No comment provided by engineer. + + + No permission to record speech + 无录音权限 + No comment provided by engineer. + + + No permission to record video + 无录像权限 + No comment provided by engineer. + No permission to record voice message 没有录制语音消息的权限 No comment provided by engineer. + + No push server + 本地 + No comment provided by engineer. + No received or sent files 未收到或发送文件 No comment provided by engineer. + + No servers for private message routing. + 无私密消息路由服务器。 + servers error + + + No servers to receive files. + 无文件接收服务器。 + servers error + + + No servers to receive messages. + 无消息接收服务器。 + servers error + + + No servers to send files. + 无文件发送服务器。 + servers error + + + No token! + 无 token! + alert title + + + No unread chats + 没有未读聊天 + No comment provided by engineer. + + + No user identifiers. + 没有用户标识符。 + No comment provided by engineer. + Not compatible! 不兼容! No comment provided by engineer. + + Notes + 附注 + No comment provided by engineer. + + + Nothing selected + 未选中任何内容 + No comment provided by engineer. + + + Nothing to forward! + 无可转发! + alert title + Notifications 通知 @@ -3808,6 +5378,21 @@ This is your link for group %@! 通知被禁用! No comment provided by engineer. + + Notifications error + 通知错误 + alert title + + + Notifications privacy + 通知隐私 + No comment provided by engineer. + + + Notifications status + 通知状态 + alert title + Now admins can: - delete members' messages. @@ -3825,36 +5410,35 @@ This is your link for group %@! Off 关闭 - No comment provided by engineer. + blur media Ok 好的 - No comment provided by engineer. + alert button Old database 旧的数据库 No comment provided by engineer. - - Old database archive - 旧数据库存档 - No comment provided by engineer. - One-time invitation link 一次性邀请链接 No comment provided by engineer. - - Onion hosts will be required for connection. Requires enabling VPN. - Onion 主机将用于连接。需要启用 VPN。 + + Onion hosts will be **required** for connection. +Requires compatible VPN. + Onion 主机将是连接所必需的。 +需要兼容的 VPN。 No comment provided by engineer. - - Onion hosts will be used when available. Requires enabling VPN. - 当可用时,将使用 Onion 主机。需要启用 VPN。 + + Onion hosts will be used when available. +Requires compatible VPN. + 如果可用,将使用洋葱主机。 +需要兼容的 VPN。 No comment provided by engineer. @@ -3862,11 +5446,21 @@ This is your link for group %@! 将不会使用 Onion 主机。 No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only chat owners can change preferences. + 仅聊天所有人可更改首选项。 + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages. 只有客户端设备存储用户资料、联系人、群组和**双层端到端加密**发送的消息。 No comment provided by engineer. + + Only delete conversation + 仅删除对话 + No comment provided by engineer. + Only group owners can change group preferences. 只有群主可以改变群组偏好设置。 @@ -3882,6 +5476,16 @@ This is your link for group %@! 只有群主可以启用语音信息。 No comment provided by engineer. + + Only sender and moderators see it + 仅发送人和moderators能看到 + No comment provided by engineer. + + + Only you and moderators see it + 只有你和moderators能看到 + No comment provided by engineer. + Only you can add message reactions. 只有您可以添加消息回应。 @@ -3889,7 +5493,7 @@ This is your link for group %@! Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours) - 只有您可以不可撤回地删除消息(您的联系人可以将它们标记为删除)。 + 只有您可以不可撤回地删除消息(您的联系人可以将它们标记为删除) No comment provided by engineer. @@ -3914,7 +5518,7 @@ This is your link for group %@! Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours) - 只有您的联系人才能不可撤回地删除消息(您可以将它们标记为删除)。 + 只有您的联系人才能不可撤回地删除消息(您可以将它们标记为删除) No comment provided by engineer. @@ -3935,13 +5539,18 @@ This is your link for group %@! Open 打开 - No comment provided by engineer. + alert action Open Settings 打开设置 No comment provided by engineer. + + Open changes + 打开更改 + No comment provided by engineer. + Open chat 打开聊天 @@ -3952,31 +5561,48 @@ This is your link for group %@! 打开聊天控制台 authentication reason + + Open conditions + 打开条款 + No comment provided by engineer. + Open group 打开群 No comment provided by engineer. + + Open link? + alert title + Open migration to another device + 打开迁移到另一台设备 authentication reason - - Open user profiles - 打开用户个人资料 - authentication reason - - - Open-source protocol and code – anybody can run the servers. - 开源协议和代码——任何人都可以运行服务器。 - No comment provided by engineer. - Opening app… + 正在打开应用程序… + No comment provided by engineer. + + + Operator + 运营方 + No comment provided by engineer. + + + Operator server + 运营方服务器 + alert title + + + Or import archive file + 或者导入或者导入压缩文件 No comment provided by engineer. Or paste archive link + 或粘贴存档链接 No comment provided by engineer. @@ -3986,6 +5612,7 @@ This is your link for group %@! Or securely share this file link + 或安全地分享此文件链接 No comment provided by engineer. @@ -3993,6 +5620,26 @@ This is your link for group %@! 或者显示此码 No comment provided by engineer. + + Or to share privately + 或者私下分享 + No comment provided by engineer. + + + Organize chats into lists + 将聊天组织到列表 + No comment provided by engineer. + + + Other + 其他 + No comment provided by engineer. + + + Other file errors: +%@ + alert message + PING count PING 次数 @@ -4028,6 +5675,11 @@ This is your link for group %@! 密码已设置! No comment provided by engineer. + + Password + 密码 + No comment provided by engineer. + Password to show 显示密码 @@ -4035,6 +5687,7 @@ This is your link for group %@! Past member %@ + 前任成员 %@ past/unknown group member @@ -4057,13 +5710,13 @@ This is your link for group %@! 粘贴您收到的链接 No comment provided by engineer. - - People can connect to you only via the links you share. - 人们只能通过您共享的链接与您建立联系。 + + Pending + 待定 No comment provided by engineer. - - Periodically + + Periodic 定期 No comment provided by engineer. @@ -4074,6 +5727,17 @@ This is your link for group %@! Picture-in-picture calls + 画中画通话 + No comment provided by engineer. + + + Play from the chat list. + 从聊天列表播放。 + No comment provided by engineer. + + + Please ask your contact to enable calls. + 请要求您的联系人开通通话功能。 No comment provided by engineer. @@ -4081,6 +5745,13 @@ This is your link for group %@! 请让您的联系人启用发送语音消息。 No comment provided by engineer. + + Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. +Please share any other issues with the developers. + 请检查移动设备和桌面是否连接到同一本地网络,以及桌面防火墙是否允许连接。 +请与开发人员分享任何其他问题。 + No comment provided by engineer. + Please check that you used the correct link or ask your contact to send you another one. 请检查您使用的链接是否正确,或者让您的联系人给您发送另一个链接。 @@ -4098,11 +5769,14 @@ This is your link for group %@! Please confirm that network settings are correct for this device. + 请确认网络设置对此这台设备正确无误。 No comment provided by engineer. Please contact developers. Error: %@ + 请联系开发人员。 +错误:%@ No comment provided by engineer. @@ -4145,60 +5819,114 @@ Error: %@ 请安全地保存密码,如果您丢失了密码,您将无法更改它。 No comment provided by engineer. + + Please try to disable and re-enable notfications. + token info + + + Please wait for token activation to complete. + token info + + + Please wait for token to be registered. + token info + Polish interface 波兰语界面 No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect 服务器地址中的证书指纹可能不正确 server test error - - Post-quantum E2EE - No comment provided by engineer. - Preserve the last message draft, with attachments. 保留最后的消息草稿及其附件。 No comment provided by engineer. - - Preset server - 预设服务器 - No comment provided by engineer. - Preset server address 预设服务器地址 No comment provided by engineer. + + Preset servers + No comment provided by engineer. + Preview 预览 No comment provided by engineer. + + Previously connected servers + 以前连接的服务器 + No comment provided by engineer. + Privacy & security 隐私和安全 No comment provided by engineer. + + Privacy for your customers. + No comment provided by engineer. + + + Privacy policy and conditions of use. + 隐私政策和使用条款。 + No comment provided by engineer. + Privacy redefined 重新定义隐私 No comment provided by engineer. + + Private chats, groups and your contacts are not accessible to server operators. + 服务器运营方无法访问私密聊天、群组和你的联系人。 + No comment provided by engineer. + Private filenames 私密文件名 No comment provided by engineer. + + Private media file names. + No comment provided by engineer. + + + Private message routing + 私有消息路由 + No comment provided by engineer. + + + Private message routing 🚀 + 私有消息路由 🚀 + No comment provided by engineer. + Private notes 私密笔记 name of notes to self + + Private routing + 专用路由 + No comment provided by engineer. + + + Private routing error + 专用路由错误 + No comment provided by engineer. + Profile and server connections 资料和服务器连接 @@ -4209,13 +5937,9 @@ Error: %@ 资料图片 No comment provided by engineer. - - Profile name - No comment provided by engineer. - - - Profile name: - 显示名: + + Profile images + 个人资料图 No comment provided by engineer. @@ -4223,10 +5947,15 @@ Error: %@ 个人资料密码 No comment provided by engineer. + + Profile theme + 个人资料主题 + No comment provided by engineer. + Profile update will be sent to your contacts. 个人资料更新将被发送给您的联系人。 - No comment provided by engineer. + alert message Prohibit audio/video calls. @@ -4248,6 +5977,15 @@ Error: %@ 禁止消息回应。 No comment provided by engineer. + + Prohibit reporting messages to moderators. + No comment provided by engineer. + + + Prohibit sending SimpleX links. + 禁止发送 SimpleX 链接。 + No comment provided by engineer. + Prohibit sending direct messages to members. 禁止向成员发送私信。 @@ -4268,11 +6006,23 @@ Error: %@ 禁止发送语音消息。 No comment provided by engineer. + + Protect IP address + 保护 IP 地址 + No comment provided by engineer. + Protect app screen 保护应用程序屏幕 No comment provided by engineer. + + Protect your IP address from the messaging relays chosen by your contacts. +Enable in *Network & servers* settings. + 保护您的 IP 地址免受联系人选择的消息中继的攻击。 +在*网络和服务器*设置中启用。 + No comment provided by engineer. + Protect your chat profiles with a password! 使用密码保护您的聊天资料! @@ -4288,6 +6038,20 @@ Error: %@ 每 KB 协议超时 No comment provided by engineer. + + Proxied + 代理 + No comment provided by engineer. + + + Proxied servers + 代理服务器 + No comment provided by engineer. + + + Proxy requires password + No comment provided by engineer. + Push notifications 推送通知 @@ -4295,10 +6059,12 @@ Error: %@ Push server + 推送服务器 No comment provided by engineer. Quantum resistant encryption + 抗量子加密 No comment provided by engineer. @@ -4306,6 +6072,11 @@ Error: %@ 评价此应用程序 No comment provided by engineer. + + Reachable chat toolbar + 可访问的聊天工具栏 + No comment provided by engineer. + React… 回应… @@ -4314,33 +6085,28 @@ Error: %@ Read 已读 - No comment provided by engineer. + swipe action Read more 阅读更多 No comment provided by engineer. - - Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address). - 在 [用户指南](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address) 中阅读更多内容。 - No comment provided by engineer. - Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode). 阅读更多[User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)。 No comment provided by engineer. + + Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses). + 在 [用户指南](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses) 中阅读更多内容。 + No comment provided by engineer. + Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends). 在 [用户指南](https://simplex.chat/docs/guide/readme.html#connect-to-friends) 中阅读更多内容。 No comment provided by engineer. - - Read more in our GitHub repository. - 在我们的 GitHub 仓库中阅读更多内容。 - No comment provided by engineer. - Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). 在我们的 [GitHub 仓库](https://github.com/simplex-chat/simplex-chat#readme) 中阅读更多信息。 @@ -4351,6 +6117,11 @@ Error: %@ 回执已禁用 No comment provided by engineer. + + Receive errors + 接收错误 + No comment provided by engineer. + Received at 已收到于 @@ -4371,6 +6142,21 @@ Error: %@ 收到的信息 message info title + + Received messages + 收到的消息 + No comment provided by engineer. + + + Received reply + 已收到回复 + No comment provided by engineer. + + + Received total + 接收总数 + No comment provided by engineer. + Receiving address will be changed to a different server. Address change will complete after sender comes online. 接收地址将变更到不同的服务器。地址更改将在发件人上线后完成。 @@ -4388,6 +6174,12 @@ Error: %@ Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). + 最近的历史记录和改进的 [目录机器人](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). + No comment provided by engineer. + + + Recipient(s) can't see who this message is from. + 收件人看不到这条消息来自何人。 No comment provided by engineer. @@ -4395,11 +6187,36 @@ Error: %@ 对方会在您键入时看到更新。 No comment provided by engineer. + + Reconnect + 重新连接 + No comment provided by engineer. + Reconnect all connected servers to force message delivery. It uses additional traffic. 重新连接所有已连接的服务器以强制发送信息。这会耗费更多流量。 No comment provided by engineer. + + Reconnect all servers + 重新连接所有服务器 + No comment provided by engineer. + + + Reconnect all servers? + 重新连接所有服务器? + No comment provided by engineer. + + + Reconnect server to force message delivery. It uses additional traffic. + 重新连接服务器以强制发送信息。它使用额外的流量。 + No comment provided by engineer. + + + Reconnect server? + 重新连接服务器? + No comment provided by engineer. + Reconnect servers? 是否重新连接服务器? @@ -4420,10 +6237,23 @@ Error: %@ 减少电池使用量 No comment provided by engineer. + + Register + No comment provided by engineer. + + + Register notification token? + token info + + + Registered + token status text + Reject 拒绝 - reject incoming call via notification + reject incoming call via notification +swipe action Reject (sender NOT notified) @@ -4450,6 +6280,15 @@ Error: %@ 移除 No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + + + Remove image + 移除图片 + No comment provided by engineer. + Remove member 删除成员 @@ -4487,10 +6326,12 @@ Error: %@ Repeat download + 重复下载 No comment provided by engineer. Repeat import + 重复导入 No comment provided by engineer. @@ -4500,6 +6341,7 @@ Error: %@ Repeat upload + 重复上传 No comment provided by engineer. @@ -4507,6 +6349,46 @@ Error: %@ 回复 chat item action + + Report + chat item action + + + Report content: only group moderators will see it. + report reason + + + Report member profile: only group moderators will see it. + report reason + + + Report other: only group moderators will see it. + report reason + + + Report reason? + No comment provided by engineer. + + + Report spam: only group moderators will see it. + report reason + + + Report violation: only group moderators will see it. + report reason + + + Report: %@ + report in notification + + + Reporting messages to moderators is prohibited. + No comment provided by engineer. + + + Reports + No comment provided by engineer. + Required 必须 @@ -4517,16 +6399,41 @@ Error: %@ 重置 No comment provided by engineer. + + Reset all hints + 重置所有提示 + No comment provided by engineer. + + + Reset all statistics + 重置所有统计信息 + No comment provided by engineer. + + + Reset all statistics? + 重置所有统计信息? + No comment provided by engineer. + Reset colors 重置颜色 No comment provided by engineer. + + Reset to app theme + 重置为应用程序主题 + No comment provided by engineer. + Reset to defaults 重置为默认 No comment provided by engineer. + + Reset to user theme + 重置为用户主题 + No comment provided by engineer. + Restart the app to create a new chat profile 重新启动应用程序以创建新的聊天资料 @@ -4567,24 +6474,24 @@ Error: %@ 揭示 chat item action - - Revert - 恢复 + + Review conditions + 审阅条款 No comment provided by engineer. Revoke - 撤销 + 吊销 No comment provided by engineer. Revoke file - 撤销文件 + 吊销文件 cancel file action Revoke file? - 撤销文件? + 吊销文件? No comment provided by engineer. @@ -4594,58 +6501,69 @@ Error: %@ Run chat - 运行聊天程序 + 运行聊天 No comment provided by engineer. - - SMP servers + + SMP server SMP 服务器 No comment provided by engineer. + + SOCKS proxy + No comment provided by engineer. + + + Safely receive files + 安全接收文件 + No comment provided by engineer. + Safer groups + 更安全的群组 No comment provided by engineer. Save 保存 - chat item action + alert button +chat item action Save (and notify contacts) 保存(并通知联系人) - No comment provided by engineer. + alert button Save and notify contact 保存并通知联系人 - No comment provided by engineer. + alert button Save and notify group members 保存并通知群组成员 No comment provided by engineer. + + Save and reconnect + 保存并重新连接 + No comment provided by engineer. + Save and update group profile 保存和更新组配置文件 No comment provided by engineer. - - Save archive - 保存存档 - No comment provided by engineer. - - - Save auto-accept settings - 保存自动接受设置 - No comment provided by engineer. - Save group profile 保存群组资料 No comment provided by engineer. + + Save list + 保存列表 + No comment provided by engineer. + Save passphrase and open chat 保存密码并打开聊天 @@ -4659,7 +6577,7 @@ Error: %@ Save preferences? 保存偏好设置? - No comment provided by engineer. + alert title Save profile password @@ -4674,28 +6592,53 @@ Error: %@ Save servers? 保存服务器? - No comment provided by engineer. - - - Save settings? - 保存设置? - No comment provided by engineer. + alert title Save welcome message? 保存欢迎信息? No comment provided by engineer. + + Save your profile? + 保存您的个人资料? + alert title + + + Saved + 已保存 + No comment provided by engineer. + Saved WebRTC ICE servers will be removed 已保存的WebRTC ICE服务器将被删除 No comment provided by engineer. + + Saved from + 保存自 + No comment provided by engineer. + Saved message 已保存的消息 message info title + + Saving %lld messages + 正在保存 %lld 条消息 + No comment provided by engineer. + + + Scale + 规模 + No comment provided by engineer. + + + Scan / Paste link + 扫描 / 粘贴链接 + No comment provided by engineer. + Scan QR code 扫描二维码 @@ -4736,11 +6679,21 @@ Error: %@ 搜索或粘贴 SimpleX 链接 No comment provided by engineer. + + Secondary + 二级 + No comment provided by engineer. + Secure queue 保护队列 server test step + + Secured + 担保 + No comment provided by engineer. + Security assessment 安全评估 @@ -4754,6 +6707,20 @@ Error: %@ Select 选择 + chat item action + + + Select chat profile + No comment provided by engineer. + + + Selected %lld + 选定的 %lld + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + 选定的聊天首选项禁止此消息。 No comment provided by engineer. @@ -4791,11 +6758,6 @@ Error: %@ 将送达回执发送给 No comment provided by engineer. - - Send direct message - 发送私信 - No comment provided by engineer. - Send direct message to connect 发送私信来连接 @@ -4806,6 +6768,11 @@ Error: %@ 发送限时消息中 No comment provided by engineer. + + Send errors + 发送错误 + No comment provided by engineer. + Send link previews 发送链接预览 @@ -4816,14 +6783,28 @@ Error: %@ 发送实时消息 No comment provided by engineer. + + Send message to enable calls. + 发送消息以启用呼叫。 + No comment provided by engineer. + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + 当 IP 地址受到保护并且您或目标服务器不支持私有路由时,直接发送消息。 + No comment provided by engineer. + + + Send messages directly when your or destination server does not support private routing. + 当您或目标服务器不支持私有路由时,直接发送消息。 + No comment provided by engineer. + Send notifications 发送通知 No comment provided by engineer. - - Send notifications: - 发送通知: + + Send private reports No comment provided by engineer. @@ -4849,7 +6830,7 @@ Error: %@ Sender cancelled file transfer. 发送人已取消文件传输。 - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -4906,6 +6887,11 @@ Error: %@ 已发送于:%@ copied message info + + Sent directly + 直接发送 + No comment provided by engineer. + Sent file event 已发送文件项目 @@ -4916,11 +6902,66 @@ Error: %@ 已发信息 message info title + + Sent messages + 已发送的消息 + No comment provided by engineer. + Sent messages will be deleted after set time. 已发送的消息将在设定的时间后被删除。 No comment provided by engineer. + + Sent reply + 已发送回复 + No comment provided by engineer. + + + Sent total + 发送总数 + No comment provided by engineer. + + + Sent via proxy + 通过代理发送 + No comment provided by engineer. + + + Server + No comment provided by engineer. + + + Server added to operator %@. + alert message + + + Server address + 服务器地址 + No comment provided by engineer. + + + Server address is incompatible with network settings. + 服务器地址与网络设置不兼容。 + srv error text. + + + Server address is incompatible with network settings: %@. + 服务器地址与网络设置不兼容:%@。 + No comment provided by engineer. + + + Server operator changed. + alert title + + + Server operators + No comment provided by engineer. + + + Server protocol changed. + alert title + Server requires authorization to create queues, check password 服务器需要授权才能创建队列,检查密码 @@ -4936,11 +6977,36 @@ Error: %@ 服务器测试失败! No comment provided by engineer. + + Server type + 服务器类型 + No comment provided by engineer. + + + Server version is incompatible with network settings. + 服务器版本与网络设置不兼容。 + srv error text + + + Server version is incompatible with your app: %@. + 服务器版本与你的应用程序不兼容:%@。 + No comment provided by engineer. + Servers 服务器 No comment provided by engineer. + + Servers info + 服务器信息 + No comment provided by engineer. + + + Servers statistics will be reset - this cannot be undone! + 服务器统计信息将被重置 - 此操作无法撤消! + No comment provided by engineer. + Session code 会话码 @@ -4951,11 +7017,20 @@ Error: %@ 设定1天 No comment provided by engineer. + + Set chat name… + No comment provided by engineer. + Set contact name… 设置联系人姓名…… No comment provided by engineer. + + Set default theme + 设置默认主题 + No comment provided by engineer. + Set group preferences 设置群组偏好设置 @@ -4966,6 +7041,10 @@ Error: %@ 设置它以代替系统身份验证。 No comment provided by engineer. + + Set message expiration in chats. + No comment provided by engineer. + Set passcode 设置密码 @@ -4973,6 +7052,7 @@ Error: %@ Set passphrase + 设置密码短语 No comment provided by engineer. @@ -4995,24 +7075,51 @@ Error: %@ 设置 No comment provided by engineer. + + Settings were changed. + alert message + + + Shape profile images + 改变个人资料图形状 + No comment provided by engineer. + Share 分享 - chat item action + alert action +chat item action Share 1-time link 分享一次性链接 No comment provided by engineer. + + Share 1-time link with a friend + No comment provided by engineer. + + + Share SimpleX address on social media. + No comment provided by engineer. + Share address 分享地址 No comment provided by engineer. + + Share address publicly + No comment provided by engineer. + Share address with contacts? 与联系人分享地址? + alert title + + + Share from other apps. + 从其他应用程序共享。 No comment provided by engineer. @@ -5020,18 +7127,32 @@ Error: %@ 分享链接 No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link 分享此一次性邀请链接 No comment provided by engineer. + + Share to SimpleX + 分享到 SimpleX + No comment provided by engineer. + Share with contacts 与联系人分享 No comment provided by engineer. + + Short link + No comment provided by engineer. + Show QR code + 显示二维码 No comment provided by engineer. @@ -5049,21 +7170,46 @@ Error: %@ 显示最近的消息 No comment provided by engineer. + + Show message status + 显示消息状态 + No comment provided by engineer. + + + Show percentage + 显示百分比 + No comment provided by engineer. + Show preview 显示预览 No comment provided by engineer. + + Show → on messages sent via private routing. + 显示 → 通过专用路由发送的信息. + No comment provided by engineer. + Show: 显示: No comment provided by engineer. + + SimpleX + SimpleX + No comment provided by engineer. + SimpleX Address SimpleX 地址 No comment provided by engineer. + + SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app. + SimpleX Chat 与 Flux 达成了协议,将由 Flux 控制的服务器纳入 SimpleX 应用。 + No comment provided by engineer. + SimpleX Chat security was audited by Trail of Bits. SimpleX Chat 的安全性 由 Trail of Bits 审核。 @@ -5094,6 +7240,21 @@ Error: %@ SimpleX 地址 No comment provided by engineer. + + SimpleX address and 1-time links are safe to share via any messenger. + 可以通过任何消息应用安全分享 SimpleX 地址和一次性链接。 + No comment provided by engineer. + + + SimpleX address or 1-time link? + SimpleX 地址或一次性链接? + No comment provided by engineer. + + + SimpleX channel link + SimpleX 频道链接 + simplex link type + SimpleX contact address SimpleX 联系地址 @@ -5101,17 +7262,27 @@ Error: %@ SimpleX encrypted message or connection event - SimpleX 加密消息或连接项目 + SimpleX 加密的消息或连接事件 notification SimpleX group link - SimpleX 群组链接 + SimpleX 群链接 simplex link type SimpleX links SimpleX 链接 + chat feature + + + SimpleX links are prohibited. + 此群禁止 SimpleX 链接。 + No comment provided by engineer. + + + SimpleX links not allowed + 不允许SimpleX 链接 No comment provided by engineer. @@ -5119,11 +7290,21 @@ Error: %@ SimpleX 一次性邀请 simplex link type + + SimpleX protocols reviewed by Trail of Bits. + SimpleX 协议由 Trail of Bits 审阅。 + No comment provided by engineer. + Simplified incognito mode 简化的隐身模式 No comment provided by engineer. + + Size + 大小 + No comment provided by engineer. + Skip 跳过 @@ -5139,16 +7320,51 @@ Error: %@ 小群组(最多 20 人) No comment provided by engineer. + + Soft + + blur media + + + Some app settings were not migrated. + 部分应用设置未被迁移。 + No comment provided by engineer. + + + Some file(s) were not exported: + 某些文件未导出: + No comment provided by engineer. + Some non-fatal errors occurred during import - you may see Chat console for more details. 导入过程中发生了一些非致命错误——您可以查看聊天控制台了解更多详细信息。 No comment provided by engineer. + + Some non-fatal errors occurred during import: + 导入过程中出现一些非致命错误: + No comment provided by engineer. + + + Some servers failed the test: +%@ + alert message + Somebody 某人 notification title + + Spam + blocking reason +report reason + + + Square, circle, or anything in between. + 方形、圆形、或两者之间的任意形状. + No comment provided by engineer. + Start chat 开始聊天 @@ -5164,6 +7380,16 @@ Error: %@ 开始迁移 No comment provided by engineer. + + Starting from %@. + 从 %@ 开始。 + No comment provided by engineer. + + + Statistics + 统计 + No comment provided by engineer. + Stop 停止 @@ -5176,11 +7402,7 @@ Error: %@ Stop chat - No comment provided by engineer. - - - Stop chat to enable database actions - 停止聊天以启用数据库操作 + 停止聊天程序 No comment provided by engineer. @@ -5211,27 +7433,60 @@ Error: %@ Stop sharing 停止分享 - No comment provided by engineer. + alert action Stop sharing address? 停止分享地址? - No comment provided by engineer. + alert title Stopping chat + 正在停止聊天 No comment provided by engineer. + + Storage + No comment provided by engineer. + + + Strong + 加粗 + blur media + Submit 提交 No comment provided by engineer. + + Subscribed + 已订阅 + No comment provided by engineer. + + + Subscription errors + 订阅错误 + No comment provided by engineer. + + + Subscriptions ignored + 忽略订阅 + No comment provided by engineer. + Support SimpleX Chat 支持 SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System 系统 @@ -5242,11 +7497,20 @@ Error: %@ 系统验证 No comment provided by engineer. + + TCP connection + TCP 连接 + No comment provided by engineer. + TCP connection timeout TCP 连接超时 No comment provided by engineer. + + TCP port for messaging + No comment provided by engineer. + TCP_KEEPCNT TCP_KEEPCNT @@ -5262,11 +7526,19 @@ Error: %@ TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture 拍照 No comment provided by engineer. + + Tap Create SimpleX address in the menu to create it later. + No comment provided by engineer. + Tap button 点击按钮 @@ -5302,16 +7574,20 @@ Error: %@ 轻按扫描 No comment provided by engineer. - - Tap to start a new chat - 点击开始一个新聊天 - No comment provided by engineer. + + Temporary file error + 临时文件错误 + file error alert title Test failed at step %@. 在步骤 %@ 上测试失败。 server test failure + + Test notifications + No comment provided by engineer. + Test server 测试服务器 @@ -5325,7 +7601,7 @@ Error: %@ Tests failed! 测试失败! - No comment provided by engineer. + alert title Thank you for installing SimpleX Chat! @@ -5342,11 +7618,6 @@ Error: %@ 感谢用户——通过 Weblate 做出贡献! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. - 第一个没有任何用户标识符的平台 - 隐私设计. - No comment provided by engineer. - The ID of the next message is incorrect (less or equal to the previous). It can happen because of some bug or when the connection is compromised. @@ -5359,6 +7630,15 @@ It can happen because of some bug or when the connection is compromised.该应用可以在您收到消息或联系人请求时通知您——请打开设置以启用通知。 No comment provided by engineer. + + The app protects your privacy by using different operators in each conversation. + No comment provided by engineer. + + + The app will ask to confirm downloads from unknown file servers (except .onion). + 该应用程序将要求确认从未知文件服务器(.onion 除外)下载。 + No comment provided by engineer. + The attempt to change database passphrase was not completed. 更改数据库密码的尝试未完成。 @@ -5369,6 +7649,10 @@ It can happen because of some bug or when the connection is compromised.您扫描的码不是 SimpleX 链接的二维码。 No comment provided by engineer. + + The connection reached the limit of undelivered messages, your contact may be offline. + No comment provided by engineer. + The connection you accepted will be cancelled! 您接受的连接将被取消! @@ -5389,6 +7673,11 @@ It can happen because of some bug or when the connection is compromised.加密正在运行,不需要新的加密协议。这可能会导致连接错误! No comment provided by engineer. + + The future of messaging + 下一代私密通讯软件 + No comment provided by engineer. + The hash of the previous message is different. 上一条消息的散列不同。 @@ -5404,9 +7693,14 @@ It can happen because of some bug or when the connection is compromised.该消息将对所有成员标记为已被管理员移除。 No comment provided by engineer. - - The next generation of private messaging - 下一代私密通讯软件 + + The messages will be deleted for all members. + 将删除所有成员的消息。 + No comment provided by engineer. + + + The messages will be marked as moderated for all members. + 对于所有成员,这些消息将被标记为已审核。 No comment provided by engineer. @@ -5414,9 +7708,12 @@ It can happen because of some bug or when the connection is compromised.旧数据库在迁移过程中没有被移除,可以删除。 No comment provided by engineer. - - The profile is only shared with your contacts. - 该资料仅与您的联系人共享。 + + The same conditions will apply to operator **%@**. + No comment provided by engineer. + + + The second preset operator in the app! No comment provided by engineer. @@ -5434,16 +7731,28 @@ It can happen because of some bug or when the connection is compromised.您当前聊天资料 **%@** 的新连接服务器。 No comment provided by engineer. + + The servers for new files of your current chat profile **%@**. + No comment provided by engineer. + The text you pasted is not a SimpleX link. 您粘贴的文本不是 SimpleX 链接。 No comment provided by engineer. - - Theme + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + + + Themes 主题 No comment provided by engineer. + + These conditions will also apply for: **%@**. + No comment provided by engineer. + These settings are for your current profile **%@**. 这些设置适用于您当前的配置文件 **%@**。 @@ -5464,6 +7773,10 @@ It can happen because of some bug or when the connection is compromised.此操作无法撤消——早于所选的发送和接收的消息将被删除。 这可能需要几分钟时间。 No comment provided by engineer. + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + alert message + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. 此操作无法撤消——您的个人资料、联系人、消息和文件将不可撤回地丢失。 @@ -5471,10 +7784,12 @@ It can happen because of some bug or when the connection is compromised. This chat is protected by end-to-end encryption. + 此聊天受端到端加密保护。 E2EE info chat item This chat is protected by quantum resistant end-to-end encryption. + 此聊天受抗量子的端到端加密保护。 E2EE info chat item @@ -5507,11 +7822,29 @@ It can happen because of some bug or when the connection is compromised.这是你自己的一次性链接! No comment provided by engineer. + + This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + No comment provided by engineer. + + + This link was used with another mobile device, please create a new link on the desktop. + 此链接已在其他移动设备上使用,请在桌面上创建新链接。 + No comment provided by engineer. + + + This message was deleted or not received yet. + No comment provided by engineer. + This setting applies to messages in your current chat profile **%@**. 此设置适用于您当前聊天资料 **%@** 中的消息。 No comment provided by engineer. + + Title + 标题 + No comment provided by engineer. + To ask any questions and to receive updates: 要提出任何问题并接收更新,请: @@ -5532,9 +7865,8 @@ It can happen because of some bug or when the connection is compromised.建立新连接 No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - 为了保护隐私,SimpleX使用针对消息队列的标识符,而不是所有其他平台使用的用户ID,每个联系人都有独立的标识符。 + + To protect against your link being replaced, you can compare contact security codes. No comment provided by engineer. @@ -5542,6 +7874,11 @@ It can happen because of some bug or when the connection is compromised.为了保护时区,图像/语音文件使用 UTC。 No comment provided by engineer. + + To protect your IP address, private routing uses your SMP servers to deliver messages. + 为了保护您的 IP 地址,私有路由使用您的 SMP 服务器来传递邮件。 + No comment provided by engineer. + To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. @@ -5549,6 +7886,23 @@ You will be prompted to complete authentication before this feature is enabled.< 在启用此功能之前,系统将提示您完成身份验证。 No comment provided by engineer. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. + 为了保护隐私,SimpleX使用针对消息队列的标识符,而不是所有其他平台使用的用户ID,每个联系人都有独立的标识符。 + No comment provided by engineer. + + + To receive + No comment provided by engineer. + + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. 请授权使用麦克风以录制语音消息。 @@ -5559,26 +7913,58 @@ You will be prompted to complete authentication before this feature is enabled.< 要显示您的隐藏的个人资料,请在**您的聊天个人资料**页面的搜索字段中输入完整密码。 No comment provided by engineer. + + To send + No comment provided by engineer. + To support instant push notifications the chat database has to be migrated. 为了支持即时推送通知,聊天数据库必须被迁移。 No comment provided by engineer. + + To use the servers of **%@**, accept conditions of use. + No comment provided by engineer. + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. 要与您的联系人验证端到端加密,请比较(或扫描)您设备上的代码。 No comment provided by engineer. + + Toggle chat list: + 切换聊天列表: + No comment provided by engineer. + Toggle incognito when connecting. 在连接时切换隐身模式。 No comment provided by engineer. + + Token status: %@. + token status + + + Toolbar opacity + 工具栏不透明度 + No comment provided by engineer. + + + Total + 共计 + No comment provided by engineer. + Transport isolation 传输隔离 No comment provided by engineer. + + Transport sessions + 传输会话 + No comment provided by engineer. + Trying to connect to the server used to receive messages from this contact (error: %@). 正在尝试连接到用于从该联系人接收消息的服务器(错误:%@)。 @@ -5591,6 +7977,7 @@ You will be prompted to complete authentication before this feature is enabled.< Turkish interface + 土耳其语界面 No comment provided by engineer. @@ -5633,10 +8020,9 @@ You will be prompted to complete authentication before this feature is enabled.< 解封成员吗? No comment provided by engineer. - - Unexpected error: %@ - 意外错误: %@ - item status description + + Undelivered messages + No comment provided by engineer. Unexpected migration state @@ -5646,7 +8032,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unfav. 取消最喜欢 - No comment provided by engineer. + swipe action Unhide @@ -5683,6 +8069,11 @@ You will be prompted to complete authentication before this feature is enabled.< 未知错误 No comment provided by engineer. + + Unknown servers! + 未知服务器! + alert title + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. 除非您使用 iOS 通话界面,否则请启用请勿打扰模式以避免打扰。 @@ -5718,11 +8109,15 @@ To connect, please ask your contact to create another connection link and check Unmute 取消静音 - No comment provided by engineer. + notification label action Unread 未读 + swipe action + + + Unsupported connection link No comment provided by engineer. @@ -5735,11 +8130,6 @@ To connect, please ask your contact to create another connection link and check 更新 No comment provided by engineer. - - Update .onion hosts setting? - 更新 .onion 主机设置? - No comment provided by engineer. - Update database passphrase 更新数据库密码 @@ -5750,9 +8140,13 @@ To connect, please ask your contact to create another connection link and check 更新网络设置? No comment provided by engineer. - - Update transport isolation mode? - 更新传输隔离模式? + + Update settings? + 更新设置? + No comment provided by engineer. + + + Updated conditions No comment provided by engineer. @@ -5760,18 +8154,19 @@ To connect, please ask your contact to create another connection link and check 更新设置会将客户端重新连接到所有服务器。 No comment provided by engineer. - - Updating this setting will re-connect the client to all servers. - 更新此设置将重新连接客户端到所有服务器。 - No comment provided by engineer. - Upgrade and open chat 升级并打开聊天 No comment provided by engineer. + + Upload errors + 上传错误 + No comment provided by engineer. + Upload failed + 上传失败了 No comment provided by engineer. @@ -5779,8 +8174,23 @@ To connect, please ask your contact to create another connection link and check 上传文件 server test step + + Uploaded + 已上传 + No comment provided by engineer. + + + Uploaded files + 已上传的文件 + No comment provided by engineer. + Uploading archive + 正在上传存档 + No comment provided by engineer. + + + Use %@ No comment provided by engineer. @@ -5788,11 +8198,23 @@ To connect, please ask your contact to create another connection link and check 使用 .onion 主机 No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? 使用 SimpleX Chat 服务器? No comment provided by engineer. + + Use TCP port %@ when no port is specified. + No comment provided by engineer. + + + Use TCP port 443 for preset servers only. + No comment provided by engineer. + Use chat 使用聊天 @@ -5803,6 +8225,14 @@ To connect, please ask your contact to create another connection link and check 使用当前配置文件 No comment provided by engineer. + + Use for files + No comment provided by engineer. + + + Use for messages + No comment provided by engineer. + Use for new connections 用于新连接 @@ -5825,6 +8255,17 @@ To connect, please ask your contact to create another connection link and check Use only local notifications? + 仅使用本地通知? + No comment provided by engineer. + + + Use private routing with unknown servers when IP address is not protected. + 当 IP 地址不受保护时,对未知服务器使用私有路由。 + No comment provided by engineer. + + + Use private routing with unknown servers. + 对未知服务器使用私有路由。 No comment provided by engineer. @@ -5832,18 +8273,35 @@ To connect, please ask your contact to create another connection link and check 使用服务器 No comment provided by engineer. + + Use servers + No comment provided by engineer. + + + Use short links (BETA) + No comment provided by engineer. + Use the app while in the call. + 通话时使用本应用. No comment provided by engineer. - - User profile - 用户资料 + + Use the app with one hand. + 用一只手使用应用程序。 No comment provided by engineer. - - Using .onion hosts requires compatible VPN provider. - 使用 .onion 主机需要兼容的 VPN 提供商。 + + Use web port + No comment provided by engineer. + + + User selection + 用户选择 + No comment provided by engineer. + + + Username No comment provided by engineer. @@ -5873,10 +8331,12 @@ To connect, please ask your contact to create another connection link and check Verify database passphrase + 验证数据库密码短语 No comment provided by engineer. Verify passphrase + 验证密码短语 No comment provided by engineer. @@ -5914,11 +8374,19 @@ To connect, please ask your contact to create another connection link and check 最大 1gb 的视频和文件 No comment provided by engineer. + + View conditions + No comment provided by engineer. + View security code 查看安全码 No comment provided by engineer. + + View updated conditions + No comment provided by engineer. + Visible history 可见的历史 @@ -5934,11 +8402,16 @@ To connect, please ask your contact to create another connection link and check 语音信息在此聊天中被禁止。 No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. 语音信息在该群组中被禁用。 No comment provided by engineer. + + Voice messages not allowed + 不允许语音消息 + No comment provided by engineer. + Voice messages prohibited! 语音消息禁止发送! @@ -5951,6 +8424,7 @@ To connect, please ask your contact to create another connection link and check Waiting for desktop... + 正在等待桌面... No comment provided by engineer. @@ -5968,8 +8442,19 @@ To connect, please ask your contact to create another connection link and check 等待视频中 No comment provided by engineer. + + Wallpaper accent + 壁纸装饰 + No comment provided by engineer. + + + Wallpaper background + 壁纸背景 + No comment provided by engineer. + Warning: starting chat on multiple devices is not supported and will cause message delivery failures + 警告:不支持在多部设备上启动聊天,这么做会导致消息传送失败 No comment provided by engineer. @@ -5994,6 +8479,7 @@ To connect, please ask your contact to create another connection link and check Welcome message is too long + 欢迎消息太大了 No comment provided by engineer. @@ -6006,9 +8492,13 @@ To connect, please ask your contact to create another connection link and check 当可用时 No comment provided by engineer. - - When people request to connect, you can accept or reject it. - 当人们请求连接时,您可以接受或拒绝它。 + + When connecting audio and video calls. + 连接音频和视频通话时。 + No comment provided by engineer. + + + When more than one operator is enabled, none of them has metadata to learn who communicates with whom. No comment provided by engineer. @@ -6016,6 +8506,21 @@ To connect, please ask your contact to create another connection link and check 当您与某人共享隐身聊天资料时,该资料将用于他们邀请您加入的群组。 No comment provided by engineer. + + WiFi + WiFi + No comment provided by engineer. + + + Will be enabled in direct chats! + 将在私聊中启用! + No comment provided by engineer. + + + Wired ethernet + 有线以太网 + No comment provided by engineer. + With encrypted files and media. 加密的文件和媒体。 @@ -6031,28 +8536,44 @@ To connect, please ask your contact to create another connection link and check 降低了电量使用。 No comment provided by engineer. + + Without Tor or VPN, your IP address will be visible to file servers. + 如果没有 Tor 或 VPN,您的 IP 地址将对文件服务器可见。 + No comment provided by engineer. + + + Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. + 如果没有 Tor 或 VPN,您的 IP 地址将对以下 XFTP 中继可见:%@。 + alert message + Wrong database passphrase 数据库密码错误 No comment provided by engineer. + + Wrong key or unknown connection - most likely this connection is deleted. + 密钥错误或连接未知 - 很可能此连接已被删除。 + snd error text + + + Wrong key or unknown file chunk address - most likely file is deleted. + 密钥错误或文件块地址未知 - 很可能文件已删除。 + file error text + Wrong passphrase! 密码错误! No comment provided by engineer. - - XFTP servers + + XFTP server XFTP 服务器 No comment provided by engineer. - - You - - No comment provided by engineer. - You **must not** use the same database on two devices. + 您 **不得** 在两台设备上使用相同的数据库。 No comment provided by engineer. @@ -6075,8 +8596,13 @@ To connect, please ask your contact to create another connection link and check 您已经连接到 %@。 No comment provided by engineer. + + You are already connected with %@. + No comment provided by engineer. + You are already connecting to %@. + 您已连接到 %@。 No comment provided by engineer. @@ -6086,14 +8612,17 @@ To connect, please ask your contact to create another connection link and check You are already in group %@. + 您已在组 %@ 中。 No comment provided by engineer. You are already joining the group %@. + 您已加入组 %@。 No comment provided by engineer. You are already joining the group via this link! + 您已经通过此链接加入群组! No comment provided by engineer. @@ -6104,6 +8633,8 @@ To connect, please ask your contact to create another connection link and check You are already joining the group! Repeat join request? + 您已经加入了这个群组! +重复加入请求? No comment provided by engineer. @@ -6116,11 +8647,25 @@ Repeat join request? 您被邀请加入群组 No comment provided by engineer. + + You are not connected to these servers. Private routing is used to deliver messages to them. + 您未连接到这些服务器。私有路由用于向他们发送消息。 + No comment provided by engineer. + You can accept calls from lock screen, without device and app authentication. 您可以从锁屏上接听电话,无需设备和应用程序的认证。 No comment provided by engineer. + + You can change it in Appearance settings. + 您可以在外观设置中更改它。 + No comment provided by engineer. + + + You can configure servers via settings. + No comment provided by engineer. + You can create it later 您可以以后创建它 @@ -6138,6 +8683,7 @@ Repeat join request? You can give another try. + 你可以再试一次。 No comment provided by engineer. @@ -6150,11 +8696,20 @@ Repeat join request? 你可以通过设置让它对你的 SimpleX 联系人可见。 No comment provided by engineer. - - You can now send messages to %@ + + You can now chat with %@ 您现在可以给 %@ 发送消息 notification body + + You can send messages to %@ from Archived contacts. + 您可以从存档的联系人向%@发送消息。 + No comment provided by engineer. + + + You can set connection name, to remember who the link was shared with. + No comment provided by engineer. + You can set lock screen notification preview via settings. 您可以通过设置来设置锁屏通知预览。 @@ -6170,16 +8725,16 @@ Repeat join request? 您可以与您的联系人分享该地址,让他们与 **%@** 联系。 No comment provided by engineer. - - You can share your address as a link or QR code - anybody can connect to you. - 您可以将您的地址作为链接或二维码共享——任何人都可以连接到您。 - No comment provided by engineer. - You can start chat via app Settings / Database or by restarting the app 您可以通过应用程序设置/数据库或重新启动应用程序开始聊天 No comment provided by engineer. + + You can still view conversation with %@ in the list of chats. + 您仍然可以在聊天列表中查看与 %@的对话。 + No comment provided by engineer. + You can turn on SimpleX Lock via Settings. 您可以通过设置开启 SimpleX 锁定。 @@ -6193,23 +8748,23 @@ Repeat join request? You can view invitation link again in connection details. 您可以在连接详情中再次查看邀请链接。 - No comment provided by engineer. + alert message You can't send messages! 您无法发送消息! No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - 您可以控制接收信息使用的服务器,您的联系人则使用您发送信息时所使用的服务器。 - No comment provided by engineer. - You could not be verified; please try again. 您的身份无法验证,请再试一次。 No comment provided by engineer. + + You decide who can connect. + 你决定谁可以连接。 + No comment provided by engineer. + You have already requested connection via this address! 你已经请求通过此地址进行连接! @@ -6218,11 +8773,8 @@ Repeat join request? You have already requested connection! Repeat connection request? - No comment provided by engineer. - - - You have no chats - 您没有聊天记录 + 您已经请求连接了! +重复连接请求? No comment provided by engineer. @@ -6245,11 +8797,26 @@ Repeat connection request? 你加入了这个群组。连接到邀请组成员。 No comment provided by engineer. + + You may migrate the exported database. + 您可以迁移导出的数据库。 + No comment provided by engineer. + + + You may save the exported archive. + 您可以保存导出的档案。 + No comment provided by engineer. + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. 您只能在一台设备上使用最新版本的聊天数据库,否则您可能会停止接收来自某些联系人的消息。 No comment provided by engineer. + + You need to allow your contact to call to be able to call them. + 您需要允许您的联系人呼叫才能呼叫他们。 + No comment provided by engineer. + You need to allow your contact to send voice messages to be able to send them. 您需要允许您的联系人发送语音消息,以便您能够发送语音消息。 @@ -6265,6 +8832,10 @@ Repeat connection request? 您发送了群组邀请 No comment provided by engineer. + + You should receive notifications. + token info + You will be connected to group when the group host's device is online, please wait or check later! 您将在组主设备上线时连接到该群组,请稍等或稍后再检查! @@ -6272,6 +8843,7 @@ Repeat connection request? You will be connected when group link host's device is online, please wait or check later! + 当 Group Link Host 的设备在线时,您将被连接,请稍候或稍后检查! No comment provided by engineer. @@ -6299,6 +8871,10 @@ Repeat connection request? 当静音配置文件处于活动状态时,您仍会收到来自静音配置文件的电话和通知。 No comment provided by engineer. + + You will stop receiving messages from this chat. Chat history will be preserved. + No comment provided by engineer. + You will stop receiving messages from this group. Chat history will be preserved. 您将停止接收来自该群组的消息。聊天记录将被保留。 @@ -6319,31 +8895,16 @@ Repeat connection request? 您正在为该群组使用隐身个人资料——为防止共享您的主要个人资料,不允许邀请联系人 No comment provided by engineer. - - Your %@ servers - 您的 %@ 服务器 - No comment provided by engineer. - Your ICE servers 您的 ICE 服务器 No comment provided by engineer. - - Your SMP servers - 您的 SMP 服务器 - No comment provided by engineer. - Your SimpleX address 您的 SimpleX 地址 No comment provided by engineer. - - Your XFTP servers - 您的 XFTP 服务器 - No comment provided by engineer. - Your calls 您的通话 @@ -6359,16 +8920,17 @@ Repeat connection request? 您的聊天数据库未加密——设置密码来加密。 No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles 您的聊天资料 No comment provided by engineer. - - Your contact needs to be online for the connection to complete. -You can cancel this connection and remove the contact (and try later with a new link). - 您的联系人需要在线才能完成连接。 -您可以取消此连接并删除联系人(然后尝试使用新链接)。 + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. No comment provided by engineer. @@ -6386,6 +8948,10 @@ You can cancel this connection and remove the contact (and try later with a new 与您的联系人保持连接。 No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. 您当前的聊天数据库将被删除并替换为导入的数据库。 @@ -6408,6 +8974,7 @@ You can cancel this connection and remove the contact (and try later with a new Your profile + 您的个人资料 No comment provided by engineer. @@ -6415,33 +8982,34 @@ You can cancel this connection and remove the contact (and try later with a new 您的个人资料 **%@** 将被共享。 No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - 您的资料存储在您的设备上并仅与您的联系人共享。 -SimpleX 服务器无法看到您的资料。 + + Your profile is stored on your device and only shared with your contacts. + 该资料仅与您的联系人共享。 No comment provided by engineer. - - Your profile, contacts and delivered messages are stored on your device. - 您的资料、联系人和发送的消息存储在您的设备上。 + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + 您的资料存储在您的设备上并仅与您的联系人共享。 SimpleX 服务器无法看到您的资料。 No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your random profile 您的随机资料 No comment provided by engineer. - - Your server - 您的服务器 - No comment provided by engineer. - Your server address 您的服务器地址 No comment provided by engineer. + + Your servers + No comment provided by engineer. + Your settings 您的设置 @@ -6482,11 +9050,20 @@ SimpleX 服务器无法看到您的资料。 已接受通话 call status + + accepted invitation + chat list item title + admin 管理员 member role + + admins + 管理员 + feature role + agreeing encryption for %@… 正在协商将加密应用于 %@… @@ -6497,6 +9074,11 @@ SimpleX 服务器无法看到您的资料。 同意加密… chat item text + + all members + 所有成员 + feature role + always 始终 @@ -6504,6 +9086,16 @@ SimpleX 服务器无法看到您的资料。 and %lld other events + 和 %lld 其他事件 + No comment provided by engineer. + + + archived report + No comment provided by engineer. + + + attempts + 尝试 No comment provided by engineer. @@ -6539,13 +9131,19 @@ SimpleX 服务器无法看到您的资料。 blocked by admin 由管理员封禁 - marked deleted chat item preview text + blocked chat item +marked deleted chat item preview text bold 加粗 No comment provided by engineer. + + call + 呼叫 + No comment provided by engineer. + call error 通话错误 @@ -6649,7 +9247,7 @@ SimpleX 服务器无法看到您的资料。 connecting… 连接中…… - chat list item title + No comment provided by engineer. connection established @@ -6663,6 +9261,7 @@ SimpleX 服务器无法看到您的资料。 contact %1$@ changed to %2$@ + 联系人 %1$@ 已更改为 %2$@ profile update event chat item @@ -6695,10 +9294,16 @@ SimpleX 服务器无法看到您的资料。 time unit + + decryption errors + 解密错误 + No comment provided by engineer. + default (%@) 默认 (%@) - pref value + delete after time +pref value default (no) @@ -6745,6 +9350,11 @@ SimpleX 服务器无法看到您的资料。 重复的消息 integrity error chat item + + duplicates + 副本 + No comment provided by engineer. + e2e encrypted 端到端加密 @@ -6820,9 +9430,14 @@ SimpleX 服务器无法看到您的资料。 错误 No comment provided by engineer. - - event happened - 发生的事 + + expired + 过期 + No comment provided by engineer. + + + forwarded + 已转发 No comment provided by engineer. @@ -6850,6 +9465,11 @@ SimpleX 服务器无法看到您的资料。 在您重启应用或改变密码后,iOS钥匙串将被用来安全地存储密码——它将允许接收推送通知。 No comment provided by engineer. + + inactive + 无效 + No comment provided by engineer. + incognito via contact address link 通过联系人地址链接隐身聊天 @@ -6890,6 +9510,11 @@ SimpleX 服务器无法看到您的资料。 邀请您加入群组 %@ group name + + invite + 邀请 + No comment provided by engineer. + invited 已邀请 @@ -6937,6 +9562,7 @@ SimpleX 服务器无法看到您的资料。 member %1$@ changed to %2$@ + 成员 %1$@ 已更改为 %2$@ profile update event chat item @@ -6944,6 +9570,11 @@ SimpleX 服务器无法看到您的资料。 已连接 rcv group event chat item + + message + 消息 + No comment provided by engineer. + message received 消息已收到 @@ -6969,6 +9600,10 @@ SimpleX 服务器无法看到您的资料。 由 %@ 审核 marked deleted chat item preview text + + moderator + member role + months @@ -6977,7 +9612,7 @@ SimpleX 服务器无法看到您的资料。 never 从不 - No comment provided by engineer. + delete after time new message @@ -7008,8 +9643,8 @@ SimpleX 服务器无法看到您的资料。 off 关闭 enabled status - group pref value - time to disappear +group pref value +time to disappear offered %@ @@ -7026,18 +9661,42 @@ SimpleX 服务器无法看到您的资料。 开启 group pref value + + other + 其他 + No comment provided by engineer. + + + other errors + 其他错误 + No comment provided by engineer. + owner 群主 member role + + owners + 所有者 + feature role + peer-to-peer 点对点 No comment provided by engineer. + + pending + No comment provided by engineer. + + + pending approval + No comment provided by engineer. + quantum resistant e2e encryption + 抗量子端到端加密 chat item text @@ -7050,6 +9709,10 @@ SimpleX 服务器无法看到您的资料。 已受到确认…… No comment provided by engineer. + + rejected + No comment provided by engineer. + rejected call 拒接来电 @@ -7080,6 +9743,25 @@ SimpleX 服务器无法看到您的资料。 已将您移除 rcv group event chat item + + requested to connect + chat list item title + + + saved + 已保存 + No comment provided by engineer. + + + saved from %@ + 保存自 %@ + No comment provided by engineer. + + + search + 搜索 + No comment provided by engineer. + sec @@ -7105,6 +9787,15 @@ SimpleX 服务器无法看到您的资料。 发送私信 No comment provided by engineer. + + server queue info: %1$@ + +last received msg: %2$@ + 服务器队列信息: %1$@ + +上次收到的消息: %2$@ + queue info + set new contact address 设置新的联系地址 @@ -7117,6 +9808,7 @@ SimpleX 服务器无法看到您的资料。 standard end-to-end encryption + 标准端到端加密 chat item text @@ -7136,6 +9828,7 @@ SimpleX 服务器无法看到您的资料。 unblocked %@ + 未阻止 %@ rcv group event chat item @@ -7143,11 +9836,21 @@ SimpleX 服务器无法看到您的资料。 未知 connection info + + unknown servers + 未知服务器 + No comment provided by engineer. + unknown status 未知状态 No comment provided by engineer. + + unprotected + 未受保护 + No comment provided by engineer. + updated group profile 已更新的群组资料 @@ -7160,6 +9863,7 @@ SimpleX 服务器无法看到您的资料。 v%@ + v%@ No comment provided by engineer. @@ -7187,6 +9891,11 @@ SimpleX 服务器无法看到您的资料。 通过中继 No comment provided by engineer. + + video + 视频 + No comment provided by engineer. + video call (not e2e encrypted) 视频通话(非端到端加密) @@ -7212,11 +9921,21 @@ SimpleX 服务器无法看到您的资料。 time unit + + when IP hidden + 当 IP 隐藏时 + No comment provided by engineer. + yes pref value + + you + + No comment provided by engineer. + you are invited to group 您被邀请加入群组 @@ -7229,6 +9948,7 @@ SimpleX 服务器无法看到您的资料。 you blocked %@ + 你阻止了%@ snd group event chat item @@ -7273,6 +9993,7 @@ SimpleX 服务器无法看到您的资料。 you unblocked %@ + 您解封了 %@ snd group event chat item @@ -7289,7 +10010,7 @@ SimpleX 服务器无法看到您的资料。
- +
@@ -7309,6 +10030,7 @@ SimpleX 服务器无法看到您的资料。 SimpleX uses local network access to allow using user chat profile via desktop app on the same network. + SimpleX 使用本地网络访问,允许通过同一网络上的桌面应用程序使用用户聊天配置文件。 Privacy - Local Network Usage Description @@ -7325,7 +10047,7 @@ SimpleX 服务器无法看到您的资料。
- +
@@ -7345,4 +10067,245 @@ SimpleX 服务器无法看到您的资料。
+ +
+ +
+ + + %d new events + notification body + + + From %d chat(s) + notification body + + + From: %@ + notification body + + + New events + notification + + + New messages + notification + + +
+ +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + 版权所有 © 2024 SimpleX Chat。保留所有权利。 + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + App is locked! + 应用程序已锁定! + No comment provided by engineer. + + + Cancel + 取消 + No comment provided by engineer. + + + Cannot access keychain to save database password + 无法访问钥匙串以保存数据库密码 + No comment provided by engineer. + + + Cannot forward message + 无法转发消息 + No comment provided by engineer. + + + Comment + 评论 + No comment provided by engineer. + + + Currently maximum supported file size is %@. + 当前支持的最大文件大小为 %@。 + No comment provided by engineer. + + + Database downgrade required + 需要数据库降级 + No comment provided by engineer. + + + Database encrypted! + 数据库已加密! + No comment provided by engineer. + + + Database error + 数据库错误 + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + 数据库密码与保存在钥匙串中的密码不同。 + No comment provided by engineer. + + + Database passphrase is required to open chat. + 需要数据库密码才能打开聊天。 + No comment provided by engineer. + + + Database upgrade required + 需要升级数据库 + No comment provided by engineer. + + + Error preparing file + 准备文件时出错 + No comment provided by engineer. + + + Error preparing message + 准备消息时出错 + No comment provided by engineer. + + + Error: %@ + 错误:%@ + No comment provided by engineer. + + + File error + 文件错误 + No comment provided by engineer. + + + Incompatible database version + 不兼容的数据库版本 + No comment provided by engineer. + + + Invalid migration confirmation + 无效的迁移确认 + No comment provided by engineer. + + + Keychain error + 钥匙串错误 + No comment provided by engineer. + + + Large file! + 大文件! + No comment provided by engineer. + + + No active profile + 无活动配置文件 + No comment provided by engineer. + + + Ok + 好的 + No comment provided by engineer. + + + Open the app to downgrade the database. + 打开应用程序以降级数据库。 + No comment provided by engineer. + + + Open the app to upgrade the database. + 打开应用程序以升级数据库。 + No comment provided by engineer. + + + Passphrase + 密码 + No comment provided by engineer. + + + Please create a profile in the SimpleX app + 请在 SimpleX 应用程序中创建配置文件 + No comment provided by engineer. + + + Selected chat preferences prohibit this message. + 选定的聊天首选项禁止此消息。 + No comment provided by engineer. + + + Sending a message takes longer than expected. + 发送消息所需的时间比预期的要长。 + No comment provided by engineer. + + + Sending message… + 正在发送消息… + No comment provided by engineer. + + + Share + 共享 + No comment provided by engineer. + + + Slow network? + 网络速度慢? + No comment provided by engineer. + + + Unknown database error: %@ + 未知数据库错误: %@ + No comment provided by engineer. + + + Unsupported format + 不支持的格式 + No comment provided by engineer. + + + Wait + 等待 + No comment provided by engineer. + + + Wrong database passphrase + 数据库密码错误 + No comment provided by engineer. + + + You can allow sharing in Privacy & Security / SimpleX Lock settings. + 您可以在 "隐私与安全"/"SimpleX Lock "设置中允许共享。 + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/en.lproj/Localizable.strings +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/contents.json b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/contents.json index 807a15f96c..91977b0744 100644 --- a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/contents.json +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/contents.json @@ -3,10 +3,10 @@ "project" : "SimpleX.xcodeproj", "targetLocale" : "zh-Hans", "toolInfo" : { - "toolBuildNumber" : "15A240d", + "toolBuildNumber" : "16C5032a", "toolID" : "com.apple.dt.xcode", "toolName" : "Xcode", - "toolVersion" : "15.0" + "toolVersion" : "16.2" }, "version" : "1.0" } \ No newline at end of file diff --git a/apps/ios/SimpleX Localizations/zh-Hant.xcloc/Localized Contents/zh-Hant.xliff b/apps/ios/SimpleX Localizations/zh-Hant.xcloc/Localized Contents/zh-Hant.xliff index 03a108d112..3ea46ee364 100644 --- a/apps/ios/SimpleX Localizations/zh-Hant.xcloc/Localized Contents/zh-Hant.xliff +++ b/apps/ios/SimpleX Localizations/zh-Hant.xcloc/Localized Contents/zh-Hant.xliff @@ -109,7 +109,7 @@
%d skipped message(s) - %d錯過了訊息 + 錯過的 %d 則訊息 integrity error chat item @@ -124,17 +124,17 @@ %lld contact(s) selected - %lld 已選擇聯絡人(s) + 已選擇 %lld 個聯絡人 No comment provided by engineer. %lld file(s) with total size of %@ - %lld 檔案(s) 的總共大小為%@ + %lld 個檔案,總共大小 %@ No comment provided by engineer. %lld members - %lld 成員 + %lld 個成員 No comment provided by engineer. @@ -187,23 +187,18 @@ ) No comment provided by engineer. - - **Add new contact**: to create your one-time QR Code or link for your contact. - **新增新的聯絡人**:建立一次性二維碼或連結連接聯絡人。 - No comment provided by engineer. - **Create link / QR code** for your contact to use. **建立連結 / 二維碼** 讓你的聯絡人使用。 No comment provided by engineer. - - **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + + **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. **更有私隱**:每20分鐘會檢查一次訊息。裝置權杖與 SimpleX Chat 伺服器分享中,但是不包括你的聯絡人和訊息資料。 No comment provided by engineer. - - **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + + **Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app. **最有私隱**:不使用 SimpleX Chat 通知服務器,在後台定期檢查訊息(取決於你使用應用程序的頻率)。 No comment provided by engineer. @@ -217,8 +212,8 @@ **請注意**:如果你忘記了密碼你將不能再次復原或更改密碼。 No comment provided by engineer. - - **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + + **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. **建議**:裝置權杖和通知都會傳送去 SimpeleX Chat 的通知伺服器,但是不包括訊息內容、大小或傳送者資料。 No comment provided by engineer. @@ -229,7 +224,7 @@ **Warning**: Instant push notifications require passphrase saved in Keychain. - **警告**:即時推送訊息通知需要數據庫的密碼儲存在資料庫中。 + **警告**:即時推送訊息通知需要將數據庫的密碼儲存在資料庫中。 No comment provided by engineer. @@ -314,7 +309,7 @@ About SimpleX Chat - 關於 SimpleX 對話 + 關於 SimpleX Chat No comment provided by engineer. @@ -358,9 +353,9 @@ 使用二維碼掃描以新增伺服器。 No comment provided by engineer. - - Add server… - 新增伺服器… + + Add server + 新增伺服器 No comment provided by engineer. @@ -445,7 +440,7 @@ Allow your contacts to send disappearing messages. - 允許你的聯絡人傳送自動銷毀的訊息。 + 允許您的聯絡人傳送限時訊息。 No comment provided by engineer. @@ -1178,8 +1173,8 @@ 私訊 chat feature - - Direct messages between members are prohibited in this group. + + Direct messages between members are prohibited. 私訊群組內的成員於這個群組內是禁用的。 No comment provided by engineer. @@ -1198,8 +1193,8 @@ 自動銷毀訊息已被禁止於此聊天室。 No comment provided by engineer. - - Disappearing messages are prohibited in this group. + + Disappearing messages are prohibited. 自動銷毀訊息於這個群組內是禁用的。 No comment provided by engineer. @@ -1623,18 +1618,18 @@ 群組內的成員可以不可逆地刪除訊息。 No comment provided by engineer. - - Group members can send direct messages. + + Members can send direct messages. 群組內的成員可以私訊群組內的成員。 No comment provided by engineer. - - Group members can send disappearing messages. + + Members can send disappearing messages. 群組內的成員可以傳送自動銷毀的訊息。 No comment provided by engineer. - - Group members can send voice messages. + + Members can send voice messages. 群組內的成員可以傳送語音訊息。 No comment provided by engineer. @@ -1747,8 +1742,8 @@ 下載圖片需要傳送者上線的時候才能下載圖片,請等待對方上線! No comment provided by engineer. - - Immune to spam and abuse + + Immune to spam 不受垃圾郵件和濫用行為影響 No comment provided by engineer. @@ -1869,8 +1864,8 @@ 不可逆地刪除訊息於這個聊天室內是禁用的。 No comment provided by engineer. - - Irreversible message deletion is prohibited in this group. + + Irreversible message deletion is prohibited. 不可逆地刪除訊息於這個群組內是禁用的。 No comment provided by engineer. @@ -2217,8 +2212,8 @@ We will be adding server redundancy to prevent lost messages. Onion 主機不會啟用。 No comment provided by engineer. - - Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + + Only client devices store user profiles, contacts, groups, and messages. 只有客戶端裝置才會儲存你的個人檔案、聯絡人,群組,所有訊息都會經過**兩層的端對端加密**。 No comment provided by engineer. @@ -2277,8 +2272,8 @@ We will be adding server redundancy to prevent lost messages. 使用終端機開啟對話 authentication reason - - Open-source protocol and code – anybody can run the servers. + + Anybody can host servers. 開放源碼協議和程式碼 – 任何人也可以運行伺服器。 No comment provided by engineer. @@ -2317,8 +2312,8 @@ We will be adding server redundancy to prevent lost messages. 將你接收到的連結貼上至下面的框內,以開始你與你的聯絡人對話。 No comment provided by engineer. - - People can connect to you only via the links you share. + + You decide who can connect. 人們只能在你分享了連結後,才能和你連接。 No comment provided by engineer. @@ -2709,12 +2704,12 @@ We will be adding server redundancy to prevent lost messages. Send link previews - 傳送可以預覽的連結 + 傳送連結預覽 No comment provided by engineer. Send live message - 傳送實況的訊息 + 傳送實時訊息 No comment provided by engineer. @@ -2729,7 +2724,7 @@ We will be adding server redundancy to prevent lost messages. Send questions and ideas - 傳送問題和想法給開發者 + 給開發者提問題和想法 No comment provided by engineer. @@ -2779,7 +2774,7 @@ We will be adding server redundancy to prevent lost messages. Set 1 day - 設定為1天 + 設定為 1 天 No comment provided by engineer. @@ -3010,8 +3005,8 @@ We will be adding server redundancy to prevent lost messages. 感謝你安裝SimpleX Chat! No comment provided by engineer. - - The 1st platform without any user identifiers – private by design. + + No user identifiers. 第一個沒有任何用戶識別符的通訊平台 – 以私隱為設計。 No comment provided by engineer. @@ -3027,7 +3022,7 @@ We will be adding server redundancy to prevent lost messages. The connection you accepted will be cancelled! - 你所接受的連接將被取消! + 你接受的連接將被取消! No comment provided by engineer. @@ -3049,8 +3044,8 @@ We will be adding server redundancy to prevent lost messages. The microphone does not work when the app is in the background. No comment provided by engineer. - - The next generation of private messaging + + The future of messaging 新一代的私密訊息平台 No comment provided by engineer. @@ -3059,14 +3054,14 @@ We will be adding server redundancy to prevent lost messages. 舊的數據庫在遷移過程中沒有被移除,可以刪除。 No comment provided by engineer. - - The profile is only shared with your contacts. + + Your profile is stored on your device and only shared with your contacts. 你的個人檔案只會和你的聯絡人分享。 No comment provided by engineer. The sender will NOT be notified - 發送者不會接收到通知 + 發送者不會收到通知 No comment provided by engineer. @@ -3076,12 +3071,12 @@ We will be adding server redundancy to prevent lost messages. This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain. - 這操作不能還原 - 所有已經接收和傳送的檔案和媒體檔案將刪除。低解析度圖片將保留。 + 這操作不能還原 - 將刪除所有已經接收和傳送的檔案和媒體。將保留低解析度圖片。 No comment provided by engineer. This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes. - 這操作無法撤銷 - 早於所選擇的時間發送和接收的訊息將被刪除。這可能需要幾分鐘的時間。 + 這操作無法撤銷 - 早於所選時間的收發訊息將被刪除。可能需要幾分鐘。 No comment provided by engineer. @@ -3118,8 +3113,8 @@ We will be adding server redundancy to prevent lost messages. To prevent the call interruption, enable Do Not Disturb mode. No comment provided by engineer. - - To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + + To protect your privacy, SimpleX uses separate IDs for each of your contacts. 為了保護隱私,而不像是其他平台般需要提取和存儲用戶的 IDs 資料,SimpleX 平台有自家佇列的標識符,這對於你的每個聯絡人也是獨一無二的。 No comment provided by engineer. @@ -3268,7 +3263,7 @@ To connect, please ask your contact to create another connection link and check Use for new connections - 用於新的連接 + 用於新的連線 No comment provided by engineer. @@ -3288,7 +3283,7 @@ To connect, please ask your contact to create another connection link and check Verify connection security - 驗證連接安全性 + 驗證連線安全性 No comment provided by engineer. @@ -3321,8 +3316,8 @@ To connect, please ask your contact to create another connection link and check 語音訊息於這個聊天窒是禁用的。 No comment provided by engineer. - - Voice messages are prohibited in this group. + + Voice messages are prohibited. 語音訊息於這個群組內是禁用的。 No comment provided by engineer. @@ -3455,11 +3450,6 @@ To connect, please ask your contact to create another connection link and check 你可以使用 Markdown 語法以更清楚標明訊息: No comment provided by engineer. - - You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. - 你可以控制通過哪一個伺服器 **來接收** 你的聯絡人訊息 – 這些伺服器用來接收他們傳送給你的訊息。 - No comment provided by engineer. - You could not be verified; please try again. 你未能通過認證;請再試一次。 @@ -4173,7 +4163,7 @@ SimpleX 伺服器並不會看到你的個人檔案。 via contact address link - 透過聯絡人的邀請連結連接 + 透過聯絡人的邀請連結連線 chat list item description @@ -4183,7 +4173,7 @@ SimpleX 伺服器並不會看到你的個人檔案。 via one-time link - 透過一次性連結連接 + 透過一次性連結連線 chat list item description @@ -4712,7 +4702,7 @@ Available in v5.1 %u messages failed to decrypt. - %u 訊息解密失敗。 + %u 則訊息解密失敗。 No comment provided by engineer. @@ -4791,8 +4781,8 @@ Available in v5.1 訊息 & 檔案 No comment provided by engineer. - - Migrations: %@ + + Migrations: 遷移:%@ No comment provided by engineer. @@ -5162,7 +5152,7 @@ Available in v5.1 Tap to activate profile. - 點擊以激活配置檔案。 + 點擊以激活設定檔。 No comment provided by engineer. @@ -5523,8 +5513,8 @@ It can happen because of some bug or when the connection is compromised.啟用自毀密碼 set passcode view - - Group members can add message reactions. + + Members can add message reactions. 群組內的成員可以新增訊息互動。 No comment provided by engineer. @@ -5699,8 +5689,8 @@ It can happen because of some bug or when the connection is compromised.已移除在 No comment provided by engineer. - - Message reactions are prohibited in this group. + + Message reactions are prohibited. 訊息互動於這個群組內是禁用的。 No comment provided by engineer. @@ -5898,6 +5888,598 @@ It can happen because of some bug or when the connection is compromised.%@ 和 %@ 已連接 No comment provided by engineer. + + %@ downloaded + %@ 下載 + + + %@ uploaded + %@ 上傳 + + + Abort + 中止 + + + **Create group**: to create a new group. + **創建群組**: 創建一個新的群組。 + + + Abort changing address + 中止更改地址 + + + Accept connection request? + 接受連線請求? + + + Camera not available + 相機不可用 + + + All messages will be deleted - this cannot be undone! + 所有訊息都將被刪除 - 這不能還原! + + + Allow irreversible message deletion only if your contact allows it to you. (24 hours) + 只有你的聯絡人允許的情況下,才允許不可逆地將訊息刪除。(24小時) + + + Allow to irreversibly delete sent messages. (24 hours) + 允許將不可撤銷的訊息刪除。(24小時) + + + Allow your contacts to irreversibly delete sent messages. (24 hours) + 允許您的聯絡人不可復原地刪除已傳送的訊息。(24小時) + + + Bad desktop address + 無效的桌面地址 + + + Error decrypting file + 解密檔案時出錯 + + + Add contact + 新增聯絡人 + + + Advanced settings + 進階設定 + + + Allow calls? + 允許通話? + + + Allow to send files and media. + 允許傳送檔案和媒體。 + + + Already joining the group! + 已加入群組! + + + App data migration + 應用資料轉移 + + + Apply + 應用 + + + Apply to + 應用到 + + + Archive and upload + 儲存並上傳 + + + Block + 封鎖 + + + Block group members + 封鎖群組成員 + + + Block member + 封鎖成員 + + + Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + 保加利亞語、芬蘭語、泰語和烏克蘭語——感謝使用者們和[Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + + + Can't call member + 無法與成員通話 + + + Can't message member + 無法傳送訊息給成員 + + + Cancel migration + 取消遷移 + + + Chat database exported + 導出聊天數據庫 + + + 0 sec + 0 秒 + + + All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. + 你的所有聯絡人、對話和文件將被安全加密並切塊上傳到設置的 XFTP 中繼。 + + + Address change will be aborted. Old receiving address will be used. + 將取消地址更改。將使用舊聯絡地址。 + + + Archiving database + 正在儲存資料庫 + + + Cellular + 行動網路 + + + %@, %@ and %lld members + %@, %@ 和 %lld 成員 + + + %lld messages marked deleted + %lld 則訊息已標記為刪除 + + + Already connecting! + 已連接! + + + Block member? + 封鎖成員? + + + (new) + (新) + + + %@, %@ and %lld other members connected + %@, %@ 和 %lld 個成員已連接 + + + A few more things + 其他 + + + Show last messages + 顯示最新的訊息 + + + App encrypts new local files (except videos). + 應用程式將為新的本機文件(影片除外)加密。 + + + Better groups + 更加的群組 + + + %lld new interface languages + %lld 種新的介面語言 + + + Blocked by admin + 由管理員封鎖 + + + Both you and your contact can irreversibly delete sent messages. (24 hours) + 您與您的聯絡人都可以不可復原地删除已傳送的訊息。(24小時) + + + Encrypt local files + 加密本機檔案 + + + - more stable message delivery. +- a bit better groups. +- and more! + - 更穩定的傳送! +- 更好的社群! +- 以及更多! + + + - optionally notify deleted contacts. +- profile names with spaces. +- and more! + - 可選擇通知已刪除的聯絡人 +- 帶空格的共人資料名稱。 +-以及更多! + + + Abort changing address? + 中止更改地址? + + + Allow to send SimpleX links. + 允許傳送 SimpleX 連結。 + + + Background + 後台 + + + SimpleX links not allowed + 不允許 SimpleX 連結 + + + Voice messages not allowed + 不允許語音訊息 + + + The text you pasted is not a SimpleX link. + 您貼在這裡的連結不是 SimpleX 連結。 + + + %d file(s) were deleted. + 已刪除 %d 個檔案。 + + + Reset to app theme + 重設至應用程式主題 + + + Retry + 重試 + + + The uploaded database archive will be permanently removed from the servers. + 上傳的資料庫存檔將從伺服器永久移除。 + + + Shape profile images + 塑造個人資料圖片 + + + **Scan / Paste link**: to connect via a link you received. + **掃描/貼上連結**:以透過您收到的連結連線。 + + + Reports + 舉報 + + + Use SOCKS proxy + 使用 SOCKS 代理 + + + Reset all statistics + 重設所有統計數據 + + + SOCKS proxy + SOCKS 代理 + + + Send message to enable calls. + 發送訊息以啟用通話功能。 + + + Send direct message to connect + 直接發送訊息以連結 + + + Scale + 顯示比例 + + + Sent via proxy + 通過代理發送 + + + Servers info + 伺服器訊息 + + + Set message expiration in chats. + 設定聊天中訊息期限。 + + + Share SimpleX address on social media. + 在社交媒體上分享 SimpleX 聯絡地址。 + + + Storage + 存儲 + + + Starting from %@. + 開始於 %@。 + + + The second tick we missed! ✅ + 我們錯過的第二個勾選! ✅ + + + Themes + 主題 + + + %d file(s) failed to download. + %d 個檔案下載失敗。 + + + Session code + 會話代碼 + + + Servers statistics will be reset - this cannot be undone! + 伺服器統計資料將被重設 - 此操作無法撤銷! + + + **Create 1-time link**: to create and share a new invitation link. + **建立一次性連結**:建立並分享新邀請連結。 + + + Set default theme + 設定缺省主題 + + + %lld group events + %lld 個群組事件 + + + Reset all statistics? + 重設所有統計數據? + + + %@ server + %@ 伺服器 + + + %d file(s) were not downloaded. + %d 個檔案未下載。 + + + %d messages not forwarded + %d 則訊息未轉發 + + + Test notifications + 测试通知 + + + (this device v%@) + (此設備 v%@) + + + Settings were changed. + 設定已更改。 + + + This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted. + 這操作不能撤銷 - 此聊天中早於所選訊息的收發訊息將被刪除。 + + + Subscription errors + 訂閱錯誤 + + + Report + 舉報 + + + Send messages directly when IP address is protected and your or destination server does not support private routing. + 當 IP 位址受保護且您或目的地伺服器不支援私人路由時,直接傳送訊息。 + + + Reset to user theme + 重設為使用者主題 + + + Use short links (BETA) + 使用短連結(Beta) + + + Up to 100 last messages are sent to new members. + 最多 100 則最後的訊息會傳送至新成員。 + + + %d seconds(s) + %d 秒 + + + %d file(s) are still being downloaded. + 仍在下載 %d 個檔案。 + + + %lld messages blocked by admin + %lld 則訊息被管理員封鎖 + + + Report: %@ + 舉報:%@ + + + Review conditions + 檢視使用條款 + + + Search or paste SimpleX link + 搜尋或貼上 SimpleX 連結 + + + Sent directly + 已直接發送 + + + SimpleX links are prohibited. + 這群組禁止 SimpleX 連結。 + + + Uploaded files + 已上傳的檔案 + + + Use %@ + 使用 %@ + + + Upload errors + 上傳錯誤 + + + Use servers + 使用伺服器 + + + security code changed + 安全碼已變更 + + + These settings are for your current profile **%@**. + 這些設定是針對您目前的設定檔 **%@**。 + + + They can be overridden in contact and group settings. + 您可在連絡人和群組設定中覆寫它們。 + + + %1$@, %2$@ + %1$@, %2$@ + + + Verify connections + 驗證連線 + + + Verify connection + 驗證連線 + + + Verify passphrase + 驗證密碼 + + + Verify code with desktop + 使用桌上電腦驗證代碼 + + + Save list + 儲存列表 + + + Saving %lld messages + 正在儲存 %lld 則訊息 + + + search + 搜尋 + + + requested to connect + 已請求連結 + + + saved + 已儲存 + + + video + 視訊 + + + Tap to Connect + 點擊以連結 + + + Unsupported connection link + 未受支持的連線連結 + + + Saved from + 儲存自 + + + Saved + 已儲存 + + + Scan / Paste link + 掃描/貼上連結 + + + SimpleX + SimpleX + + + Use the app while in the call. + 在通話時使用此應用程式。 + + + v%@ + v%@ + + + Save your profile? + 儲存設定檔? + + + Use for messages + 用於訊息 + + + Uploading archive + 正在上傳檔案庫 + + + Unlink + 從桌上電腦解除連結 + + + %lld messages blocked + 已封鎖 %d 則訊息 + + + The same conditions will apply to operator **%@**. + 相同條件也適用於 **%@** 操作員。 + + + These conditions will also apply for: **%@**. + 這些條件也適用於:**%@**。 + + + Upload failed + 上傳失敗 + + + Use the app with one hand. + 單手使用此應用程式。 + + + Safely receive files + 安全地接收檔案 + + + Saved message + 已儲存的訊息 + + + Use from desktop + 在桌上電腦上使用 + + + Via secure quantum resistant protocol. + 使用量子安全的協定。 + + + Uploaded + 已上傳 + diff --git a/apps/ios/SimpleX NSE/ConcurrentQueue.swift b/apps/ios/SimpleX NSE/ConcurrentQueue.swift deleted file mode 100644 index 274a683c00..0000000000 --- a/apps/ios/SimpleX NSE/ConcurrentQueue.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// ConcurrentQueue.swift -// SimpleX NSE -// -// Created by Evgeny on 08/12/2023. -// Copyright © 2023 SimpleX Chat. All rights reserved. -// - -import Foundation - -struct DequeueElement { - var elementId: UUID? - var task: Task -} - -class ConcurrentQueue { - private var queue: [T] = [] - private var queueLock = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.concurrent-queue.lock.\(UUID())") - private var continuations = [(elementId: UUID, continuation: CheckedContinuation)]() - - func enqueue(_ el: T) { - resumeContinuation(el) { self.queue.append(el) } - } - - func frontEnqueue(_ el: T) { - resumeContinuation(el) { self.queue.insert(el, at: 0) } - } - - private func resumeContinuation(_ el: T, add: @escaping () -> Void) { - queueLock.sync { - if let (_, cont) = continuations.first { - continuations.remove(at: 0) - cont.resume(returning: el) - } else { - add() - } - } - } - - func dequeue() -> DequeueElement { - queueLock.sync { - if queue.isEmpty { - let elementId = UUID() - let task = Task { - await withCheckedContinuation { cont in - continuations.append((elementId, cont)) - } - } - return DequeueElement(elementId: elementId, task: task) - } else { - let el = queue.remove(at: 0) - return DequeueElement(task: Task { el }) - } - } - } - - func cancelDequeue(_ elementId: UUID) { - queueLock.sync { - let cancelled = continuations.filter { $0.elementId == elementId } - continuations.removeAll { $0.elementId == elementId } - cancelled.forEach { $0.continuation.resume(returning: nil) } - } - } -} diff --git a/apps/ios/SimpleX NSE/NSEAPITypes.swift b/apps/ios/SimpleX NSE/NSEAPITypes.swift new file mode 100644 index 0000000000..35a838fff9 --- /dev/null +++ b/apps/ios/SimpleX NSE/NSEAPITypes.swift @@ -0,0 +1,127 @@ +// +// APITypes.swift +// SimpleX +// +// Created by EP on 01/05/2025. +// Copyright © 2025 SimpleX Chat. All rights reserved. +// + +import SimpleXChat + +enum NSEChatCommand: ChatCmdProtocol { + case showActiveUser + case startChat(mainApp: Bool, enableSndFiles: Bool) + case apiActivateChat(restoreChat: Bool) + case apiSuspendChat(timeoutMicroseconds: Int) + case apiSetNetworkConfig(networkConfig: NetCfg) + case apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) + case apiSetEncryptLocalFiles(enable: Bool) + case apiGetNtfConns(nonce: String, encNtfInfo: String) + case apiGetConnNtfMessages(connMsgReqs: [ConnMsgReq]) + case receiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?, inline: Bool?) + case setFileToReceive(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?) + + var cmdString: String { + switch self { + case .showActiveUser: return "/u" + case let .startChat(mainApp, enableSndFiles): return "/_start main=\(onOff(mainApp)) snd_files=\(onOff(enableSndFiles))" + case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))" + case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)" + case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))" + case let .apiSetAppFilePaths(filesFolder, tempFolder, assetsFolder): + return "/set file paths \(encodeJSON(AppFilePaths(appFilesFolder: filesFolder, appTempFolder: tempFolder, appAssetsFolder: assetsFolder)))" + case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))" + case let .apiGetNtfConns(nonce, encNtfInfo): return "/_ntf conns \(nonce) \(encNtfInfo)" + case let .apiGetConnNtfMessages(connMsgReqs): return "/_ntf conn messages \(connMsgReqs.map { $0.cmdString }.joined(separator: ","))" + case let .receiveFile(fileId, userApprovedRelays, encrypt, inline): return "/freceive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))" + case let .setFileToReceive(fileId, userApprovedRelays, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))" + } + } + + private func onOffParam(_ param: String, _ b: Bool?) -> String { + if let b = b { + " \(param)=\(onOff(b))" + } else { + "" + } + } +} + +enum NSEChatResponse: Decodable, ChatAPIResult { + case activeUser(user: User) + case chatStarted + case chatRunning + case rcvFileAccepted(user: UserRef, chatItem: AChatItem) + case ntfConns(ntfConns: [NtfConn]) + case connNtfMessages(receivedMsgs: [RcvNtfMsgInfo]) + case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgAckInfo) + case cmdOk(user_: UserRef?) + + var responseType: String { + switch self { + case .activeUser: "activeUser" + case .chatStarted: "chatStarted" + case .chatRunning: "chatRunning" + case .rcvFileAccepted: "rcvFileAccepted" + case .ntfConns: "ntfConns" + case .connNtfMessages: "connNtfMessages" + case .ntfMessage: "ntfMessage" + case .cmdOk: "cmdOk" + } + } + + var details: String { + switch self { + case let .activeUser(user): return String(describing: user) + case .chatStarted: return noDetails + case .chatRunning: return noDetails + case let .rcvFileAccepted(u, chatItem): return withUser(u, String(describing: chatItem)) + case let .ntfConns(ntfConns): return String(describing: ntfConns) + case let .connNtfMessages(receivedMsgs): return "receivedMsgs: \(String(describing: receivedMsgs))" + case let .ntfMessage(u, connEntity, ntfMessage): return withUser(u, "connEntity: \(String(describing: connEntity))\nntfMessage: \(String(describing: ntfMessage))") + case .cmdOk: return noDetails + } + } +} + +enum NSEChatEvent: Decodable, ChatAPIResult { + case chatSuspended + case contactConnected(user: UserRef, contact: Contact, userCustomProfile: Profile?) + case receivedContactRequest(user: UserRef, contactRequest: UserContactRequest) + case newChatItems(user: UserRef, chatItems: [AChatItem]) + case rcvFileSndCancelled(user: UserRef, chatItem: AChatItem, rcvFileTransfer: RcvFileTransfer) + case sndFileComplete(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer) + case sndFileRcvCancelled(user: UserRef, chatItem_: AChatItem?, sndFileTransfer: SndFileTransfer) + case callInvitation(callInvitation: RcvCallInvitation) + case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgAckInfo) + + var responseType: String { + switch self { + case .chatSuspended: "chatSuspended" + case .contactConnected: "contactConnected" + case .receivedContactRequest: "receivedContactRequest" + case .newChatItems: "newChatItems" + case .rcvFileSndCancelled: "rcvFileSndCancelled" + case .sndFileComplete: "sndFileComplete" + case .sndFileRcvCancelled: "sndFileRcvCancelled" + case .callInvitation: "callInvitation" + case .ntfMessage: "ntfMessage" + } + } + + var details: String { + switch self { + case .chatSuspended: return noDetails + case let .contactConnected(u, contact, _): return withUser(u, String(describing: contact)) + case let .receivedContactRequest(u, contactRequest): return withUser(u, String(describing: contactRequest)) + case let .newChatItems(u, chatItems): + let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n") + return withUser(u, itemsString) + case let .rcvFileSndCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .sndFileComplete(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .sndFileRcvCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .callInvitation(inv): return String(describing: inv) + case let .ntfMessage(u, connEntity, ntfMessage): return withUser(u, "connEntity: \(String(describing: connEntity))\nntfMessage: \(String(describing: ntfMessage))") + } + } +} diff --git a/apps/ios/SimpleX NSE/NotificationService.swift b/apps/ios/SimpleX NSE/NotificationService.swift index 6f76781837..176da2481e 100644 --- a/apps/ios/SimpleX NSE/NotificationService.swift +++ b/apps/ios/SimpleX NSE/NotificationService.swift @@ -22,122 +22,53 @@ let nseSuspendSchedule: SuspendSchedule = (2, 4) let fastNSESuspendSchedule: SuspendSchedule = (1, 1) -typealias NtfStream = ConcurrentQueue +public enum NSENotificationData { + case connectionEvent(_ user: User, _ connEntity: ConnectionEntity) + case contactConnected(_ user: any UserLike, _ contact: Contact) + case contactRequest(_ user: any UserLike, _ contactRequest: UserContactRequest) + case messageReceived(_ user: any UserLike, _ cInfo: ChatInfo, _ cItem: ChatItem) + case callInvitation(_ invitation: RcvCallInvitation) + case msgInfo(NtfMsgAckInfo) + case noNtf -// Notifications are delivered via concurrent queues, as they are all received from chat controller in a single loop that -// writes to ConcurrentQueue and when notification is processed, the instance of Notification service extension reads from the queue. -// One queue per connection (entity) is used. -// The concurrent queues allow read cancellation, to ensure that notifications are not lost in case the current thread completes -// before expected notification is read (multiple notifications can be expected, because one notification can be delivered for several messages). -actor PendingNtfs { - static let shared = PendingNtfs() - private var ntfStreams: [String: NtfStream] = [:] - - func createStream(_ id: String) async { - logger.debug("NotificationService PendingNtfs.createStream: \(id)") - if ntfStreams[id] == nil { - ntfStreams[id] = ConcurrentQueue() - logger.debug("NotificationService PendingNtfs.createStream: created ConcurrentQueue") - } - } - - func readStream(_ id: String, for nse: NotificationService, ntfInfo: NtfMessages) async { - logger.debug("NotificationService PendingNtfs.readStream: \(id) \(ntfInfo.ntfMessages.count)") - if !ntfInfo.user.showNotifications { - nse.setBestAttemptNtf(.empty) - } - if let s = ntfStreams[id] { - logger.debug("NotificationService PendingNtfs.readStream: has stream") - var expected = Set(ntfInfo.ntfMessages.map { $0.msgId }) - logger.debug("NotificationService PendingNtfs.readStream: expecting: \(expected)") - var readCancelled = false - var dequeued: DequeueElement? - nse.cancelRead = { - readCancelled = true - if let elementId = dequeued?.elementId { - s.cancelDequeue(elementId) - } - } - while !readCancelled { - dequeued = s.dequeue() - if let ntf = await dequeued?.task.value { - if readCancelled { - logger.debug("NotificationService PendingNtfs.readStream: read cancelled, put ntf to queue front") - s.frontEnqueue(ntf) - break - } else if case let .msgInfo(info) = ntf { - let found = expected.remove(info.msgId) - if found != nil { - logger.debug("NotificationService PendingNtfs.readStream: msgInfo, last: \(expected.isEmpty)") - if expected.isEmpty { break } - } else if let msgTs = ntfInfo.msgTs, info.msgTs > msgTs { - logger.debug("NotificationService PendingNtfs.readStream: unexpected msgInfo") - s.frontEnqueue(ntf) - break - } - } else if ntfInfo.user.showNotifications { - logger.debug("NotificationService PendingNtfs.readStream: setting best attempt") - nse.setBestAttemptNtf(ntf) - if ntf.isCallInvitation { break } - } - } else { - break - } - } - nse.cancelRead = nil - logger.debug("NotificationService PendingNtfs.readStream: exiting") - } - } - - func writeStream(_ id: String, _ ntf: NSENotification) async { - logger.debug("NotificationService PendingNtfs.writeStream: \(id)") - if let s = ntfStreams[id] { - logger.debug("NotificationService PendingNtfs.writeStream: writing ntf") - s.enqueue(ntf) - } - } -} - -// The current implementation assumes concurrent notification delivery and uses semaphores -// to process only one notification per connection (entity) at a time. -class NtfStreamSemaphores { - static let shared = NtfStreamSemaphores() - private static let queue = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.notification-semaphores.lock") - private var semaphores: [String: DispatchSemaphore] = [:] - - func waitForStream(_ id: String) { - streamSemaphore(id, value: 0)?.wait() - } - - func signalStreamReady(_ id: String) { - streamSemaphore(id, value: 1)?.signal() - } - - // this function returns nil if semaphore is just created, so passed value shoud be coordinated with the desired end value of the semaphore - private func streamSemaphore(_ id: String, value: Int) -> DispatchSemaphore? { - NtfStreamSemaphores.queue.sync { - if let s = semaphores[id] { - return s - } else { - semaphores[id] = DispatchSemaphore(value: value) - return nil - } - } - } -} - -enum NSENotification { - case nse(UNMutableNotificationContent) - case callkit(RcvCallInvitation) - case empty - case msgInfo(NtfMsgInfo) - - var isCallInvitation: Bool { + @inline(__always) + var callInvitation: RcvCallInvitation? { switch self { - case let .nse(ntf): ntf.categoryIdentifier == ntfCategoryCallInvitation - case .callkit: true - case .empty: false - case .msgInfo: false + case let .callInvitation(invitation): invitation + default: nil + } + } + + func notificationContent(_ badgeCount: Int) -> UNMutableNotificationContent { + return switch self { + case let .connectionEvent(user, connEntity): createConnectionEventNtf(user, connEntity, badgeCount) + case let .contactConnected(user, contact): createContactConnectedNtf(user, contact, badgeCount) + case let .contactRequest(user, contactRequest): createContactRequestNtf(user, contactRequest, badgeCount) + case let .messageReceived(user, cInfo, cItem): createMessageReceivedNtf(user, cInfo, cItem, badgeCount) + case let .callInvitation(invitation): createCallInvitationNtf(invitation, badgeCount) + case .msgInfo: UNMutableNotificationContent() + case .noNtf: UNMutableNotificationContent() + } + } + + @inline(__always) + var notificationEvent: NSENotificationData? { + switch self { + case .connectionEvent: self + case .contactConnected: self + case .contactRequest: self + case .messageReceived: self + case .callInvitation: self + case .msgInfo: nil + case .noNtf: nil + } + } + + @inline(__always) + var newMsgNtf: NSENotificationData? { + switch self { + case .messageReceived: self + default: nil } } } @@ -147,30 +78,143 @@ enum NSENotification { // or when background notification is received. class NSEThreads { static let shared = NSEThreads() - private static let queue = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.notification-threads.lock") + private let queue = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.notification-threads.lock") private var allThreads: Set = [] - private var activeThreads: Set = [] + private var activeThreads: [(threadId: UUID, nse: NotificationService)] = [] + private var droppedNotifications: [(entityId: ChatId, ntf: NSENotificationData)] = [] + @inline(__always) + private init() {} // only shared instance can be used + + @inline(__always) func newThread() -> UUID { - NSEThreads.queue.sync { + queue.sync { let (_, t) = allThreads.insert(UUID()) return t } } - func startThread(_ t: UUID) { - NSEThreads.queue.sync { + @inline(__always) + func startThread(_ t: UUID, _ service: NotificationService) { + queue.sync { if allThreads.contains(t) { - _ = activeThreads.insert(t) + activeThreads.append((t, service)) } else { logger.warning("NotificationService startThread: thread \(t) was removed before it started") } } } + // atomically: + // - checks that passed NSE instance can start processing passed notification entity, + // - adds it to the passed NSE instance, + // - marks as started, if no other NSE instance is processing it. + // Making all these steps atomic prevents a race condition between threads when both will be added and none will be started + @inline(__always) + func startEntity(_ nse: NotificationService, _ ntfEntity: NotificationEntity) -> Bool { + queue.sync { + // checking that none of activeThreads with another NSE instance processes the same entity and is not ready + let canStart = !activeThreads.contains(where: { (tId, otherNSE) in + tId != nse.threadId + && otherNSE.notificationEntities.contains(where: { (id, otherEntity) in + id == ntfEntity.entityId + && otherEntity.expectedMsg != nil + }) + }) + // atomically add entity to passed NSE instance + let id = ntfEntity.entityId + nse.notificationEntities[id] = ntfEntity + if canStart { + // and set as started, so it cannot be chosen to start by another NSE entity in nextThread + nse.notificationEntities[id]?.startedProcessingNewMsgs = true + } + return canStart + } + } + + @inline(__always) + func addDroppedNtf(_ id: ChatId, _ ntf: NSENotificationData) { + queue.sync { droppedNotifications.append((id, ntf)) } + } + + // atomically remove and return first dropped notification for the passed entity + @inline(__always) + func takeDroppedNtf(_ ntfEntity: NotificationEntity) -> (entityId: ChatId, ntf: NSENotificationData)? { + queue.sync { + if droppedNotifications.isEmpty { + nil + } else if let i = droppedNotifications.firstIndex(where: { (id, _) in id == ntfEntity.entityId }) { + droppedNotifications.remove(at: i) + } else { + nil + } + } + } + + // passes notification for processing to NSE instance chosen by rcvEntityThread + @inline(__always) + func processNotification(_ id: ChatId, _ ntf: NSENotificationData) async -> Void { + if let (nse, ntfEntity, expectedMsg) = rcvEntityThread(id, ntf) { + logger.debug("NotificationService processNotification \(id): found nse thread expecting message") + if nse.processReceivedNtf(ntfEntity, expectedMsg, ntf) { + nse.finalizeEntity(id) + } + } + } + + // atomically: + // - chooses active NSE instance that is ready to process notifications and expects message for passed entity ID + // - returns all dependencies for processing (notification entity and expected message) + // - adds notification to droppedNotifications if no ready NSE instance is found for the entity + @inline(__always) + private func rcvEntityThread(_ id: ChatId, _ ntf: NSENotificationData) -> (NotificationService, NotificationEntity, NtfMsgInfo)? { + queue.sync { + // this selects the earliest thread that: + // 1) has this connection entity in nse.notificationEntitites + // 2) has not completed processing messages for this connection entity (not ready) + let r = activeThreads.lazy.compactMap({ (_, nse) in + let ntfEntity = nse.notificationEntities[id] + return if let ntfEntity, let expectedMsg = ntfEntity.expectedMsg, ntfEntity.shouldProcessNtf { + (nse, ntfEntity, expectedMsg) + } else { + nil + } + }).first + if r == nil { droppedNotifications.append((id, ntf)) } + return r + } + } + + // Atomically mark entity in the passed NSE instance as not expecting messages, + // and signal the next NSE instance with this entity to start its processing. + @inline(__always) + func signalNextThread(_ nse: NotificationService, _ id: ChatId) { + queue.sync { + nse.notificationEntities[id]?.expectedMsg = nil + nse.notificationEntities[id]?.shouldProcessNtf = false + let next = activeThreads.first(where: { (_, nseNext) in + if let ntfEntity = nseNext.notificationEntities[id] { + ntfEntity.expectedMsg != nil && !ntfEntity.startedProcessingNewMsgs + } else { + false + } + }) + if let (tNext, nseNext) = next { + if let t = nse.threadId { logger.debug("NotificationService thread \(t): signalNextThread: signal next thread \(tNext) for entity \(id)") } + nseNext.notificationEntities[id]?.startedProcessingNewMsgs = true + nseNext.notificationEntities[id]?.semaphore.signal() + } + } + } + + @inline(__always) func endThread(_ t: UUID) -> Bool { - NSEThreads.queue.sync { - let tActive = activeThreads.remove(t) + queue.sync { + let tActive: UUID? = if let index = activeThreads.firstIndex(where: { $0.0 == t }) { + activeThreads.remove(at: index).0 + } else { + nil + } let t = allThreads.remove(t) if tActive != nil && activeThreads.isEmpty { return true @@ -182,50 +226,102 @@ class NSEThreads { } } + @inline(__always) var noThreads: Bool { allThreads.isEmpty } } +// NotificationEntity is a processing state for notifications from a single connection entity (message queue). +// Each NSE instance within NSE process can have more than one NotificationEntity. +// NotificationEntities of an NSE instance are processed concurrently, as messages arrive in any order. +// NotificationEntities for the same connection across multiple NSE instances (NSEThreads) are processed sequentially, so that the earliest NSE instance receives the earliest messages. +// The reason for this complexity is to process all required messages within allotted 30 seconds, +// accounting for the possibility that multiple notifications may be delivered concurrently. +struct NotificationEntity { + var ntfConn: NtfConn + var entityId: ChatId + + // expectedMsg == nil means that entity already has the best attempt to deliver, and no more messages are expected. + // It happens when: + // - the user is muted (set to nil in mkNotificationEntity) + // - apiGetNtfConns returns that there are no new messages (msgId in notification matches previously received), + // - messaging server fails to respond or replies that there are no messages (apiGetConnNtfMessages / getConnNtfMessage), + // - the message is received with the correct ID or timestamp (set to nil in signalNextThread). + var expectedMsg: NtfMsgInfo? + var allowedGetNextAttempts: Int = 3 + var msgBestAttemptNtf: NSENotificationData + + // startedProcessingNewMsgs determines that the entity stared processing events once it processed dropped notifications. + // It remains true when shouldProcessNtf is set to false, to prevent NSE from being chosen as the next for the entity. + // It is atomically set to true by startThead or by nextThread + var startedProcessingNewMsgs: Bool = false + + // shouldProcessNtf determines that NSE should process events for this entity, + // it is atomically set: + // - to true in processDroppedNotifications in case dropped notification is not chosen for delivery, and more messages are needed. + // - to false in nextThread + var shouldProcessNtf: Bool = false + + // this semaphone is used to wait for another NSE instance processing events for the same entity + var semaphore: DispatchSemaphore = DispatchSemaphore(value: 0) + + var connMsgReq: ConnMsgReq? { + if let expectedMsg { + ConnMsgReq(msgConnId: ntfConn.agentConnId, msgDbQueueId: ntfConn.agentDbQueueId, msgTs: expectedMsg.msgTs) + } else { + nil + } + } +} + // Notification service extension creates a new instance of the class and calls didReceive for each notification. // Each didReceive is called in its own thread, but multiple calls can be made in one process, and, empirically, there is never // more than one process of notification service extension exists at a time. // Soon after notification service delivers the last notification it is either suspended or terminated. class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? - var bestAttemptNtf: NSENotification? + // served as notification if no message attempts (msgBestAttemptNtf) could be produced + var serviceBestAttemptNtf: UNMutableNotificationContent? var badgeCount: Int = 0 // thread is added to allThreads here - if thread did not start chat, // chat does not need to be suspended but NSE state still needs to be set to "suspended". var threadId: UUID? = NSEThreads.shared.newThread() - var receiveEntityId: String? - var cancelRead: (() -> Void)? + var notificationEntities: Dictionary = [:] // key is entityId var appSubscriber: AppSubscriber? var returnedSuspension = false override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { logger.debug("DEBUGGING: NotificationService.didReceive") - let ntf = if let ntf_ = request.content.mutableCopy() as? UNMutableNotificationContent { ntf_ } else { UNMutableNotificationContent() } - setBestAttemptNtf(ntf) + let receivedNtf = if let ntf_ = request.content.mutableCopy() as? UNMutableNotificationContent { ntf_ } else { UNMutableNotificationContent() } + setServiceBestAttemptNtf(receivedNtf) self.contentHandler = contentHandler registerGroupDefaults() let appState = appStateGroupDefault.get() logger.debug("NotificationService: app is \(appState.rawValue)") switch appState { case .stopped: +// Use this block to debug notificaitons delivery in CLI, with "ejected" database and stopped chat +// if let nrData = ntfRequestData(request) { +// logger.debug("NotificationService get notification connections: /_ntf conns \(nrData.nonce) \(nrData.encNtfInfo)") +// contentHandler(receivedNtf) +// return; +// } setBadgeCount() - setBestAttemptNtf(createAppStoppedNtf()) - deliverBestAttemptNtf() + contentHandler(createAppStoppedNtf(badgeCount)) case .suspended: - setBadgeCount() - receiveNtfMessages(request, contentHandler) + setExpirationTimer() + receiveNtfMessages(request) case .suspending: - setBadgeCount() + // while application is suspending, the current instance will be waiting + setExpirationTimer() Task { let state: AppState = await withCheckedContinuation { cont in + // this subscriber uses message delivery via NSFileCoordinator to communicate between the app and NSE appSubscriber = appStateSubscriber { s in if s == .suspended { appSuspension(s) } } + // this is a fallback timeout, in case message from the app does not arrive DispatchQueue.global().asyncAfter(deadline: .now() + Double(appSuspendTimeout) + 1) { logger.debug("NotificationService: appSuspension timeout") appSuspension(appStateGroupDefault.get()) @@ -241,122 +337,304 @@ class NotificationService: UNNotificationServiceExtension { } } logger.debug("NotificationService: app state is now \(state.rawValue)") - if state.inactive { - receiveNtfMessages(request, contentHandler) + if state.inactive && self.contentHandler != nil { + receiveNtfMessages(request) } else { - deliverBestAttemptNtf() + contentHandler(receivedNtf) } } - default: - deliverBestAttemptNtf() + case .active: contentHandler(receivedNtf) + case .activating: contentHandler(receivedNtf) + case .bgRefresh: contentHandler(receivedNtf) } } - func receiveNtfMessages(_ request: UNNotificationRequest, _ contentHandler: @escaping (UNNotificationContent) -> Void) { + // This timer compensates for the scenarios when serviceExtensionTimeWillExpire does not fire at all. + // It is not clear why in some cases it does not fire, possibly it is a bug, + // or it depends on what the current thread is doing at the moment. + // If notification is not delivered and not cancelled, no further notifications will be processed. + @inline(__always) + private func setExpirationTimer() -> Void { + DispatchQueue.main.asyncAfter(deadline: .now() + 30) { + self.deliverBestAttemptNtf(urgent: true) + } + } + + @inline(__always) + private func ntfRequestData(_ request: UNNotificationRequest) -> (nonce: String, encNtfInfo: String)? { + if let ntfData = request.content.userInfo["notificationData"] as? [AnyHashable : Any], + let nonce = ntfData["nonce"] as? String, + let encNtfInfo = ntfData["message"] as? String { + (nonce, encNtfInfo) + } else { + nil + } + } + + // This function triggers notification message delivery for connection entities referenced in the notification. + // Notification may reference multiple connection entities (message queues) in order to compensate for Apple servers + // only delivering the latest notification, so it allows receiving messages from up to 6 contacts and groups from a + // single notification. This aggregation is handled by a notification server and is delivered via APNS servers in + // e2e encrypted envelope, and the app core prevents duplicate processing by keeping track of the last processed message. + + // The process steps: + // 0. apiGetConnNtfMessages or getConnNtfMessage get messages from the server for passed connection entities. + // We don't know in advance which chat events will be delivered from app core for a given notification, + // it may be a message, but it can also be contact request, various protocol confirmations, calls, etc., + // this function only returns metadata for the expected chat events. + // This metadata is correlated with .ntfMessage core event / .msgInfo notification marker - + // this marker allows determining when some message completed processing. + // 1. receiveMessages: singleton loop receiving events from core. + // 2. receivedMsgNtf: maps core events to notification events. + // 3. NSEThreads.shared.processNotification: chooses which notification service instance in the current process should process notification. + // While most of the time we observe that notifications are delivered sequentially, nothing in the documentation confirms it is sequential, + // and from various sources it follows that each instance executes in its own thread, so concurrency is expected. + // 4. processReceivedNtf: one of the instances of NSE processes notification event, deciding whether to request further messages + // for a given connection entity (via getConnNtfMessage) or that the correct message was received and notification can be delivered (deliverBestAttemptNtf). + // It is based on .msgInfo markers that indicate that message with a given timestamp was processed. + // 5. deliverBestAttemptNtf: is called multiple times, once each connection receives enough messages (based on .msgInfo marker). + // If further messages are expected, this function does nothing (unless it is called with urgent flag from timeout/expiration handlers). + func receiveNtfMessages(_ request: UNNotificationRequest) { logger.debug("NotificationService: receiveNtfMessages") if case .documents = dbContainerGroupDefault.get() { deliverBestAttemptNtf() return } - let userInfo = request.content.userInfo - if let ntfData = userInfo["notificationData"] as? [AnyHashable : Any], - let nonce = ntfData["nonce"] as? String, - let encNtfInfo = ntfData["message"] as? String, - // check it here again + if let nrData = ntfRequestData(request), + // Check that the app is still inactive before starting the core. appStateGroupDefault.get().inactive { // thread is added to activeThreads tracking set here - if thread started chat it needs to be suspended - if let t = threadId { NSEThreads.shared.startThread(t) } + guard let t = threadId else { return } + NSEThreads.shared.startThread(t, self) let dbStatus = startChat() + // If database is opened successfully, get the list of connection entities (group members, contacts) + // that are referenced in the encrypted notification metadata. if case .ok = dbStatus, - let ntfInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) { - logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessages.count))") - if let connEntity = ntfInfo.connEntity_ { - setBestAttemptNtf( - ntfInfo.ntfsEnabled - ? .nse(createConnectionEventNtf(ntfInfo.user, connEntity)) - : .empty - ) - if let id = connEntity.id { - receiveEntityId = id - NtfStreamSemaphores.shared.waitForStream(id) - if receiveEntityId != nil { - Task { - logger.debug("NotificationService: receiveNtfMessages: in Task, connEntity id \(id)") - await PendingNtfs.shared.createStream(id) - await PendingNtfs.shared.readStream(id, for: self, ntfInfo: ntfInfo) - deliverBestAttemptNtf() + let ntfConns = apiGetNtfConns(nonce: nrData.nonce, encNtfInfo: nrData.encNtfInfo) { + logger.debug("NotificationService: receiveNtfMessages: apiGetNtfConns ntfConns count = \(ntfConns.count)") + // uncomment localDisplayName in ConnectionEntity + // logger.debug("NotificationService: receiveNtfMessages: apiGetNtfConns ntfConns \(String(describing: ntfConns.map { $0.connEntity.localDisplayName }))") + + // Prepare expected messages - they will be delivered to the reception loop in this chain: + // They are atomically added to the instance notificationEntities inside msgReqs loop, to avoid any race conditions. + let ntfEntities = ntfConns.compactMap(mkNotificationEntity) + + // collect notification message requests for all connection entities + let msgReqs: [(chatId: String, connMsgReq: ConnMsgReq)] = ntfEntities.compactMap { ntfEntity -> (chatId: String, connMsgReq: ConnMsgReq)? in + // No need to request messages for connection entities that are "ready", + // e.g. for muted users or when the message is not expected based on notification. + let id = ntfEntity.entityId + if let expectedMsg = ntfEntity.expectedMsg { + if NSEThreads.shared.startEntity(self, ntfEntity) { // atomically checks and adds ntfEntity to NSE + // process any notifications "postponed" by the previous instance + let completed = processDroppedNotifications(ntfEntity, expectedMsg) + return if !completed, let connMsgReq = notificationEntities[id]?.connMsgReq { + (id, connMsgReq) + } else { + nil } + } else { + // wait for another instance processing the same connection entity + logger.debug("NotificationService thread \(t, privacy: .private): receiveNtfMessages: entity \(id, privacy: .private) waiting on semaphore") + // this semaphore will be released by signalNextThread function, that looks up the instance + // waiting for the connection entity via activeThreads in NSEThreads + notificationEntities[id]?.semaphore.wait() + logger.debug("NotificationService thread \(t, privacy: .private): receiveNtfMessages: entity \(id, privacy: .private) proceeding after semaphore") + Task { + // process any notifications "postponed" by the previous instance + let completed = processDroppedNotifications(ntfEntity, expectedMsg) + // Request messages from the server for this connection entity. + // It triggers event delivery to receiveMessages loop (see above). + if !completed, let connMsgReq = notificationEntities[id]?.connMsgReq, + let rcvMsg = getConnNtfMessage(connMsgReq: connMsgReq), + rcvMsg.noMsg { + // if server returns error or "no message", deliver what we have for this connection entity. + finalizeEntity(id) // also releases any waiting threads for this entity + } + } + return nil + } + } else { // no expected message + notificationEntities[id] = ntfEntity + return nil + } + } + + // Request messages for all connection entities that were not used by other instances. + // It triggers event delivery to receiveMessages loop (see above). + if !msgReqs.isEmpty, + let rcvMsgs = apiGetConnNtfMessages(connMsgReqs: msgReqs.map { $0.connMsgReq }) { + for i in 0 ..< min(msgReqs.count, rcvMsgs.count) { // a sanity check, API always returns the same size + if rcvMsgs[i].noMsg { + // mark entity as ready if there are no message on the server (or on error) + finalizeEntity(msgReqs[i].chatId) } - return } } } else if let dbStatus = dbStatus { - setBestAttemptNtf(createErrorNtf(dbStatus)) + setServiceBestAttemptNtf(createErrorNtf(dbStatus, badgeCount)) } } + // try to deliver the best attempt before exiting deliverBestAttemptNtf() } + @inline(__always) + func mkNotificationEntity(ntfConn: NtfConn) -> NotificationEntity? { + if let rcvEntityId = ntfConn.connEntity.id { + // don't receive messages for muted user profile + let expectedMsg: NtfMsgInfo? = if ntfConn.user.showNotifications { ntfConn.expectedMsg_ } else { nil } + return NotificationEntity( + ntfConn: ntfConn, + entityId: rcvEntityId, + expectedMsg: expectedMsg, + msgBestAttemptNtf: defaultBestAttemptNtf(ntfConn) + ) + } + return nil + } + + // Processes notifications received and postponed by the previous NSE instance + func processDroppedNotifications(_ ntfEntity: NotificationEntity, _ expectedMsg: NtfMsgInfo) -> Bool { + var completed = false + while !completed { + if let dropped = NSEThreads.shared.takeDroppedNtf(ntfEntity) { + completed = processReceivedNtf(ntfEntity, expectedMsg, dropped.ntf) + } else { + break + } + } + if completed { + finalizeEntity(ntfEntity.entityId) + } else { + notificationEntities[ntfEntity.entityId]?.shouldProcessNtf = true + } + return completed + } + override func serviceExtensionTimeWillExpire() { logger.debug("DEBUGGING: NotificationService.serviceExtensionTimeWillExpire") deliverBestAttemptNtf(urgent: true) } + @inline(__always) + var expectingMoreMessages: Bool { + notificationEntities.contains { $0.value.expectedMsg != nil } + } + + // processReceivedNtf returns "completed" - true when no more messages for the passed entity should be processed by the current NSE instance. + // This is used to call finalizeEntity(id) and by processDroppedNotifications to decide if further processing is needed. + func processReceivedNtf(_ ntfEntity: NotificationEntity, _ expectedMsg: NtfMsgInfo, _ ntf: NSENotificationData) -> Bool { + let id = ntfEntity.entityId + if case let .msgInfo(info) = ntf { + if info.msgId == expectedMsg.msgId { + // The message for this instance is processed, no more expected, deliver. + logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): expected") + return true + } else if let msgTs = info.msgTs_, msgTs > expectedMsg.msgTs { + // Otherwise check timestamp - if it is after the currently expected timestamp, preserve .msgInfo marker for the next instance. + logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): unexpected msgInfo, let other instance to process it, stopping this one") + NSEThreads.shared.addDroppedNtf(id, ntf) + return true + } else if ntfEntity.allowedGetNextAttempts > 0, let connMsgReq = ntfEntity.connMsgReq { + // Otherwise this instance expects more messages, and still has allowed attempts - + // request more messages with getConnNtfMessage. + logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): unexpected msgInfo, get next message") + notificationEntities[id]?.allowedGetNextAttempts -= 1 + let receivedMsg = getConnNtfMessage(connMsgReq: connMsgReq) + if case let .info(msg) = receivedMsg, let msg { + // Server delivered message, it will be processed in the loop - see the comments in receiveNtfMessages. + logger.debug("NotificationService processNtf, on getConnNtfMessage: msgInfo msgId = \(info.msgId, privacy: .private), receivedMsg msgId = \(msg.msgId, privacy: .private)") + return false + } else { + // Server reported no messages or error, deliver what we have. + logger.debug("NotificationService processNtf, on getConnNtfMessage: msgInfo msgId = \(info.msgId, privacy: .private): no next message, deliver best attempt") + return true + } + } else { + // Current instance needs more messages, but ran out of attempts - deliver what we have. + logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): unknown message, let other instance to process it") + return true + } + } else if ntfEntity.ntfConn.user.showNotifications { + // This is the notification event for the user with enabled notifications. + logger.debug("NotificationService processNtf: setting best attempt") + if ntf.notificationEvent != nil { + setBadgeCount() + } + // If previous "best attempt" is not a call, or if the current notification is a call, replace best attempt. + // NOTE: we are delaying it until notification marker to make sure we are not delivering stale calls that can't be connected. + // A better logic could be to check whether we have a call in the best attempt while processing .msgInfo marker above. + // If the best attempt is a call, and its marker is received, and the call is recent (e.g., the last 30 seconds), it would deliver at once, + // instead of requesting further messages. + if ntfEntity.msgBestAttemptNtf.callInvitation == nil || ntf.callInvitation != nil { + notificationEntities[id]?.msgBestAttemptNtf = ntf + } // otherwise keep call as best attempt + return false + } else { + // We should not get to this branch, as notifications are not delivered for muted users. + return true + } + } + + func finalizeEntity(_ entityId: ChatId) { + if let t = threadId { logger.debug("NotificationService thread \(t): entityReady: entity \(entityId)") } + NSEThreads.shared.signalNextThread(self, entityId) + deliverBestAttemptNtf() + } + func setBadgeCount() { badgeCount = ntfBadgeCountGroupDefault.get() + 1 ntfBadgeCountGroupDefault.set(badgeCount) } - func setBestAttemptNtf(_ ntf: UNMutableNotificationContent) { - setBestAttemptNtf(.nse(ntf)) - } - - func setBestAttemptNtf(_ ntf: NSENotification) { - logger.debug("NotificationService.setBestAttemptNtf") - if case let .nse(notification) = ntf { - notification.badge = badgeCount as NSNumber - bestAttemptNtf = .nse(notification) - } else { - bestAttemptNtf = ntf - } + @inline(__always) + func setServiceBestAttemptNtf(_ ntf: UNMutableNotificationContent) { + logger.debug("NotificationService.setServiceBestAttemptNtf") + serviceBestAttemptNtf = ntf } private func deliverBestAttemptNtf(urgent: Bool = false) { - logger.debug("NotificationService.deliverBestAttemptNtf") - if let cancel = cancelRead { - cancelRead = nil - cancel() + logger.debug("NotificationService.deliverBestAttemptNtf urgent: \(urgent) expectingMoreMessages: \(self.expectingMoreMessages)") + if let handler = contentHandler, urgent || !expectingMoreMessages { + if urgent { + contentHandler = nil + } + logger.debug("NotificationService.deliverBestAttemptNtf") + // stop processing other messages + for (key, _) in notificationEntities { + notificationEntities[key]?.shouldProcessNtf = false + } + + let suspend: Bool + if let t = threadId { + threadId = nil + suspend = NSEThreads.shared.endThread(t) && NSEThreads.shared.noThreads + } else { + suspend = false + } + deliverCallkitOrNotification(urgent: urgent, suspend: suspend, handler: handler) } - if let id = receiveEntityId { - receiveEntityId = nil - NtfStreamSemaphores.shared.signalStreamReady(id) - } - let suspend: Bool - if let t = threadId { - threadId = nil - suspend = NSEThreads.shared.endThread(t) && NSEThreads.shared.noThreads - } else { - suspend = false - } - deliverCallkitOrNotification(urgent: urgent, suspend: suspend) } - private func deliverCallkitOrNotification(urgent: Bool, suspend: Bool = false) { - if case .callkit = bestAttemptNtf { + @inline(__always) + private func deliverCallkitOrNotification(urgent: Bool, suspend: Bool = false, handler: @escaping (UNNotificationContent) -> Void) { + let callInv = notificationEntities.lazy.compactMap({ $0.value.msgBestAttemptNtf.callInvitation }).first + if callInv != nil && useCallKit() { logger.debug("NotificationService.deliverCallkitOrNotification: will suspend, callkit") + // suspending NSE even though there may be other notifications + // to allow the app to process callkit call if urgent { - // suspending NSE even though there may be other notifications - // to allow the app to process callkit call suspendChat(0) - deliverNotification() + deliverNotification(handler, callInv) } else { - // suspending NSE with delay and delivering after the suspension + // when not "urgent", suspending NSE with delay and delivering after the suspension // because pushkit notification must be processed without delay - // to avoid app termination + // to avoid app termination. DispatchQueue.global().asyncAfter(deadline: .now() + fastNSESuspendSchedule.delay) { suspendChat(fastNSESuspendSchedule.timeout) DispatchQueue.global().asyncAfter(deadline: .now() + Double(fastNSESuspendSchedule.timeout)) { - self.deliverNotification() + self.deliverNotification(handler, callInv) } } } @@ -375,38 +653,119 @@ class NotificationService: UNNotificationServiceExtension { } } } - deliverNotification() + deliverNotification(handler, callInv) } } - private func deliverNotification() { - if let handler = contentHandler, let ntf = bestAttemptNtf { + private func deliverNotification(_ handler: @escaping (UNNotificationContent) -> Void, _ callInv: RcvCallInvitation?) { + if let serviceNtf = serviceBestAttemptNtf { + serviceBestAttemptNtf = nil contentHandler = nil - bestAttemptNtf = nil - let deliver: (UNMutableNotificationContent?) -> Void = { ntf in - let useNtf = if let ntf = ntf { - appStateGroupDefault.get().running ? UNMutableNotificationContent() : ntf + if let callInv { + if useCallKit() { + logger.debug("NotificationService reportNewIncomingVoIPPushPayload for \(callInv.contact.id)") + CXProvider.reportNewIncomingVoIPPushPayload([ + "displayName": callInv.contact.displayName, + "contactId": callInv.contact.id, + "callUUID": callInv.callUUID ?? "", + "media": callInv.callType.media.rawValue, + "callTs": callInv.callTs.timeIntervalSince1970 + ]) { error in + logger.debug("reportNewIncomingVoIPPushPayload result: \(error)") + handler(error == nil ? UNMutableNotificationContent() : createCallInvitationNtf(callInv, self.badgeCount)) + } } else { - UNMutableNotificationContent() + handler(createCallInvitationNtf(callInv, badgeCount)) } - handler(useNtf) + } else if notificationEntities.isEmpty { + handler(serviceNtf) + } else { + handler(prepareNotification()) } + } + } + + @inline(__always) + private func prepareNotification() -> UNMutableNotificationContent { + // uncomment localDisplayName in ConnectionEntity + // let conns = self.notificationEntities.compactMap { $0.value.ntfConn.connEntity.localDisplayName } + // logger.debug("NotificationService prepareNotification for \(String(describing: conns))") + let ntfs = notificationEntities.compactMap { $0.value.msgBestAttemptNtf.notificationEvent } + let newMsgNtfs = ntfs.compactMap({ $0.newMsgNtf }) + let useNtfs = if newMsgNtfs.isEmpty { ntfs } else { newMsgNtfs } + return createNtf(useNtfs) + + func createNtf(_ ntfs: [NSENotificationData]) -> UNMutableNotificationContent { + logger.debug("NotificationService prepareNotification: \(ntfs.count) events") + return switch ntfs.count { + case 0: UNMutableNotificationContent() // used to mute notifications that did not unsubscribe yet + case 1: ntfs[0].notificationContent(badgeCount) + default: createJointNtf(ntfs) + } + } + } + + // NOTE: this can be improved when there are two or more connection entity events when no messages were delivered. + // Possibly, it is better to postpone this improvement until message priority is added to prevent notifications in muted groups, + // unless it is a mention, a reply or some other high priority message marked for notification delivery. + @inline(__always) + private func createJointNtf(_ ntfs: [NSENotificationData]) -> UNMutableNotificationContent { + let previewMode = ntfPreviewModeGroupDefault.get() + logger.debug("NotificationService.createJointNtf ntfs: \(ntfs.count)") + let (userId, chatsNames) = newMsgsChatsNames(ntfs) + if !chatsNames.isEmpty, let userId { + let body = if previewMode == .hidden { + String.localizedStringWithFormat(NSLocalizedString("From %d chat(s)", comment: "notification body"), chatsNames.count) + } else { + String.localizedStringWithFormat(NSLocalizedString("From: %@", comment: "notification body"), newMsgsChatsNamesStr(chatsNames)) + } + return createNotification( + categoryIdentifier: ntfCategoryManyEvents, + title: NSLocalizedString("New messages", comment: "notification"), + body: body, + userInfo: ["userId": userId], + badgeCount: badgeCount + ) + } else { + return createNotification( + categoryIdentifier: ntfCategoryManyEvents, + title: NSLocalizedString("New events", comment: "notification"), + body: String.localizedStringWithFormat(NSLocalizedString("%d new events", comment: "notification body"), ntfs.count), + badgeCount: badgeCount + ) + } + } + + @inline(__always) + private func newMsgsChatsNames(_ ntfs: [NSENotificationData]) -> (Int64?, [String]) { + var seenChatIds = Set() + var chatsNames: [String] = [] + var userId: Int64? + for ntf in ntfs { switch ntf { - case let .nse(content): deliver(content) - case let .callkit(invitation): - logger.debug("NotificationService reportNewIncomingVoIPPushPayload for \(invitation.contact.id)") - CXProvider.reportNewIncomingVoIPPushPayload([ - "displayName": invitation.contact.displayName, - "contactId": invitation.contact.id, - "media": invitation.callType.media.rawValue - ]) { error in - logger.debug("reportNewIncomingVoIPPushPayload result: \(error)") - deliver(error == nil ? nil : createCallInvitationNtf(invitation)) + case let .messageReceived(user, chat, _): + if seenChatIds.isEmpty { userId = user.userId } + if !seenChatIds.contains(chat.id) { + seenChatIds.insert(chat.id) + chatsNames.append(chat.chatViewName) } - case .empty: deliver(nil) // used to mute notifications that did not unsubscribe yet - case .msgInfo: deliver(nil) // unreachable, the best attempt is never set to msgInfo + default: () } } + return (userId, chatsNames) + } + + @inline(__always) + private func newMsgsChatsNamesStr(_ names: [String]) -> String { + return switch names.count { + case 1: names[0] + case 2: "\(names[0]) and \(names[1])" + case 3: "\(names[0] + ", " + names[1]) and \(names[2])" + default: + names.count > 3 + ? "\(names[0]), \(names[1]) and \(names.count - 2) other chats" + : "" + } } } @@ -415,9 +774,8 @@ class NSEChatState { static let shared = NSEChatState() private var value_ = NSEState.created - var value: NSEState { - value_ - } + @inline(__always) + var value: NSEState { value_ } func set(_ state: NSEState) { nseStateGroupDefault.set(state) @@ -425,7 +783,7 @@ class NSEChatState { value_ = state } - init() { + private init() { // This is always set to .created state, as in case previous start of NSE crashed in .active state, it is stored correctly. // Otherwise the app will be activating slower set(.created) @@ -449,6 +807,16 @@ func appStateSubscriber(onState: @escaping (AppState) -> Void) -> AppSubscriber } } +let seSubscriber = seMessageSubscriber { + switch $0 { + case let .state(state): + if state == .sendingMessage && NSEChatState.shared.value.canSuspend { + logger.debug("NotificationService: seSubscriber app state \(state.rawValue), suspending") + suspendChat(fastNSESuspendSchedule.timeout) + } + } +} + var receiverStarted = false let startLock = DispatchSemaphore(value: 1) let suspendLock = DispatchSemaphore(value: 1) @@ -463,7 +831,7 @@ func startChat() -> DBMigrationResult? { startLock.wait() defer { startLock.signal() } - + if hasChatCtrl() { return switch NSEChatState.shared.value { case .created: doStartChat() @@ -493,11 +861,10 @@ func doStartChat() -> DBMigrationResult? { let state = NSEChatState.shared.value NSEChatState.shared.set(.starting) if let user = apiGetActiveUser() { - logger.debug("NotificationService active user \(String(describing: user))") + logger.debug("NotificationService active user \(user.displayName)") do { try setNetworkConfig(networkConfig) - try apiSetTempFolder(tempFolder: getTempFilesDirectory().path) - try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path) + try apiSetAppFilePaths(filesFolder: getAppFilesDirectory().path, tempFolder: getTempFilesDirectory().path, assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path) try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get()) // prevent suspension while starting chat suspendLock.wait() @@ -572,7 +939,7 @@ func chatSuspended() { } // A single loop is used per Notification service extension process to receive and process all messages depending on the NSE state -// If the extension is not active yet, or suspended/suspending, or the app is running, the notifications will no be received. +// If the extension is not active yet, or suspended/suspending, or the app is running, the notifications will not be received. func receiveMessages() async { logger.debug("NotificationService receiveMessages") while true { @@ -587,13 +954,18 @@ func receiveMessages() async { } func receiveMsg() async { - if let msg = await chatRecvMsg() { + switch await chatRecvMsg() { + case let .result(msg): logger.debug("NotificationService receiveMsg: message") if let (id, ntf) = await receivedMsgNtf(msg) { logger.debug("NotificationService receiveMsg: notification") - await PendingNtfs.shared.createStream(id) - await PendingNtfs.shared.writeStream(id, ntf) + await NSEThreads.shared.processNotification(id, ntf) } + case let .error(err): + logger.error("NotificationService receivedMsgNtf error: \(String(describing: err))") + case let .invalid(type, _): + logger.error("NotificationService receivedMsgNtf invalid: \(type)") + case .none: () } } @@ -603,36 +975,46 @@ func receiveMessages() async { } } -func chatRecvMsg() async -> ChatResponse? { +func chatRecvMsg() async -> APIResult? { await withCheckedContinuation { cont in - let resp = recvSimpleXMsg() + let resp: APIResult? = recvSimpleXMsg() cont.resume(returning: resp) } } private let isInChina = SKStorefront().countryCode == "CHN" + +@inline(__always) private func useCallKit() -> Bool { !isInChina && callKitEnabledGroupDefault.get() } -func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? { +@inline(__always) +func receivedMsgNtf(_ res: NSEChatEvent) async -> (String, NSENotificationData)? { logger.debug("NotificationService receivedMsgNtf: \(res.responseType)") switch res { case let .contactConnected(user, contact, _): - return (contact.id, .nse(createContactConnectedNtf(user, contact))) + return (contact.id, .contactConnected(user, contact)) // case let .contactConnecting(contact): // TODO profile update case let .receivedContactRequest(user, contactRequest): - return (UserContact(contactRequest: contactRequest).id, .nse(createContactRequestNtf(user, contactRequest))) - case let .newChatItem(user, aChatItem): - let cInfo = aChatItem.chatInfo - var cItem = aChatItem.chatItem - if !cInfo.ntfsEnabled { - ntfBadgeCountGroupDefault.set(max(0, ntfBadgeCountGroupDefault.get() - 1)) + return (UserContact(contactRequest: contactRequest).id, .contactRequest(user, contactRequest)) + case let .newChatItems(user, chatItems): + // Received items are created one at a time + if let chatItem = chatItems.first { + let cInfo = chatItem.chatInfo + var cItem = chatItem.chatItem + if let file = cItem.autoReceiveFile() { + cItem = autoReceiveFile(file) ?? cItem + } + let ntf: NSENotificationData = (cInfo.ntfsEnabled(chatItem: cItem) && cItem.showNotification) ? .messageReceived(user, cInfo, cItem) : .noNtf + let chatIdOrMemberId = if case let .groupRcv(groupMember) = chatItem.chatItem.chatDir { + groupMember.id + } else { + chatItem.chatInfo.id + } + return (chatIdOrMemberId, ntf) + } else { + return nil } - if let file = cItem.autoReceiveFile() { - cItem = autoReceiveFile(file) ?? cItem - } - let ntf: NSENotification = cInfo.ntfsEnabled ? .nse(createMessageReceivedNtf(user, cInfo, cItem)) : .empty - return cItem.showNotification ? (aChatItem.chatId, ntf) : nil case let .rcvFileSndCancelled(_, aChatItem, _): cleanupFile(aChatItem) return nil @@ -644,29 +1026,18 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? { cleanupDirectFile(aChatItem) } return nil - case let .sndFileCompleteXFTP(_, aChatItem, _): - cleanupFile(aChatItem) - return nil case let .callInvitation(invitation): // Do not post it without CallKit support, iOS will stop launching the app without showing CallKit - return ( - invitation.contact.id, - useCallKit() ? .callkit(invitation) : .nse(createCallInvitationNtf(invitation)) - ) + return (invitation.contact.id, .callInvitation(invitation)) case let .ntfMessage(_, connEntity, ntfMessage): return if let id = connEntity.id { (id, .msgInfo(ntfMessage)) } else { nil } case .chatSuspended: chatSuspended() return nil - case let .chatError(_, err): - logger.error("NotificationService receivedMsgNtf error: \(String(describing: err))") - return nil - default: - logger.debug("NotificationService receivedMsgNtf ignored event: \(res.responseType)") - return nil } } +@inline(__always) func updateNetCfg() { let newNetConfig = getNetCfg() if newNetConfig != networkConfig { @@ -681,14 +1052,14 @@ func updateNetCfg() { } func apiGetActiveUser() -> User? { - let r = sendSimpleXCmd(.showActiveUser) + let r: APIResult = sendSimpleXCmd(NSEChatCommand.showActiveUser) logger.debug("apiGetActiveUser sendSimpleXCmd response: \(r.responseType)") switch r { - case let .activeUser(user): return user - case .chatCmdError(_, .error(.noActiveUser)): + case let .result(.activeUser(user)): return user + case .error(.error(.noActiveUser)): logger.debug("apiGetActiveUser sendSimpleXCmd no active user") return nil - case let .chatCmdError(_, err): + case let .error(err): logger.debug("apiGetActiveUser sendSimpleXCmd error: \(String(describing: err))") return nil default: @@ -698,75 +1069,93 @@ func apiGetActiveUser() -> User? { } func apiStartChat() throws -> Bool { - let r = sendSimpleXCmd(.startChat(mainApp: false)) + let r: APIResult = sendSimpleXCmd(NSEChatCommand.startChat(mainApp: false, enableSndFiles: false)) switch r { - case .chatStarted: return true - case .chatRunning: return false - default: throw r + case .result(.chatStarted): return true + case .result(.chatRunning): return false + default: throw r.unexpected } } func apiActivateChat() -> Bool { chatReopenStore() - let r = sendSimpleXCmd(.apiActivateChat(restoreChat: false)) - if case .cmdOk = r { return true } + let r: APIResult = sendSimpleXCmd(NSEChatCommand.apiActivateChat(restoreChat: false)) + if case .result(.cmdOk) = r { return true } logger.error("NotificationService apiActivateChat error: \(String(describing: r))") return false } func apiSuspendChat(timeoutMicroseconds: Int) -> Bool { - let r = sendSimpleXCmd(.apiSuspendChat(timeoutMicroseconds: timeoutMicroseconds)) - if case .cmdOk = r { return true } + let r: APIResult = sendSimpleXCmd(NSEChatCommand.apiSuspendChat(timeoutMicroseconds: timeoutMicroseconds)) + if case .result(.cmdOk) = r { return true } logger.error("NotificationService apiSuspendChat error: \(String(describing: r))") return false } -func apiSetTempFolder(tempFolder: String) throws { - let r = sendSimpleXCmd(.setTempFolder(tempFolder: tempFolder)) - if case .cmdOk = r { return } - throw r -} - -func apiSetFilesFolder(filesFolder: String) throws { - let r = sendSimpleXCmd(.setFilesFolder(filesFolder: filesFolder)) - if case .cmdOk = r { return } - throw r +func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) throws { + let r: APIResult = sendSimpleXCmd(NSEChatCommand.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder)) + if case .result(.cmdOk) = r { return } + throw r.unexpected } func apiSetEncryptLocalFiles(_ enable: Bool) throws { - let r = sendSimpleXCmd(.apiSetEncryptLocalFiles(enable: enable)) - if case .cmdOk = r { return } - throw r + let r: APIResult = sendSimpleXCmd(NSEChatCommand.apiSetEncryptLocalFiles(enable: enable)) + if case .result(.cmdOk) = r { return } + throw r.unexpected } -func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? { +func apiGetNtfConns(nonce: String, encNtfInfo: String) -> [NtfConn]? { + guard apiGetActiveUser() != nil else { + logger.debug("NotificationService: no active user") + return nil + } + let r: APIResult = sendSimpleXCmd(NSEChatCommand.apiGetNtfConns(nonce: nonce, encNtfInfo: encNtfInfo)) + if case let .result(.ntfConns(ntfConns)) = r { + logger.debug("NotificationService apiGetNtfConns response ntfConns: \(ntfConns.count) conections") + return ntfConns + } else if case let .error(error) = r { + logger.debug("NotificationService apiGetNtfMessage error response: \(String.init(describing: error))") + } else { + logger.debug("NotificationService apiGetNtfMessage ignored response: \(r.responseType) \(String.init(describing: r))") + } + return nil +} + +func apiGetConnNtfMessages(connMsgReqs: [ConnMsgReq]) -> [RcvNtfMsgInfo]? { guard apiGetActiveUser() != nil else { logger.debug("no active user") return nil } - let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo)) - if case let .ntfMessages(user, connEntity_, msgTs, ntfMessages) = r, let user = user { - logger.debug("apiGetNtfMessage response ntfMessages: \(ntfMessages.count)") - return NtfMessages(user: user, connEntity_: connEntity_, msgTs: msgTs, ntfMessages: ntfMessages) - } else if case let .chatCmdError(_, error) = r { - logger.debug("apiGetNtfMessage error response: \(String.init(describing: error))") - } else { - logger.debug("apiGetNtfMessage ignored response: \(r.responseType) \(String.init(describing: r))") +// logger.debug("NotificationService apiGetConnNtfMessages command: \(NSEChatCommand.apiGetConnNtfMessages(connMsgReqs: connMsgReqs).cmdString)") + logger.debug("NotificationService apiGetConnNtfMessages requests: \(connMsgReqs.count)") + let r: APIResult = sendSimpleXCmd(NSEChatCommand.apiGetConnNtfMessages(connMsgReqs: connMsgReqs)) + if case let .result(.connNtfMessages(msgs)) = r { +// logger.debug("NotificationService apiGetConnNtfMessages responses: \(String(describing: msgs))") + logger.debug("NotificationService apiGetConnNtfMessages responses: total \(msgs.count), expecting messages \(msgs.count { !$0.noMsg }), errors \(msgs.count { $0.isError })") + return msgs } + logger.debug("NotificationService apiGetConnNtfMessages error: \(responseError(r.unexpected))") return nil } +func getConnNtfMessage(connMsgReq: ConnMsgReq) -> RcvNtfMsgInfo? { + let r = apiGetConnNtfMessages(connMsgReqs: [connMsgReq]) + return if let r, r.count > 0 { r[0] } else { nil } +} + func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil) -> AChatItem? { - let r = sendSimpleXCmd(.receiveFile(fileId: fileId, encrypted: encrypted, inline: inline)) - if case let .rcvFileAccepted(_, chatItem) = r { return chatItem } - logger.error("receiveFile error: \(responseError(r))") + let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get() + let r: APIResult = sendSimpleXCmd(NSEChatCommand.receiveFile(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted, inline: inline)) + if case let .result(.rcvFileAccepted(_, chatItem)) = r { return chatItem } + logger.error("receiveFile error: \(responseError(r.unexpected))") return nil } func apiSetFileToReceive(fileId: Int64, encrypted: Bool) { - let r = sendSimpleXCmd(.setFileToReceive(fileId: fileId, encrypted: encrypted)) - if case .cmdOk = r { return } - logger.error("setFileToReceive error: \(responseError(r))") + let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get() + let r: APIResult = sendSimpleXCmd(NSEChatCommand.setFileToReceive(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted)) + if case .result(.cmdOk) = r { return } + logger.error("setFileToReceive error: \(responseError(r.unexpected))") } func autoReceiveFile(_ file: CIFile) -> ChatItem? { @@ -783,18 +1172,32 @@ func autoReceiveFile(_ file: CIFile) -> ChatItem? { } func setNetworkConfig(_ cfg: NetCfg) throws { - let r = sendSimpleXCmd(.apiSetNetworkConfig(networkConfig: cfg)) - if case .cmdOk = r { return } - throw r + let r: APIResult = sendSimpleXCmd(NSEChatCommand.apiSetNetworkConfig(networkConfig: cfg)) + if case .result(.cmdOk) = r { return } + throw r.unexpected } -struct NtfMessages { - var user: User - var connEntity_: ConnectionEntity? - var msgTs: Date? - var ntfMessages: [NtfMsgInfo] - - var ntfsEnabled: Bool { - user.showNotifications && (connEntity_?.ntfsEnabled ?? false) +func defaultBestAttemptNtf(_ ntfConn: NtfConn) -> NSENotificationData { + let user = ntfConn.user + let connEntity = ntfConn.connEntity + return if !user.showNotifications { + .noNtf + } else { + switch ntfConn.connEntity { + case let .rcvDirectMsgConnection(_, contact): + contact?.chatSettings.enableNtfs == .all + ? .connectionEvent(user, connEntity) + : .noNtf + case let .rcvGroupMsgConnection(_, groupInfo, _): + groupInfo.chatSettings.enableNtfs == .all + ? .connectionEvent(user, connEntity) + : .noNtf + case .sndFileConnection: .noNtf + case .rcvFileConnection: .noNtf + case let .userContactConnection(_, userContact): + userContact.groupId == nil + ? .connectionEvent(user, connEntity) + : .noNtf + } } } diff --git a/apps/ios/SimpleX NSE/bg.lproj/Localizable.strings b/apps/ios/SimpleX NSE/bg.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/bg.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/cs.lproj/Localizable.strings b/apps/ios/SimpleX NSE/cs.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/cs.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/de.lproj/InfoPlist.strings b/apps/ios/SimpleX NSE/de.lproj/InfoPlist.strings index 9c675514f4..6cc768efe1 100644 --- a/apps/ios/SimpleX NSE/de.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX NSE/de.lproj/InfoPlist.strings @@ -5,5 +5,5 @@ "CFBundleName" = "SimpleX NSE"; /* Copyright (human-readable) */ -"NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; +"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. All rights reserved."; diff --git a/apps/ios/SimpleX NSE/de.lproj/Localizable.strings b/apps/ios/SimpleX NSE/de.lproj/Localizable.strings new file mode 100644 index 0000000000..ec502c53c6 --- /dev/null +++ b/apps/ios/SimpleX NSE/de.lproj/Localizable.strings @@ -0,0 +1,15 @@ +/* notification body */ +"%d new events" = "%d neue Ereignisse"; + +/* notification body */ +"From %d chat(s)" = "Von %d Chat(s)"; + +/* notification body */ +"From: %@" = "Von: %@"; + +/* notification */ +"New events" = "Neue Ereignisse"; + +/* notification */ +"New messages" = "Neue Nachrichten"; + diff --git a/apps/ios/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX NSE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9c675514f4 --- /dev/null +++ b/apps/ios/SimpleX NSE/en.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX NSE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX NSE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/es.lproj/Localizable.strings b/apps/ios/SimpleX NSE/es.lproj/Localizable.strings new file mode 100644 index 0000000000..685eb3d93d --- /dev/null +++ b/apps/ios/SimpleX NSE/es.lproj/Localizable.strings @@ -0,0 +1,15 @@ +/* notification body */ +"%d new events" = "%d evento(s) nuevo(s)"; + +/* notification body */ +"From %d chat(s)" = "De %d chat(s)"; + +/* notification body */ +"From: %@" = "De: %@"; + +/* notification */ +"New events" = "Eventos nuevos"; + +/* notification */ +"New messages" = "Mensajes nuevos"; + diff --git a/apps/ios/SimpleX NSE/fi.lproj/Localizable.strings b/apps/ios/SimpleX NSE/fi.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/fi.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/fr.lproj/Localizable.strings b/apps/ios/SimpleX NSE/fr.lproj/Localizable.strings new file mode 100644 index 0000000000..999bb3608f --- /dev/null +++ b/apps/ios/SimpleX NSE/fr.lproj/Localizable.strings @@ -0,0 +1,12 @@ +/* notification body */ +"%d new events" = "%d nouveaux événements"; + +/* notification body */ +"From: %@" = "De : %@"; + +/* notification */ +"New events" = "Nouveaux événements"; + +/* notification */ +"New messages" = "Nouveaux messages"; + diff --git a/apps/ios/SimpleX NSE/hu.lproj/Localizable.strings b/apps/ios/SimpleX NSE/hu.lproj/Localizable.strings new file mode 100644 index 0000000000..a6330b93db --- /dev/null +++ b/apps/ios/SimpleX NSE/hu.lproj/Localizable.strings @@ -0,0 +1,15 @@ +/* notification body */ +"%d new events" = "%d új esemény"; + +/* notification body */ +"From %d chat(s)" = "%d csevegésből"; + +/* notification body */ +"From: %@" = "Tőle: %@"; + +/* notification */ +"New events" = "Új események"; + +/* notification */ +"New messages" = "Új üzenetek"; + diff --git a/apps/ios/SimpleX NSE/it.lproj/Localizable.strings b/apps/ios/SimpleX NSE/it.lproj/Localizable.strings new file mode 100644 index 0000000000..a6c1ec215b --- /dev/null +++ b/apps/ios/SimpleX NSE/it.lproj/Localizable.strings @@ -0,0 +1,15 @@ +/* notification body */ +"%d new events" = "%d nuovi eventi"; + +/* notification body */ +"From %d chat(s)" = "Da %d chat"; + +/* notification body */ +"From: %@" = "Da: %@"; + +/* notification */ +"New events" = "Nuovi eventi"; + +/* notification */ +"New messages" = "Nuovi messaggi"; + diff --git a/apps/ios/SimpleX NSE/ja.lproj/Localizable.strings b/apps/ios/SimpleX NSE/ja.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/ja.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/nl.lproj/Localizable.strings b/apps/ios/SimpleX NSE/nl.lproj/Localizable.strings new file mode 100644 index 0000000000..12d1e01f1d --- /dev/null +++ b/apps/ios/SimpleX NSE/nl.lproj/Localizable.strings @@ -0,0 +1,12 @@ +/* notification body */ +"%d new events" = "‐%d nieuwe gebeurtenissen"; + +/* notification body */ +"From: %@" = "Van: %@"; + +/* notification */ +"New events" = "Nieuwe gebeurtenissen"; + +/* notification */ +"New messages" = "Nieuwe berichten"; + diff --git a/apps/ios/SimpleX NSE/pl.lproj/Localizable.strings b/apps/ios/SimpleX NSE/pl.lproj/Localizable.strings new file mode 100644 index 0000000000..3a577620a0 --- /dev/null +++ b/apps/ios/SimpleX NSE/pl.lproj/Localizable.strings @@ -0,0 +1,3 @@ +/* notification body */ +"New messages in %d chats" = "Nowe wiadomości w %d czatach"; + diff --git a/apps/ios/SimpleX NSE/ru.lproj/Localizable.strings b/apps/ios/SimpleX NSE/ru.lproj/Localizable.strings new file mode 100644 index 0000000000..7205b37e7f --- /dev/null +++ b/apps/ios/SimpleX NSE/ru.lproj/Localizable.strings @@ -0,0 +1,12 @@ +/* notification body */ +"%d new events" = "%d новых сообщений"; + +/* notification body */ +"From: %@" = "От: %@"; + +/* notification */ +"New events" = "Новые события"; + +/* notification */ +"New messages" = "Новые сообщения"; + diff --git a/apps/ios/SimpleX NSE/th.lproj/Localizable.strings b/apps/ios/SimpleX NSE/th.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/th.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/tr.lproj/Localizable.strings b/apps/ios/SimpleX NSE/tr.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/tr.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/uk.lproj/Localizable.strings b/apps/ios/SimpleX NSE/uk.lproj/Localizable.strings new file mode 100644 index 0000000000..ceace71e34 --- /dev/null +++ b/apps/ios/SimpleX NSE/uk.lproj/Localizable.strings @@ -0,0 +1,12 @@ +/* notification body */ +"%d new events" = "%d нових подій"; + +/* notification body */ +"From: %@" = "Від: %@"; + +/* notification */ +"New events" = "Нові події"; + +/* notification */ +"New messages" = "Нові повідомлення"; + diff --git a/apps/ios/SimpleX NSE/zh-Hans.lproj/Localizable.strings b/apps/ios/SimpleX NSE/zh-Hans.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/zh-Hans.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/Info.plist b/apps/ios/SimpleX SE/Info.plist new file mode 100644 index 0000000000..2ce1f45040 --- /dev/null +++ b/apps/ios/SimpleX SE/Info.plist @@ -0,0 +1,35 @@ + + + + + NSExtension + + NSExtensionAttributes + + NSExtensionActivationRule + + NSExtensionActivationSupportsText + + NSExtensionActivationSupportsAttachmentsWithMinCount + 0 + NSExtensionActivationSupportsAttachmentsWithMaxCount + 1 + NSExtensionActivationSupportsWebPageWithMaxCount + 1 + NSExtensionActivationSupportsWebURLWithMaxCount + 1 + NSExtensionActivationSupportsFileWithMaxCount + 1 + NSExtensionActivationSupportsImageWithMaxCount + 1 + NSExtensionActivationSupportsMovieWithMaxCount + 1 + + + NSExtensionPointIdentifier + com.apple.share-services + NSExtensionPrincipalClass + ShareViewController + + + diff --git a/apps/ios/SimpleX SE/SEChatState.swift b/apps/ios/SimpleX SE/SEChatState.swift new file mode 100644 index 0000000000..581bff894a --- /dev/null +++ b/apps/ios/SimpleX SE/SEChatState.swift @@ -0,0 +1,39 @@ +// +// SEChatState.swift +// SimpleX SE +// +// Created by User on 18/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +// SEStateGroupDefault must not be used in the share extension directly, only via this singleton +class SEChatState { + static let shared = SEChatState() + private var value_ = seStateGroupDefault.get() + + var value: SEState { + value_ + } + + func set(_ state: SEState) { + seStateGroupDefault.set(state) + sendSEState(state) + value_ = state + } +} + +/// Waits for other processes to set their state to suspended +/// Will wait for maximum of two seconds, since they might not be running +func waitForOtherProcessesToSuspend() async { + let startTime = CFAbsoluteTimeGetCurrent() + while CFAbsoluteTimeGetCurrent() - startTime < 2 { + try? await Task.sleep(nanoseconds: 100 * NSEC_PER_MSEC) + if appStateGroupDefault.get() == .suspended && + nseStateGroupDefault.get() == .suspended { + break + } + } +} diff --git a/apps/ios/SimpleX SE/ShareAPI.swift b/apps/ios/SimpleX SE/ShareAPI.swift new file mode 100644 index 0000000000..3e901c73eb --- /dev/null +++ b/apps/ios/SimpleX SE/ShareAPI.swift @@ -0,0 +1,226 @@ +// +// ShareAPI.swift +// SimpleX SE +// +// Created by User on 15/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import OSLog +import Foundation +import SimpleXChat + +let logger = Logger() + +func apiGetActiveUser() throws -> User? { + let r: APIResult = sendSimpleXCmd(SEChatCommand.showActiveUser) + switch r { + case let .result(.activeUser(user)): return user + case .error(.error(.noActiveUser)): return nil + default: throw r.unexpected + } +} + +func apiStartChat() throws -> Bool { + let r: APIResult = sendSimpleXCmd(SEChatCommand.startChat(mainApp: false, enableSndFiles: true)) + switch r { + case .result(.chatStarted): return true + case .result(.chatRunning): return false + default: throw r.unexpected + } +} + +func apiSetNetworkConfig(_ cfg: NetCfg) throws { + let r: APIResult = sendSimpleXCmd(SEChatCommand.apiSetNetworkConfig(networkConfig: cfg)) + if case .result(.cmdOk) = r { return } + throw r.unexpected +} + +func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) throws { + let r: APIResult = sendSimpleXCmd(SEChatCommand.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder)) + if case .result(.cmdOk) = r { return } + throw r.unexpected +} + +func apiSetEncryptLocalFiles(_ enable: Bool) throws { + let r: APIResult = sendSimpleXCmd(SEChatCommand.apiSetEncryptLocalFiles(enable: enable)) + if case .result(.cmdOk) = r { return } + throw r.unexpected +} + +func apiGetChats(userId: User.ID) throws -> Array { + let r: APIResult = sendSimpleXCmd(SEChatCommand.apiGetChats(userId: userId)) + if case let .result(.apiChats(user: _, chats: chats)) = r { return chats } + throw r.unexpected +} + +func apiSendMessages( + chatInfo: ChatInfo, + composedMessages: [ComposedMessage] +) throws -> [AChatItem] { + let r: APIResult = sendSimpleXCmd( + chatInfo.chatType == .local + ? SEChatCommand.apiCreateChatItems( + noteFolderId: chatInfo.apiId, + composedMessages: composedMessages + ) + : SEChatCommand.apiSendMessages( + type: chatInfo.chatType, + id: chatInfo.apiId, + live: false, + ttl: nil, + composedMessages: composedMessages + ) + ) + if case let .result(.newChatItems(_, chatItems)) = r { + return chatItems + } else { + for composedMessage in composedMessages { + if let filePath = composedMessage.fileSource?.filePath { removeFile(filePath) } + } + throw r.unexpected + } +} + +func apiActivateChat() throws { + chatReopenStore() + let r: APIResult = sendSimpleXCmd(SEChatCommand.apiActivateChat(restoreChat: false)) + if case .result(.cmdOk) = r { return } + throw r.unexpected +} + +func apiSuspendChat(expired: Bool) { + let r: APIResult = sendSimpleXCmd(SEChatCommand.apiSuspendChat(timeoutMicroseconds: expired ? 0 : 3_000000)) + // Block until `chatSuspended` received or 3 seconds has passed + var suspended = false + if case .result(.cmdOk) = r, !expired { + let startTime = CFAbsoluteTimeGetCurrent() + while CFAbsoluteTimeGetCurrent() - startTime < 3 { + let msg: APIResult? = recvSimpleXMsg(messageTimeout: 3_500000) + switch msg { + case .result(.chatSuspended): + suspended = false + break + default: continue + } + } + } + if !suspended { + let _r1: APIResult = sendSimpleXCmd(SEChatCommand.apiSuspendChat(timeoutMicroseconds: 0)) + } + logger.debug("close store") + chatCloseStore() + SEChatState.shared.set(.inactive) +} + +enum SEChatCommand: ChatCmdProtocol { + case showActiveUser + case startChat(mainApp: Bool, enableSndFiles: Bool) + case apiActivateChat(restoreChat: Bool) + case apiSuspendChat(timeoutMicroseconds: Int) + case apiSetNetworkConfig(networkConfig: NetCfg) + case apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) + case apiSetEncryptLocalFiles(enable: Bool) + case apiGetChats(userId: Int64) + case apiCreateChatItems(noteFolderId: Int64, composedMessages: [ComposedMessage]) + case apiSendMessages(type: ChatType, id: Int64, live: Bool, ttl: Int?, composedMessages: [ComposedMessage]) + + var cmdString: String { + switch self { + case .showActiveUser: return "/u" + case let .startChat(mainApp, enableSndFiles): return "/_start main=\(onOff(mainApp)) snd_files=\(onOff(enableSndFiles))" + case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))" + case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)" + case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))" + case let .apiSetAppFilePaths(filesFolder, tempFolder, assetsFolder): + return "/set file paths \(encodeJSON(AppFilePaths(appFilesFolder: filesFolder, appTempFolder: tempFolder, appAssetsFolder: assetsFolder)))" + case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))" + case let .apiGetChats(userId): return "/_get chats \(userId) pcc=on" + case let .apiCreateChatItems(noteFolderId, composedMessages): + let msgs = encodeJSON(composedMessages) + return "/_create *\(noteFolderId) json \(msgs)" + case let .apiSendMessages(type, id, live, ttl, composedMessages): + let msgs = encodeJSON(composedMessages) + let ttlStr = ttl != nil ? "\(ttl!)" : "default" + return "/_send \(ref(type, id)) live=\(onOff(live)) ttl=\(ttlStr) json \(msgs)" + } + } + + func ref(_ type: ChatType, _ id: Int64) -> String { + "\(type.rawValue)\(id)" + } +} + +enum SEChatResponse: Decodable, ChatAPIResult { + case activeUser(user: User) + case chatStarted + case chatRunning + case apiChats(user: UserRef, chats: [ChatData]) + case newChatItems(user: UserRef, chatItems: [AChatItem]) + case cmdOk(user_: UserRef?) + + var responseType: String { + switch self { + case .activeUser: "activeUser" + case .chatStarted: "chatStarted" + case .chatRunning: "chatRunning" + case .apiChats: "apiChats" + case .newChatItems: "newChatItems" + case .cmdOk: "cmdOk" + } + } + + var details: String { + switch self { + case let .activeUser(user): return String(describing: user) + case .chatStarted: return noDetails + case .chatRunning: return noDetails + case let .apiChats(u, chats): return withUser(u, String(describing: chats)) + case let .newChatItems(u, chatItems): + let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n") + return withUser(u, itemsString) + case .cmdOk: return noDetails + } + } + + static func fallbackResult(_ type: String, _ json: NSDictionary) -> SEChatResponse? { + if type == "apiChats", let r = parseApiChats(json) { + .apiChats(user: r.user, chats: r.chats) + } else { + nil + } + } +} + +enum SEChatEvent: Decodable, ChatAPIResult { + case chatSuspended + case sndFileProgressXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, sentSize: Int64, totalSize: Int64) + case sndFileCompleteXFTP(user: UserRef, chatItem: AChatItem, fileTransferMeta: FileTransferMeta) + case chatItemsStatusesUpdated(user: UserRef, chatItems: [AChatItem]) + case sndFileError(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, errorMessage: String) + case sndFileWarning(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, errorMessage: String) + + var responseType: String { + switch self { + case .chatSuspended: "chatSuspended" + case .sndFileProgressXFTP: "sndFileProgressXFTP" + case .sndFileCompleteXFTP: "sndFileCompleteXFTP" + case .chatItemsStatusesUpdated: "chatItemsStatusesUpdated" + case .sndFileError: "sndFileError" + case .sndFileWarning: "sndFileWarning" + } + } + + var details: String { + switch self { + case .chatSuspended: return noDetails + case let .sndFileProgressXFTP(u, chatItem, _, sentSize, totalSize): return withUser(u, "chatItem: \(String(describing: chatItem))\nsentSize: \(sentSize)\ntotalSize: \(totalSize)") + case let .sndFileCompleteXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem)) + case let .chatItemsStatusesUpdated(u, chatItems): + let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n") + return withUser(u, itemsString) + case let .sndFileError(u, chatItem, _, err): return withUser(u, "error: \(String(describing: err))\nchatItem: \(String(describing: chatItem))") + case let .sndFileWarning(u, chatItem, _, err): return withUser(u, "error: \(String(describing: err))\nchatItem: \(String(describing: chatItem))") + } + } +} diff --git a/apps/ios/SimpleX SE/ShareModel.swift b/apps/ios/SimpleX SE/ShareModel.swift new file mode 100644 index 0000000000..12a775f85c --- /dev/null +++ b/apps/ios/SimpleX SE/ShareModel.swift @@ -0,0 +1,542 @@ +// +// ShareModel.swift +// SimpleX SE +// +// Created by Levitating Pineapple on 09/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import UniformTypeIdentifiers +import AVFoundation +import SwiftUI +import SimpleXChat + +/// Maximum size of hex encoded media previews +private let MAX_DATA_SIZE: Int64 = 14000 + +/// Maximum dimension (width or height) of an image, before passed for processing +private let MAX_DOWNSAMPLE_SIZE: Int64 = 2000 + +class ShareModel: ObservableObject { + @Published var sharedContent: SharedContent? + @Published var chats: [ChatData] = [] + @Published var profileImages: [ChatInfo.ID: UIImage] = [:] + @Published var search = "" + @Published var comment = "" + @Published var selected: ChatData? + @Published var isLoaded = false + @Published var bottomBar: BottomBar = .loadingSpinner + @Published var errorAlert: ErrorAlert? + @Published var hasSimplexLink = false + @Published var alertRequiresPassword = false + var networkTimeout = CFAbsoluteTimeGetCurrent() + + enum BottomBar { + case sendButton + case loadingSpinner + case loadingBar(progress: Double) + + var isLoading: Bool { + switch self { + case .sendButton: false + case .loadingSpinner: true + case .loadingBar: true + } + } + } + + var completion: () -> Void = { + fatalError("completion has not been set") + } + + private var itemProvider: NSItemProvider? + + var isSendDisbled: Bool { sharedContent == nil || selected == nil || isProhibited(selected) } + + var isLinkPreview: Bool { + switch sharedContent { + case .url: true + default: false + } + } + + func isProhibited(_ chat: ChatData?) -> Bool { + if let chat, let sharedContent { + sharedContent.prohibited(in: chat, hasSimplexLink: hasSimplexLink) + } else { false } + } + + var filteredChats: [ChatData] { + search.isEmpty + ? filterChatsToForwardTo(chats: chats) + : filterChatsToForwardTo(chats: chats) + .filter { foundChat($0, search.localizedLowercase) } + } + + func setup(context: NSExtensionContext) { + if appLocalAuthEnabledGroupDefault.get() && !allowShareExtensionGroupDefault.get() { + errorAlert = ErrorAlert(title: "App is locked!", message: "You can allow sharing in Privacy & Security / SimpleX Lock settings.") + return + } + if let item = context.inputItems.first as? NSExtensionItem, + let itemProvider = item.attachments?.first { + self.itemProvider = itemProvider + self.completion = { + ShareModel.CompletionHandler.isEventLoopEnabled = false + context.completeRequest(returningItems: [item]) { + apiSuspendChat(expired: $0) + } + } + setup() + } + } + + func setup(with dbKey: String? = nil) { + // Init Chat + Task { + if let e = initChat(with: dbKey) { + await MainActor.run { errorAlert = e } + } else { + // Load Chats + Task { + switch fetchChats() { + case let .success(chats): + // Decode base64 images on background thread + let profileImages = chats.reduce(into: Dictionary()) { dict, chatData in + if let profileImage = chatData.chatInfo.image, + let uiImage = imageFromBase64(profileImage) { + dict[chatData.id] = uiImage + } + } + await MainActor.run { + self.chats = chats + self.profileImages = profileImages + withAnimation { isLoaded = true } + } + case let .failure(error): + await MainActor.run { errorAlert = error } + } + } + // Process Attachment + Task { + switch await getSharedContent(self.itemProvider!) { + case let .success(chatItemContent): + await MainActor.run { + self.sharedContent = chatItemContent + self.bottomBar = .sendButton + if case let .text(string) = chatItemContent { comment = string } + } + case let .failure(errorAlert): + await MainActor.run { self.errorAlert = errorAlert } + } + } + } + } + } + + func send() { + if let sharedContent, let selected { + Task { + await MainActor.run { self.bottomBar = .loadingSpinner } + do { + SEChatState.shared.set(.sendingMessage) + await waitForOtherProcessesToSuspend() + let chatItems = try apiSendMessages( + chatInfo: selected.chatInfo, + composedMessages: [ComposedMessage(fileSource: sharedContent.cryptoFile, msgContent: sharedContent.msgContent(comment: self.comment))] + ) + if selected.chatInfo.chatType == .local { + completion() + } else { + // TODO batch send: share multiple items + if let ci = chatItems.first { + await MainActor.run { self.bottomBar = .loadingBar(progress: 0) } + if let e = await handleEvents( + isGroupChat: ci.chatInfo.chatType == .group, + isWithoutFile: sharedContent.cryptoFile == nil, + chatItemId: ci.chatItem.id + ) { + await MainActor.run { errorAlert = e } + } else { + completion() + } + } + } + } catch { + if let e = error as? ErrorAlert { + await MainActor.run { errorAlert = e } + } + } + } + } + } + + private func initChat(with dbKey: String? = nil) -> ErrorAlert? { + do { + if hasChatCtrl() && dbKey == nil { + try apiActivateChat() + } else { + resetChatCtrl() // Clears retained migration result + registerGroupDefaults() + haskell_init_se() + let (_, result) = chatMigrateInit(dbKey, confirmMigrations: defaultMigrationConfirmation(), backgroundMode: false) + if let e = migrationError(result) { return e } + try apiSetAppFilePaths( + filesFolder: getAppFilesDirectory().path, + tempFolder: getTempFilesDirectory().path, + assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path + ) + let isRunning = try apiStartChat() + logger.log(level: .debug, "chat started, running: \(isRunning)") + } + try apiSetNetworkConfig(getNetCfg()) + try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get()) + } catch { return ErrorAlert(error) } + return nil + } + + private func migrationError(_ r: DBMigrationResult) -> ErrorAlert? { + let useKeychain = storeDBPassphraseGroupDefault.get() + let storedDBKey = kcDatabasePassword.get() + if case .errorNotADatabase = r { + Task { await MainActor.run { self.alertRequiresPassword = true } } + } + return switch r { + case .errorNotADatabase: + if useKeychain && storedDBKey != nil && storedDBKey != "" { + ErrorAlert( + title: "Wrong database passphrase", + message: "Database passphrase is different from saved in the keychain." + ) + } else { + ErrorAlert( + title: "Database encrypted!", + message: "Database passphrase is required to open chat." + ) + } + case let .errorMigration(_, migrationError): + switch migrationError { + case .upgrade: + ErrorAlert( + title: "Database upgrade required", + message: "Open the app to upgrade the database." + ) + case .downgrade: + ErrorAlert( + title: "Database downgrade required", + message: "Open the app to downgrade the database." + ) + case let .migrationError(mtrError): + ErrorAlert( + title: "Incompatible database version", + message: mtrErrorDescription(mtrError) + ) + } + case let .errorSQL(_, migrationSQLError): + ErrorAlert( + title: "Database error", + message: "Error: \(migrationSQLError)" + ) + case .errorKeychain: + ErrorAlert( + title: "Keychain error", + message: "Cannot access keychain to save database password" + ) + case .invalidConfirmation: + ErrorAlert("Invalid migration confirmation") + case let .unknown(json): + ErrorAlert( + title: "Database error", + message: "Unknown database error: \(json)" + ) + case .ok: nil + } + } + + private func fetchChats() -> Result, ErrorAlert> { + do { + guard let user = try apiGetActiveUser() else { + return .failure( + ErrorAlert( + title: "No active profile", + message: "Please create a profile in the SimpleX app" + ) + ) + } + return .success(try apiGetChats(userId: user.id)) + } catch { + return .failure(ErrorAlert(error)) + } + } + + actor CompletionHandler { + static var isEventLoopEnabled = false + private var fileCompleted = false + private var messageCompleted = false + + func completeFile() { fileCompleted = true } + + func completeMessage() { messageCompleted = true } + + var isRunning: Bool { + Self.isEventLoopEnabled && !(fileCompleted && messageCompleted) + } + } + + /// Polls and processes chat events + /// Returns when message sending has completed optionally returning and error. + private func handleEvents(isGroupChat: Bool, isWithoutFile: Bool, chatItemId: ChatItem.ID) async -> ErrorAlert? { + func isMessage(for item: AChatItem?) -> Bool { + item.map { $0.chatItem.id == chatItemId } ?? false + } + + CompletionHandler.isEventLoopEnabled = true + let ch = CompletionHandler() + if isWithoutFile { await ch.completeFile() } + networkTimeout = CFAbsoluteTimeGetCurrent() + while await ch.isRunning { + if CFAbsoluteTimeGetCurrent() - networkTimeout > 30 { + await MainActor.run { + self.errorAlert = ErrorAlert(title: "Slow network?", message: "Sending a message takes longer than expected.") { + Button("Wait", role: .cancel) { self.networkTimeout = CFAbsoluteTimeGetCurrent() } + Button("Cancel", role: .destructive) { self.completion() } + } + } + } + let r: APIResult? = recvSimpleXMsg(messageTimeout: 1_000_000) + switch r { + case let .result(.sndFileProgressXFTP(_, ci, _, sentSize, totalSize)): + guard isMessage(for: ci) else { continue } + networkTimeout = CFAbsoluteTimeGetCurrent() + await MainActor.run { + withAnimation { + let progress = Double(sentSize) / Double(totalSize) + bottomBar = .loadingBar(progress: progress) + } + } + case let .result(.sndFileCompleteXFTP(_, ci, _)): + guard isMessage(for: ci) else { continue } + if isGroupChat { + await MainActor.run { bottomBar = .loadingSpinner } + } + await ch.completeFile() + if await !ch.isRunning { break } + case let .result(.chatItemsStatusesUpdated(_, chatItems)): + guard let ci = chatItems.last else { continue } + guard isMessage(for: ci) else { continue } + if let (title, message) = ci.chatItem.meta.itemStatus.statusInfo { + // `title` and `message` already localized and interpolated + return ErrorAlert( + title: "\(title)", + message: "\(message)" + ) + } else if case let .sndSent(sndProgress) = ci.chatItem.meta.itemStatus { + switch sndProgress { + case .complete: + await ch.completeMessage() + case .partial: + if isGroupChat { + Task { + try? await Task.sleep(nanoseconds: 5 * NSEC_PER_SEC) + await ch.completeMessage() + } + } + } + } + case let .result(.sndFileError(_, ci, _, errorMessage)): + guard isMessage(for: ci) else { continue } + if let ci { cleanupFile(ci) } + return ErrorAlert(title: "File error", message: "\(fileErrorInfo(ci) ?? errorMessage)") + case let .result(.sndFileWarning(_, ci, _, errorMessage)): + guard isMessage(for: ci) else { continue } + if let ci { cleanupFile(ci) } + return ErrorAlert(title: "File error", message: "\(fileErrorInfo(ci) ?? errorMessage)") + case let .error(chatError): + return ErrorAlert(chatError) + default: continue + } + } + return nil + } + + private func fileErrorInfo(_ ci: AChatItem?) -> String? { + switch ci?.chatItem.file?.fileStatus { + case let .sndError(e): e.errorInfo + case let .sndWarning(e): e.errorInfo + default: nil + } + } +} + +/// Chat Item Content extracted from `NSItemProvider` without the comment +enum SharedContent { + case image(preview: String, cryptoFile: CryptoFile) + case movie(preview: String, duration: Int, cryptoFile: CryptoFile) + case url(preview: LinkPreview) + case text(string: String) + case data(cryptoFile: CryptoFile) + + var cryptoFile: CryptoFile? { + switch self { + case let .image(_, cryptoFile): cryptoFile + case let .movie(_, _, cryptoFile): cryptoFile + case .url: nil + case .text: nil + case let .data(cryptoFile): cryptoFile + } + } + + func msgContent(comment: String) -> MsgContent { + switch self { + case let .image(preview, _): .image(text: comment, image: preview) + case let .movie(preview, duration, _): .video(text: comment, image: preview, duration: duration) + case let .url(preview): .link(text: preview.uri.absoluteString + (comment == "" ? "" : "\n" + comment), preview: preview) + case .text: .text(comment) + case .data: .file(comment) + } + } + + func prohibited(in chatData: ChatData, hasSimplexLink: Bool) -> Bool { + chatData.prohibitedByPref( + hasSimplexLink: hasSimplexLink, + isMediaOrFileAttachment: cryptoFile != nil, + isVoice: false + ) + } +} + +fileprivate func getSharedContent(_ ip: NSItemProvider) async -> Result { + if let type = firstMatching(of: [.image, .movie, .fileURL, .url, .text]) { + switch type { + // Prepare Image message + case .image: + // Animated + return if ip.hasItemConformingToTypeIdentifier(UTType.gif.identifier) { + if let url = try? await inPlaceUrl(type: type), + let data = try? Data(contentsOf: url), + let image = UIImage(data: data), + let cryptoFile = saveFile(data, generateNewFileName("IMG", "gif"), encrypted: privacyEncryptLocalFilesGroupDefault.get()), + let preview = await resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE) { + .success(.image(preview: preview, cryptoFile: cryptoFile)) + } else { .failure(ErrorAlert("Error preparing message")) } + + // Static + } else { + if let image = await staticImage(), + let cryptoFile = saveImage(image), + let preview = await resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE) { + .success(.image(preview: preview, cryptoFile: cryptoFile)) + } else { .failure(ErrorAlert("Error preparing message")) } + } + + // Prepare Movie message + case .movie: + if let url = try? await inPlaceUrl(type: type), + let trancodedUrl = await transcodeVideo(from: url), + let (image, duration) = AVAsset(url: trancodedUrl).generatePreview(), + let preview = await resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE), + let cryptoFile = moveTempFileFromURL(trancodedUrl) { + try? FileManager.default.removeItem(at: trancodedUrl) + return .success(.movie(preview: preview, duration: duration, cryptoFile: cryptoFile)) + } else { return .failure(ErrorAlert("Error preparing message")) } + + // Prepare Data message + case .fileURL: + if let url = try? await inPlaceUrl(type: .data) { + if isFileTooLarge(for: url) { + let sizeString = ByteCountFormatter.string( + fromByteCount: Int64(getMaxFileSize(.xftp)), + countStyle: .binary + ) + return .failure( + ErrorAlert( + title: "Large file!", + message: "Currently maximum supported file size is \(sizeString)." + ) + ) + } + if let file = saveFileFromURL(url) { + return .success(.data(cryptoFile: file)) + } + } + return .failure(ErrorAlert("Error preparing file")) + + // Prepare Link message + case .url: + if let url = try? await ip.loadItem(forTypeIdentifier: type.identifier) as? URL { + let content: SharedContent = + if privacyLinkPreviewsGroupDefault.get(), let linkPreview = await getLinkPreview(for: url) { + .url(preview: linkPreview) + } else { + .text(string: url.absoluteString) + } + return .success(content) + } else { return .failure(ErrorAlert("Error preparing message")) } + + // Prepare Text message + case .text: + return if let text = try? await ip.loadItem(forTypeIdentifier: type.identifier) as? String { + .success(.text(string: text)) + } else { .failure(ErrorAlert("Error preparing message")) } + default: return .failure(ErrorAlert("Unsupported format")) + } + } else { + return .failure(ErrorAlert("Unsupported format")) + } + + + func inPlaceUrl(type: UTType) async throws -> URL { + try await withCheckedThrowingContinuation { cont in + let _ = ip.loadInPlaceFileRepresentation(forTypeIdentifier: type.identifier) { url, bool, error in + if let url = url { + cont.resume(returning: url) + } else if let error = error { + cont.resume(throwing: error) + } else { + fatalError("Either `url` or `error` must be present") + } + } + } + } + + func firstMatching(of types: Array) -> UTType? { + for type in types { + if ip.hasItemConformingToTypeIdentifier(type.identifier) { return type } + } + return nil + } + + func staticImage() async -> UIImage? { + if let url = try? await inPlaceUrl(type: .image), + let downsampledImage = downsampleImage(at: url, to: MAX_DOWNSAMPLE_SIZE) { + downsampledImage + } else { + /// Fallback to loading image directly from `ItemProvider` + /// in case loading from disk is not possible. Required for sharing screenshots. + try? await ip.loadItem(forTypeIdentifier: UTType.image.identifier) as? UIImage + } + } +} + + +fileprivate func transcodeVideo(from input: URL) async -> URL? { + let outputUrl = URL( + fileURLWithPath: generateNewFileName( + getTempFilesDirectory().path + "/" + "video", "mp4", + fullPath: true + ) + ) + if await makeVideoQualityLower(input, outputUrl: outputUrl) { + return outputUrl + } else { + try? FileManager.default.removeItem(at: outputUrl) + return nil + } +} + +fileprivate func isFileTooLarge(for url: URL) -> Bool { + fileSize(url) + .map { $0 > getMaxFileSize(.xftp) } + ?? false +} + diff --git a/apps/ios/SimpleX SE/ShareView.swift b/apps/ios/SimpleX SE/ShareView.swift new file mode 100644 index 0000000000..07180ffa1b --- /dev/null +++ b/apps/ios/SimpleX SE/ShareView.swift @@ -0,0 +1,230 @@ +// +// ShareView.swift +// SimpleX SE +// +// Created by Levitating Pineapple on 09/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct ShareView: View { + @ObservedObject var model: ShareModel + @Environment(\.colorScheme) var colorScheme + @State private var password = String() + @AppStorage(GROUP_DEFAULT_PROFILE_IMAGE_CORNER_RADIUS, store: groupDefaults) private var radius = defaultProfileImageCorner + + var body: some View { + NavigationView { + ZStack(alignment: .bottom) { + if model.isLoaded { + List(model.filteredChats) { chat in + let isProhibited = model.isProhibited(chat) + let isSelected = model.selected == chat + HStack { + profileImage( + chatInfoId: chat.chatInfo.id, + iconName: chatIconName(chat.chatInfo), + size: 30 + ) + Text(chat.chatInfo.displayName).foregroundStyle( + isProhibited ? .secondary : .primary + ) + Spacer() + radioButton(selected: isSelected && !isProhibited) + } + .contentShape(Rectangle()) + .onTapGesture { + if isProhibited { + model.errorAlert = ErrorAlert( + title: "Cannot forward message", + message: "Selected chat preferences prohibit this message." + ) { Button("Ok", role: .cancel) { } } + } else { + model.selected = isSelected ? nil : chat + } + } + .tag(chat) + } + } else { + ProgressView().frame(maxHeight: .infinity) + } + } + .navigationTitle("Share") + .safeAreaInset(edge: .bottom) { + switch model.bottomBar { + case .sendButton: + compose(isLoading: false) + case .loadingSpinner: + compose(isLoading: true) + case .loadingBar(let progress): + loadingBar(progress: progress) + } + } + } + .searchable( + text: $model.search, + placement: .navigationBarDrawer(displayMode: .always) + ) + .alert($model.errorAlert) { alert in + if model.alertRequiresPassword { + SecureField("Passphrase", text: $password) + Button("Ok") { + model.setup(with: password) + password = String() + } + Button("Cancel", role: .cancel) { model.completion() } + } else { + Button("Ok") { model.completion() } + } + } + .onChange(of: model.comment) { + model.hasSimplexLink = hasSimplexLink($0) + } + } + + private func compose(isLoading: Bool) -> some View { + VStack(spacing: 0) { + Divider() + if let content = model.sharedContent { + itemPreview(content) + } + HStack { + Group { + if #available(iOSApplicationExtension 16.0, *) { + TextField("Comment", text: $model.comment, axis: .vertical).lineLimit(6) + } else { + TextField("Comment", text: $model.comment) + } + } + .contentShape(Rectangle()) + .disabled(isLoading) + .padding(.horizontal, 12) + .padding(.vertical, 4) + Group { + if isLoading { + ProgressView() + } else { + Button(action: model.send) { + Image(systemName: "arrow.up.circle.fill") + .resizable() + } + .disabled(model.isSendDisbled) + } + } + .frame(width: 28, height: 28) + .padding(6) + + } + .background(Color(.systemBackground)) + .clipShape(RoundedRectangle(cornerRadius: 20)) + .overlay( + RoundedRectangle(cornerRadius: 20) + .strokeBorder(.secondary, lineWidth: 0.5).opacity(0.7) + ) + .padding(8) + } + .background(.thinMaterial) + } + + @ViewBuilder private func itemPreview(_ content: SharedContent) -> some View { + switch content { + case let .image(preview, _): imagePreview(preview) + case let .movie(preview, _, _): imagePreview(preview) + case let .url(preview): linkPreview(preview) + case let .data(cryptoFile): + previewArea { + Image(systemName: "doc.fill") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 30, height: 30) + .foregroundColor(Color(uiColor: .tertiaryLabel)) + .padding(.leading, 4) + Text(cryptoFile.filePath) + } + case .text: EmptyView() + } + } + + @ViewBuilder private func imagePreview(_ imgStr: String) -> some View { + if let img = imageFromBase64(imgStr) { + previewArea { + Image(uiImage: img) + .resizable() + .scaledToFit() + .frame(minHeight: 40, maxHeight: 60) + } + } else { + EmptyView() + } + } + + private func linkPreview(_ linkPreview: LinkPreview) -> some View { + previewArea { + HStack(alignment: .center, spacing: 8) { + if let uiImage = imageFromBase64(linkPreview.image) { + Image(uiImage: uiImage) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(maxWidth: 80, maxHeight: 60) + } + VStack(alignment: .center, spacing: 4) { + Text(linkPreview.title) + .lineLimit(1) + Text(linkPreview.uri.absoluteString) + .font(.caption) + .lineLimit(1) + .foregroundColor(.secondary) + } + .padding(.vertical, 5) + .frame(maxWidth: .infinity, minHeight: 60) + } + } + } + + @ViewBuilder private func previewArea(@ViewBuilder content: @escaping () -> V) -> some View { + HStack(alignment: .center, spacing: 8) { + content() + Spacer() + } + .padding(.vertical, 1) + .frame(minHeight: 54) + .background { + switch colorScheme { + case .light: LightColorPaletteApp.sentMessage + case .dark: DarkColorPaletteApp.sentMessage + @unknown default: Color(.tertiarySystemBackground) + } + } + Divider() + } + + private func loadingBar(progress: Double) -> some View { + VStack { + Text("Sending message…") + ProgressView(value: progress) + } + .padding() + .background(Material.ultraThin) + } + + @ViewBuilder private func profileImage(chatInfoId: ChatInfo.ID, iconName: String, size: Double) -> some View { + if let uiImage = model.profileImages[chatInfoId] { + clipProfileImage(Image(uiImage: uiImage), size: size, radius: radius) + } else { + Image(systemName: iconName) + .resizable() + .foregroundColor(Color(uiColor: .tertiaryLabel)) + .frame(width: size, height: size) +// add background when adding themes to SE +// .background(Circle().fill(backgroundColor != nil ? backgroundColor! : .clear)) + } + } + + private func radioButton(selected: Bool) -> some View { + Image(systemName: selected ? "checkmark.circle.fill" : "circle") + .imageScale(.large) + .foregroundStyle(selected ? Color.accentColor : Color(.tertiaryLabel)) + } +} diff --git a/apps/ios/SimpleX SE/ShareViewController.swift b/apps/ios/SimpleX SE/ShareViewController.swift new file mode 100644 index 0000000000..bf22f44a3b --- /dev/null +++ b/apps/ios/SimpleX SE/ShareViewController.swift @@ -0,0 +1,46 @@ +// +// ShareViewController.swift +// SimpleX SE +// +// Created by Levitating Pineapple on 08/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import UIKit +import SwiftUI +import SimpleXChat + +/// Extension Entry point +/// System will create this controller each time share sheet is invoked +/// using `NSExtensionPrincipalClass` in the info.plist +@objc(ShareViewController) +class ShareViewController: UIHostingController { + private let model = ShareModel() + // Assuming iOS continues to only allow single share sheet to be presented at once + static var isVisible: Bool = false + + @objc init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(rootView: ShareView(model: model)) + } + + @available(*, unavailable) + required init?(coder aDecoder: NSCoder) { fatalError() } + + override func viewDidLoad() { + ShareModel.CompletionHandler.isEventLoopEnabled = false + model.setup(context: extensionContext!) + } + + override func viewWillAppear(_ animated: Bool) { + logger.debug("ShareSheet will appear") + super.viewWillAppear(animated) + Self.isVisible = true + } + + override func viewWillDisappear(_ animated: Bool) { + logger.debug("ShareSheet will dissappear") + super.viewWillDisappear(animated) + ShareModel.CompletionHandler.isEventLoopEnabled = false + Self.isVisible = false + } +} diff --git a/apps/ios/SimpleX SE/SimpleX SE.entitlements b/apps/ios/SimpleX SE/SimpleX SE.entitlements new file mode 100644 index 0000000000..51dea2c806 --- /dev/null +++ b/apps/ios/SimpleX SE/SimpleX SE.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.application-groups + + group.chat.simplex.app + + keychain-access-groups + + $(AppIdentifierPrefix)chat.simplex.app + + + diff --git a/apps/ios/SimpleX SE/bg.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/bg.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/bg.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/bg.lproj/Localizable.strings b/apps/ios/SimpleX SE/bg.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/bg.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/cs.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/cs.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/cs.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/cs.lproj/Localizable.strings b/apps/ios/SimpleX SE/cs.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/cs.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/de.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/de.lproj/InfoPlist.strings new file mode 100644 index 0000000000..4a387a4361 --- /dev/null +++ b/apps/ios/SimpleX SE/de.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2025 SimpleX Chat. Alle Rechte vorbehalten."; + diff --git a/apps/ios/SimpleX SE/de.lproj/Localizable.strings b/apps/ios/SimpleX SE/de.lproj/Localizable.strings new file mode 100644 index 0000000000..4c10694986 --- /dev/null +++ b/apps/ios/SimpleX SE/de.lproj/Localizable.strings @@ -0,0 +1,111 @@ +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "Die App ist gesperrt!"; + +/* No comment provided by engineer. */ +"Cancel" = "Abbrechen"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Es ist nicht möglich, auf den Schlüsselbund zuzugreifen, um das Datenbankpasswort zu speichern"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "Nachricht kann nicht weitergeleitet werden"; + +/* No comment provided by engineer. */ +"Comment" = "Kommentieren"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "Die maximal erlaubte Dateigröße beträgt aktuell %@."; + +/* No comment provided by engineer. */ +"Database downgrade required" = "Datenbank-Herunterstufung ist erforderlich"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "Datenbank ist verschlüsselt!"; + +/* No comment provided by engineer. */ +"Database error" = "Datenbankfehler"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "Das Datenbank-Passwort unterscheidet sich vom im Schlüsselbund gespeicherten."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "Um den Chat zu öffnen, ist ein Datenbank-Passwort ist erforderlich."; + +/* No comment provided by engineer. */ +"Database upgrade required" = "Datenbank-Aktualisierung erforderlich"; + +/* No comment provided by engineer. */ +"Error preparing file" = "Fehler beim Vorbereiten der Datei"; + +/* No comment provided by engineer. */ +"Error preparing message" = "Fehler beim Vorbereiten der Nachricht"; + +/* No comment provided by engineer. */ +"Error: %@" = "Fehler: %@"; + +/* No comment provided by engineer. */ +"File error" = "Datei-Fehler"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Datenbank-Version nicht kompatibel"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Migrations-Bestätigung ungültig"; + +/* No comment provided by engineer. */ +"Keychain error" = "Schlüsselbund-Fehler"; + +/* No comment provided by engineer. */ +"Large file!" = "Große Datei!"; + +/* No comment provided by engineer. */ +"No active profile" = "Kein aktives Profil"; + +/* No comment provided by engineer. */ +"Ok" = "OK"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "Öffnen Sie die App, um die Datenbank herunterzustufen."; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "Öffnen Sie die App, um die Datenbank zu aktualisieren."; + +/* No comment provided by engineer. */ +"Passphrase" = "Passwort"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "Bitte erstellen Sie ein Profil in der SimpleX-App"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Diese Nachricht ist wegen der gewählten Chat-Einstellungen nicht erlaubt."; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "Das Senden einer Nachricht dauert länger als erwartet."; + +/* No comment provided by engineer. */ +"Sending message…" = "Nachricht wird gesendet…"; + +/* No comment provided by engineer. */ +"Share" = "Teilen"; + +/* No comment provided by engineer. */ +"Slow network?" = "Langsames Netzwerk?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Unbekannter Datenbankfehler: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "Nicht unterstütztes Format"; + +/* No comment provided by engineer. */ +"Wait" = "Warten"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Falsches Datenbank-Passwort"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Sie können das Teilen in den Einstellungen zu Datenschutz & Sicherheit / SimpleX-Sperre erlauben."; + diff --git a/apps/ios/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/es.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/es.lproj/InfoPlist.strings new file mode 100644 index 0000000000..74bda58efb --- /dev/null +++ b/apps/ios/SimpleX SE/es.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Todos los derechos reservados."; + diff --git a/apps/ios/SimpleX SE/es.lproj/Localizable.strings b/apps/ios/SimpleX SE/es.lproj/Localizable.strings new file mode 100644 index 0000000000..4cc5029537 --- /dev/null +++ b/apps/ios/SimpleX SE/es.lproj/Localizable.strings @@ -0,0 +1,111 @@ +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "¡Aplicación bloqueada!"; + +/* No comment provided by engineer. */ +"Cancel" = "Cancelar"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Keychain inaccesible para guardar la contraseña de la base de datos"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "No se puede reenviar el mensaje"; + +/* No comment provided by engineer. */ +"Comment" = "Comentario"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "El tamaño máximo de archivo admitido es %@."; + +/* No comment provided by engineer. */ +"Database downgrade required" = "Se requiere volver a versión anterior de la base de datos"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "¡Base de datos cifrada!"; + +/* No comment provided by engineer. */ +"Database error" = "Error en base de datos"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "La contraseña de la base de datos es diferente a la almacenada en keychain."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "Se requiere la contraseña de la base de datos para abrir la aplicación."; + +/* No comment provided by engineer. */ +"Database upgrade required" = "Se requiere actualizar la base de datos"; + +/* No comment provided by engineer. */ +"Error preparing file" = "Error al preparar el archivo"; + +/* No comment provided by engineer. */ +"Error preparing message" = "Error al preparar el mensaje"; + +/* No comment provided by engineer. */ +"Error: %@" = "Error: %@"; + +/* No comment provided by engineer. */ +"File error" = "Error de archivo"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Versión de base de datos incompatible"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Confirmación de migración no válida"; + +/* No comment provided by engineer. */ +"Keychain error" = "Error en keychain"; + +/* No comment provided by engineer. */ +"Large file!" = "¡Archivo grande!"; + +/* No comment provided by engineer. */ +"No active profile" = "Ningún perfil activo"; + +/* No comment provided by engineer. */ +"Ok" = "Ok"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "Abre la aplicación para volver a versión anterior de la base de datos."; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "Abre la aplicación para actualizar la base de datos."; + +/* No comment provided by engineer. */ +"Passphrase" = "Frase de contraseña"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "Por favor, crea un perfil en SimpleX"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Las preferencias seleccionadas no permiten este mensaje."; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "Enviar el mensaje lleva más tiempo del esperado."; + +/* No comment provided by engineer. */ +"Sending message…" = "Enviando mensaje…"; + +/* No comment provided by engineer. */ +"Share" = "Compartir"; + +/* No comment provided by engineer. */ +"Slow network?" = "¿Red lenta?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Error desconocido en la base de datos: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "Formato sin soporte"; + +/* No comment provided by engineer. */ +"Wait" = "Espera"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Contraseña incorrecta de la base de datos"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Puedes dar permiso para compartir en Privacidad y Seguridad / Bloque SimpleX."; + diff --git a/apps/ios/SimpleX SE/fi.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/fi.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/fi.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/fi.lproj/Localizable.strings b/apps/ios/SimpleX SE/fi.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/fi.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/fr.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/fr.lproj/InfoPlist.strings new file mode 100644 index 0000000000..4f89e54128 --- /dev/null +++ b/apps/ios/SimpleX SE/fr.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Tous droits réservés."; + diff --git a/apps/ios/SimpleX SE/fr.lproj/Localizable.strings b/apps/ios/SimpleX SE/fr.lproj/Localizable.strings new file mode 100644 index 0000000000..46a458b471 --- /dev/null +++ b/apps/ios/SimpleX SE/fr.lproj/Localizable.strings @@ -0,0 +1,111 @@ +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "L'app est verrouillée !"; + +/* No comment provided by engineer. */ +"Cancel" = "Annuler"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Impossible d'accéder à la keychain pour enregistrer le mot de passe de la base de données"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "Impossible de transférer le message"; + +/* No comment provided by engineer. */ +"Comment" = "Commenter"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "Actuellement, la taille maximale des fichiers supportés est de %@."; + +/* No comment provided by engineer. */ +"Database downgrade required" = "Mise à jour de la base de données nécessaire"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "Base de données chiffrée !"; + +/* No comment provided by engineer. */ +"Database error" = "Erreur de base de données"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "La phrase secrète de la base de données est différente de celle enregistrée dans la keychain."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "La phrase secrète de la base de données est nécessaire pour ouvrir le chat."; + +/* No comment provided by engineer. */ +"Database upgrade required" = "Mise à niveau de la base de données nécessaire"; + +/* No comment provided by engineer. */ +"Error preparing file" = "Erreur lors de la préparation du fichier"; + +/* No comment provided by engineer. */ +"Error preparing message" = "Erreur lors de la préparation du message"; + +/* No comment provided by engineer. */ +"Error: %@" = "Erreur : %@"; + +/* No comment provided by engineer. */ +"File error" = "Erreur de fichier"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Version de la base de données incompatible"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Confirmation de migration invalide"; + +/* No comment provided by engineer. */ +"Keychain error" = "Erreur de la keychain"; + +/* No comment provided by engineer. */ +"Large file!" = "Fichier trop lourd !"; + +/* No comment provided by engineer. */ +"No active profile" = "Pas de profil actif"; + +/* No comment provided by engineer. */ +"Ok" = "Ok"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "Ouvrez l'app pour rétrograder la base de données."; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "Ouvrez l'app pour mettre à jour la base de données."; + +/* No comment provided by engineer. */ +"Passphrase" = "Phrase secrète"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "Veuillez créer un profil dans l'app SimpleX"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Les paramètres de chat sélectionnés ne permettent pas l'envoi de ce message."; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "L'envoi d'un message prend plus de temps que prévu."; + +/* No comment provided by engineer. */ +"Sending message…" = "Envoi du message…"; + +/* No comment provided by engineer. */ +"Share" = "Partager"; + +/* No comment provided by engineer. */ +"Slow network?" = "Réseau lent ?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Erreur inconnue de la base de données : %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "Format non pris en charge"; + +/* No comment provided by engineer. */ +"Wait" = "Attendez"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Mauvaise phrase secrète pour la base de données"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Vous pouvez autoriser le partage dans les paramètres Confidentialité et sécurité / SimpleX Lock."; + diff --git a/apps/ios/SimpleX SE/hu.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/hu.lproj/InfoPlist.strings new file mode 100644 index 0000000000..e1979850d1 --- /dev/null +++ b/apps/ios/SimpleX SE/hu.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Minden jog fenntartva."; + diff --git a/apps/ios/SimpleX SE/hu.lproj/Localizable.strings b/apps/ios/SimpleX SE/hu.lproj/Localizable.strings new file mode 100644 index 0000000000..2fedf0e6f1 --- /dev/null +++ b/apps/ios/SimpleX SE/hu.lproj/Localizable.strings @@ -0,0 +1,111 @@ +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "Az alkalmazás zárolva van!"; + +/* No comment provided by engineer. */ +"Cancel" = "Mégse"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Nem lehet hozzáférni a kulcstartóhoz az adatbázis jelszavának mentéséhez"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "Nem lehet továbbítani az üzenetet"; + +/* No comment provided by engineer. */ +"Comment" = "Hozzászólás"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "Jelenleg támogatott legnagyobb fájl méret: %@."; + +/* No comment provided by engineer. */ +"Database downgrade required" = "Adatbázis visszafejlesztése szükséges"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "Adatbázis titkosítva!"; + +/* No comment provided by engineer. */ +"Database error" = "Adatbázishiba"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "Az adatbázis jelmondata nem egyezik a kulcstartóba mentettől."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "A csevegés megnyitásához adja meg az adatbázis jelmondatát."; + +/* No comment provided by engineer. */ +"Database upgrade required" = "Adatbázis fejlesztése szükséges"; + +/* No comment provided by engineer. */ +"Error preparing file" = "Hiba történt a fájl előkészítésekor"; + +/* No comment provided by engineer. */ +"Error preparing message" = "Hiba történt az üzenet előkészítésekor"; + +/* No comment provided by engineer. */ +"Error: %@" = "Hiba: %@"; + +/* No comment provided by engineer. */ +"File error" = "Fájlhiba"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Nem kompatibilis adatbázis-verzió"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Érvénytelen átköltöztetési visszaigazolás"; + +/* No comment provided by engineer. */ +"Keychain error" = "Kulcstartóhiba"; + +/* No comment provided by engineer. */ +"Large file!" = "Nagy fájl!"; + +/* No comment provided by engineer. */ +"No active profile" = "Nincs aktív profil"; + +/* No comment provided by engineer. */ +"Ok" = "Rendben"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "Nyissa meg az alkalmazást az adatbázis visszafejlesztéséhez."; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "Nyissa meg az alkalmazást az adatbázis fejlesztéséhez."; + +/* No comment provided by engineer. */ +"Passphrase" = "Jelmondat"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "Hozzon létre egy profilt a SimpleX alkalmazásban"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "A kijelölt csevegési beállítások tiltják ezt az üzenetet."; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "Az üzenet elküldése a vártnál tovább tart."; + +/* No comment provided by engineer. */ +"Sending message…" = "Üzenet küldése…"; + +/* No comment provided by engineer. */ +"Share" = "Megosztás"; + +/* No comment provided by engineer. */ +"Slow network?" = "Lassú a hálózata?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Ismeretlen adatbázishiba: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "Nem támogatott formátum"; + +/* No comment provided by engineer. */ +"Wait" = "Várjon"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Érvénytelen adatbázis-jelmondat"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "A megosztást az Adatvédelem és biztonság / SimpleX-zár menüben engedélyezheti."; + diff --git a/apps/ios/SimpleX SE/it.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/it.lproj/InfoPlist.strings new file mode 100644 index 0000000000..78145285c2 --- /dev/null +++ b/apps/ios/SimpleX SE/it.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Tutti i diritti riservati."; + diff --git a/apps/ios/SimpleX SE/it.lproj/Localizable.strings b/apps/ios/SimpleX SE/it.lproj/Localizable.strings new file mode 100644 index 0000000000..e3d34650a3 --- /dev/null +++ b/apps/ios/SimpleX SE/it.lproj/Localizable.strings @@ -0,0 +1,111 @@ +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "L'app è bloccata!"; + +/* No comment provided by engineer. */ +"Cancel" = "Annulla"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Impossibile accedere al portachiavi per salvare la password del database"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "Impossibile inoltrare il messaggio"; + +/* No comment provided by engineer. */ +"Comment" = "Commento"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "Attualmente la dimensione massima supportata è di %@."; + +/* No comment provided by engineer. */ +"Database downgrade required" = "Downgrade del database necessario"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "Database crittografato!"; + +/* No comment provided by engineer. */ +"Database error" = "Errore del database"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "La password del database è diversa da quella salvata nel portachiavi."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "La password del database è necessaria per aprire la chat."; + +/* No comment provided by engineer. */ +"Database upgrade required" = "Aggiornamento del database necessario"; + +/* No comment provided by engineer. */ +"Error preparing file" = "Errore nella preparazione del file"; + +/* No comment provided by engineer. */ +"Error preparing message" = "Errore nella preparazione del messaggio"; + +/* No comment provided by engineer. */ +"Error: %@" = "Errore: %@"; + +/* No comment provided by engineer. */ +"File error" = "Errore del file"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Versione del database incompatibile"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Conferma di migrazione non valida"; + +/* No comment provided by engineer. */ +"Keychain error" = "Errore del portachiavi"; + +/* No comment provided by engineer. */ +"Large file!" = "File grande!"; + +/* No comment provided by engineer. */ +"No active profile" = "Nessun profilo attivo"; + +/* No comment provided by engineer. */ +"Ok" = "Ok"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "Apri l'app per eseguire il downgrade del database."; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "Apri l'app per aggiornare il database."; + +/* No comment provided by engineer. */ +"Passphrase" = "Password"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "Crea un profilo nell'app SimpleX"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Le preferenze della chat selezionata vietano questo messaggio."; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "L'invio di un messaggio richiede più tempo del previsto."; + +/* No comment provided by engineer. */ +"Sending message…" = "Invio messaggio…"; + +/* No comment provided by engineer. */ +"Share" = "Condividi"; + +/* No comment provided by engineer. */ +"Slow network?" = "Rete lenta?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Errore del database sconosciuto: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "Formato non supportato"; + +/* No comment provided by engineer. */ +"Wait" = "Attendi"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Password del database sbagliata"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Puoi consentire la condivisione in Privacy e sicurezza / impostazioni di SimpleX Lock."; + diff --git a/apps/ios/SimpleX SE/ja.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/ja.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/ja.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/ja.lproj/Localizable.strings b/apps/ios/SimpleX SE/ja.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/ja.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/nl.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/nl.lproj/InfoPlist.strings new file mode 100644 index 0000000000..c61e43a87f --- /dev/null +++ b/apps/ios/SimpleX SE/nl.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX SE/nl.lproj/Localizable.strings b/apps/ios/SimpleX SE/nl.lproj/Localizable.strings new file mode 100644 index 0000000000..e5d2487b54 --- /dev/null +++ b/apps/ios/SimpleX SE/nl.lproj/Localizable.strings @@ -0,0 +1,111 @@ +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "App is vergrendeld!"; + +/* No comment provided by engineer. */ +"Cancel" = "Annuleren"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Kan geen toegang krijgen tot de keychain om het database wachtwoord op te slaan"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "Kan bericht niet doorsturen"; + +/* No comment provided by engineer. */ +"Comment" = "Opmerking"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "De momenteel maximaal ondersteunde bestandsgrootte is %@."; + +/* No comment provided by engineer. */ +"Database downgrade required" = "Database downgrade vereist"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "Database versleuteld!"; + +/* No comment provided by engineer. */ +"Database error" = "Database fout"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "Het wachtwoord van de database verschilt van het wachtwoord die in de keychain is opgeslagen."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "Database wachtwoord is vereist om je chats te openen."; + +/* No comment provided by engineer. */ +"Database upgrade required" = "Database upgrade vereist"; + +/* No comment provided by engineer. */ +"Error preparing file" = "Fout bij voorbereiden bestand"; + +/* No comment provided by engineer. */ +"Error preparing message" = "Fout bij het voorbereiden van bericht"; + +/* No comment provided by engineer. */ +"Error: %@" = "Fout: %@"; + +/* No comment provided by engineer. */ +"File error" = "Bestandsfout"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Incompatibele database versie"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Ongeldige migratie bevestiging"; + +/* No comment provided by engineer. */ +"Keychain error" = "Keychain fout"; + +/* No comment provided by engineer. */ +"Large file!" = "Groot bestand!"; + +/* No comment provided by engineer. */ +"No active profile" = "Geen actief profiel"; + +/* No comment provided by engineer. */ +"Ok" = "Ok"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "Open de app om de database te downgraden."; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "Open de app om de database te upgraden."; + +/* No comment provided by engineer. */ +"Passphrase" = "Wachtwoord"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "Maak een profiel aan in de SimpleX app"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Geselecteerde chat voorkeuren verbieden dit bericht."; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "Het verzenden van een bericht duurt langer dan verwacht."; + +/* No comment provided by engineer. */ +"Sending message…" = "Bericht versturen…"; + +/* No comment provided by engineer. */ +"Share" = "Deel"; + +/* No comment provided by engineer. */ +"Slow network?" = "Traag netwerk?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Onbekende database fout: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "Niet ondersteund formaat"; + +/* No comment provided by engineer. */ +"Wait" = "wachten"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Verkeerde database wachtwoord"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "U kunt delen toestaan in de instellingen voor Privacy en beveiliging / SimpleX Lock."; + diff --git a/apps/ios/SimpleX SE/pl.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/pl.lproj/InfoPlist.strings new file mode 100644 index 0000000000..81283a3f02 --- /dev/null +++ b/apps/ios/SimpleX SE/pl.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Wszelkie prawa zastrzeżone."; + diff --git a/apps/ios/SimpleX SE/pl.lproj/Localizable.strings b/apps/ios/SimpleX SE/pl.lproj/Localizable.strings new file mode 100644 index 0000000000..c563431c28 --- /dev/null +++ b/apps/ios/SimpleX SE/pl.lproj/Localizable.strings @@ -0,0 +1,111 @@ +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "Aplikacja zablokowana!"; + +/* No comment provided by engineer. */ +"Cancel" = "Anuluj"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Nie można uzyskać dostępu do pęku kluczy aby zapisać hasło do bazy danych"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "Nie można przekazać wiadomości"; + +/* No comment provided by engineer. */ +"Comment" = "Komentarz"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "Obecnie maksymalny obsługiwany rozmiar pliku to %@."; + +/* No comment provided by engineer. */ +"Database downgrade required" = "Wymagane obniżenie wersji bazy danych"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "Baza danych zaszyfrowana!"; + +/* No comment provided by engineer. */ +"Database error" = "Błąd bazy danych"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "Hasło bazy danych jest inne niż zapisane w pęku kluczy."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "Hasło do bazy danych jest wymagane do otwarcia czatu."; + +/* No comment provided by engineer. */ +"Database upgrade required" = "Wymagana aktualizacja bazy danych"; + +/* No comment provided by engineer. */ +"Error preparing file" = "Błąd przygotowania pliku"; + +/* No comment provided by engineer. */ +"Error preparing message" = "Błąd przygotowania wiadomości"; + +/* No comment provided by engineer. */ +"Error: %@" = "Błąd: %@"; + +/* No comment provided by engineer. */ +"File error" = "Błąd pliku"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Niekompatybilna wersja bazy danych"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Nieprawidłowe potwierdzenie migracji"; + +/* No comment provided by engineer. */ +"Keychain error" = "Błąd pęku kluczy"; + +/* No comment provided by engineer. */ +"Large file!" = "Duży plik!"; + +/* No comment provided by engineer. */ +"No active profile" = "Brak aktywnego profilu"; + +/* No comment provided by engineer. */ +"Ok" = "Ok"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "Otwórz aplikację aby obniżyć wersję bazy danych."; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "Otwórz aplikację aby zaktualizować bazę danych."; + +/* No comment provided by engineer. */ +"Passphrase" = "Hasło"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "Proszę utworzyć profil w aplikacji SimpleX"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Wybrane preferencje czatu zabraniają tej wiadomości."; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "Wysłanie wiadomości trwa dłużej niż oczekiwano."; + +/* No comment provided by engineer. */ +"Sending message…" = "Wysyłanie wiadomości…"; + +/* No comment provided by engineer. */ +"Share" = "Udostępnij"; + +/* No comment provided by engineer. */ +"Slow network?" = "Wolna sieć?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Nieznany błąd bazy danych: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "Niewspierany format"; + +/* No comment provided by engineer. */ +"Wait" = "Czekaj"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Nieprawidłowe hasło bazy danych"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Możesz zezwolić na udostępnianie w ustawieniach Prywatność i bezpieczeństwo / Blokada SimpleX."; + diff --git a/apps/ios/SimpleX SE/ru.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/ru.lproj/InfoPlist.strings new file mode 100644 index 0000000000..d45b3d735d --- /dev/null +++ b/apps/ios/SimpleX SE/ru.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Все права защищены."; + diff --git a/apps/ios/SimpleX SE/ru.lproj/Localizable.strings b/apps/ios/SimpleX SE/ru.lproj/Localizable.strings new file mode 100644 index 0000000000..0841e8e47f --- /dev/null +++ b/apps/ios/SimpleX SE/ru.lproj/Localizable.strings @@ -0,0 +1,111 @@ +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "Приложение заблокировано!"; + +/* No comment provided by engineer. */ +"Cancel" = "Отменить"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Невозможно сохранить пароль в keychain"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "Невозможно переслать сообщение"; + +/* No comment provided by engineer. */ +"Comment" = "Комментарий"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "В настоящее время максимальный поддерживаемый размер файла составляет %@."; + +/* No comment provided by engineer. */ +"Database downgrade required" = "Требуется откат базы данных"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "База данных зашифрована!"; + +/* No comment provided by engineer. */ +"Database error" = "Ошибка базы данных"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "Пароль базы данных отличается от сохраненного в keychain."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "Введите пароль базы данных, чтобы открыть чат."; + +/* No comment provided by engineer. */ +"Database upgrade required" = "Требуется обновление базы данных"; + +/* No comment provided by engineer. */ +"Error preparing file" = "Ошибка подготовки файла"; + +/* No comment provided by engineer. */ +"Error preparing message" = "Ошибка подготовки сообщения"; + +/* No comment provided by engineer. */ +"Error: %@" = "Ошибка: %@"; + +/* No comment provided by engineer. */ +"File error" = "Ошибка файла"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Несовместимая версия базы данных"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Ошибка подтверждения миграции"; + +/* No comment provided by engineer. */ +"Keychain error" = "Ошибка keychain"; + +/* No comment provided by engineer. */ +"Large file!" = "Большой файл!"; + +/* No comment provided by engineer. */ +"No active profile" = "Нет активного профиля"; + +/* No comment provided by engineer. */ +"Ok" = "Ок"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "Откройте приложение, чтобы откатить базу данных."; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "Откройте приложение, чтобы обновить базу данных."; + +/* No comment provided by engineer. */ +"Passphrase" = "Пароль"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "Пожалуйста, создайте профиль в приложении SimpleX."; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Выбранные настройки чата запрещают это сообщение."; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "Отправка сообщения занимает дольше ожиданного."; + +/* No comment provided by engineer. */ +"Sending message…" = "Отправка сообщения…"; + +/* No comment provided by engineer. */ +"Share" = "Поделиться"; + +/* No comment provided by engineer. */ +"Slow network?" = "Медленная сеть?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Неизвестная ошибка базы данных: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "Неподдерживаемый формат"; + +/* No comment provided by engineer. */ +"Wait" = "Подождать"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Неправильный пароль базы данных"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Вы можете разрешить функцию Поделиться в настройках Конфиденциальности / Блокировка SimpleX."; + diff --git a/apps/ios/SimpleX SE/th.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/th.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/th.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/th.lproj/Localizable.strings b/apps/ios/SimpleX SE/th.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/th.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/tr.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/tr.lproj/InfoPlist.strings new file mode 100644 index 0000000000..cf1ca31f53 --- /dev/null +++ b/apps/ios/SimpleX SE/tr.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Telif Hakkı © 2024 SimpleX Chat. Tüm hakları saklıdır."; + diff --git a/apps/ios/SimpleX SE/tr.lproj/Localizable.strings b/apps/ios/SimpleX SE/tr.lproj/Localizable.strings new file mode 100644 index 0000000000..baef71c127 --- /dev/null +++ b/apps/ios/SimpleX SE/tr.lproj/Localizable.strings @@ -0,0 +1,111 @@ +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "Uygulama kilitlendi!"; + +/* No comment provided by engineer. */ +"Cancel" = "İptal et"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Veritabanı şifresini kaydetmek için Anahtar Zinciri'ne erişilemiyor"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "Mesaj iletilemiyor"; + +/* No comment provided by engineer. */ +"Comment" = "Yorum"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "Şu anki maksimum desteklenen dosya boyutu %@ kadardır."; + +/* No comment provided by engineer. */ +"Database downgrade required" = "Veritabanı sürüm düşürme gerekli"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "Veritabanı şifrelendi!"; + +/* No comment provided by engineer. */ +"Database error" = "Veritabanı hatası"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "Veritabanı parolası Anahtar Zinciri'nde kayıtlı olandan farklıdır."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "Konuşmayı açmak için veri tabanı parolası gerekli."; + +/* No comment provided by engineer. */ +"Database upgrade required" = "Veritabanı yükseltmesi gerekli"; + +/* No comment provided by engineer. */ +"Error preparing file" = "Dosya hazırlanırken hata oluştu"; + +/* No comment provided by engineer. */ +"Error preparing message" = "Mesaj hazırlanırken hata oluştu"; + +/* No comment provided by engineer. */ +"Error: %@" = "Hata: %@"; + +/* No comment provided by engineer. */ +"File error" = "Dosya hatası"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Uyumsuz veritabanı sürümü"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Geçerli olmayan taşıma onayı"; + +/* No comment provided by engineer. */ +"Keychain error" = "Anahtarlık hatası"; + +/* No comment provided by engineer. */ +"Large file!" = "Büyük dosya!"; + +/* No comment provided by engineer. */ +"No active profile" = "Aktif profil yok"; + +/* No comment provided by engineer. */ +"Ok" = "Tamam"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "Veritabanının sürümünü düşürmek için uygulamayı açın."; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "Veritabanını güncellemek için uygulamayı açın."; + +/* No comment provided by engineer. */ +"Passphrase" = "Parola"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "Lütfen SimpleX uygulamasında bir profil oluşturun"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Seçilen sohbet tercihleri bu mesajı yasakladı."; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "Mesaj göndermek beklenenden daha uzun sürüyor."; + +/* No comment provided by engineer. */ +"Sending message…" = "Mesaj gönderiliyor…"; + +/* No comment provided by engineer. */ +"Share" = "Paylaş"; + +/* No comment provided by engineer. */ +"Slow network?" = "Ağ yavaş mı?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Bilinmeyen veritabanı hatası: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "Desteklenmeyen format"; + +/* No comment provided by engineer. */ +"Wait" = "Bekleyin"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Yanlış veritabanı parolası"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Gizlilik ve Güvenlik / SimpleX Lock ayarlarından paylaşıma izin verebilirsiniz."; + diff --git a/apps/ios/SimpleX SE/uk.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/uk.lproj/InfoPlist.strings new file mode 100644 index 0000000000..18c4d5e8a5 --- /dev/null +++ b/apps/ios/SimpleX SE/uk.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Всі права захищені."; + diff --git a/apps/ios/SimpleX SE/uk.lproj/Localizable.strings b/apps/ios/SimpleX SE/uk.lproj/Localizable.strings new file mode 100644 index 0000000000..a6da81185e --- /dev/null +++ b/apps/ios/SimpleX SE/uk.lproj/Localizable.strings @@ -0,0 +1,111 @@ +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "Додаток заблоковано!"; + +/* No comment provided by engineer. */ +"Cancel" = "Скасувати"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Не вдається отримати доступ до зв'язки ключів для збереження пароля до бази даних"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "Неможливо переслати повідомлення"; + +/* No comment provided by engineer. */ +"Comment" = "Коментар"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "Наразі максимальний підтримуваний розмір файлу - %@."; + +/* No comment provided by engineer. */ +"Database downgrade required" = "Потрібне оновлення бази даних"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "База даних зашифрована!"; + +/* No comment provided by engineer. */ +"Database error" = "Помилка в базі даних"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "Парольна фраза бази даних відрізняється від збереженої у в’язці ключів."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "Для відкриття чату потрібно ввести пароль до бази даних."; + +/* No comment provided by engineer. */ +"Database upgrade required" = "Потрібне оновлення бази даних"; + +/* No comment provided by engineer. */ +"Error preparing file" = "Помилка підготовки файлу"; + +/* No comment provided by engineer. */ +"Error preparing message" = "Повідомлення про підготовку до помилки"; + +/* No comment provided by engineer. */ +"Error: %@" = "Помилка: %@"; + +/* No comment provided by engineer. */ +"File error" = "Помилка файлу"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Несумісна версія бази даних"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Недійсне підтвердження міграції"; + +/* No comment provided by engineer. */ +"Keychain error" = "Помилка зв'язки ключів"; + +/* No comment provided by engineer. */ +"Large file!" = "Великий файл!"; + +/* No comment provided by engineer. */ +"No active profile" = "Немає активного профілю"; + +/* No comment provided by engineer. */ +"Ok" = "Гаразд"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "Відкрийте програму, щоб знизити версію бази даних."; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "Відкрийте програму, щоб оновити базу даних."; + +/* No comment provided by engineer. */ +"Passphrase" = "Парольна фраза"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "Будь ласка, створіть профіль у додатку SimpleX"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Вибрані налаштування чату забороняють це повідомлення."; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "Надсилання повідомлення займає більше часу, ніж очікувалося."; + +/* No comment provided by engineer. */ +"Sending message…" = "Надсилаю повідомлення…"; + +/* No comment provided by engineer. */ +"Share" = "Поділіться"; + +/* No comment provided by engineer. */ +"Slow network?" = "Повільна мережа?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Невідома помилка бази даних: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "Непідтримуваний формат"; + +/* No comment provided by engineer. */ +"Wait" = "Зачекай"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Неправильна ключова фраза до бази даних"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Ви можете дозволити спільний доступ у налаштуваннях Конфіденційність і безпека / SimpleX Lock."; + diff --git a/apps/ios/SimpleX SE/zh-Hans.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/zh-Hans.lproj/InfoPlist.strings new file mode 100644 index 0000000000..760be62885 --- /dev/null +++ b/apps/ios/SimpleX SE/zh-Hans.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "版权所有 © 2024 SimpleX Chat。保留所有权利。"; + diff --git a/apps/ios/SimpleX SE/zh-Hans.lproj/Localizable.strings b/apps/ios/SimpleX SE/zh-Hans.lproj/Localizable.strings new file mode 100644 index 0000000000..362e2edb74 --- /dev/null +++ b/apps/ios/SimpleX SE/zh-Hans.lproj/Localizable.strings @@ -0,0 +1,111 @@ +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "应用程序已锁定!"; + +/* No comment provided by engineer. */ +"Cancel" = "取消"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "无法访问钥匙串以保存数据库密码"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "无法转发消息"; + +/* No comment provided by engineer. */ +"Comment" = "评论"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "当前支持的最大文件大小为 %@。"; + +/* No comment provided by engineer. */ +"Database downgrade required" = "需要数据库降级"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "数据库已加密!"; + +/* No comment provided by engineer. */ +"Database error" = "数据库错误"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "数据库密码与保存在钥匙串中的密码不同。"; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "需要数据库密码才能打开聊天。"; + +/* No comment provided by engineer. */ +"Database upgrade required" = "需要升级数据库"; + +/* No comment provided by engineer. */ +"Error preparing file" = "准备文件时出错"; + +/* No comment provided by engineer. */ +"Error preparing message" = "准备消息时出错"; + +/* No comment provided by engineer. */ +"Error: %@" = "错误:%@"; + +/* No comment provided by engineer. */ +"File error" = "文件错误"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "不兼容的数据库版本"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "无效的迁移确认"; + +/* No comment provided by engineer. */ +"Keychain error" = "钥匙串错误"; + +/* No comment provided by engineer. */ +"Large file!" = "大文件!"; + +/* No comment provided by engineer. */ +"No active profile" = "无活动配置文件"; + +/* No comment provided by engineer. */ +"Ok" = "好的"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "打开应用程序以降级数据库。"; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "打开应用程序以升级数据库。"; + +/* No comment provided by engineer. */ +"Passphrase" = "密码"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "请在 SimpleX 应用程序中创建配置文件"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "选定的聊天首选项禁止此消息。"; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "发送消息所需的时间比预期的要长。"; + +/* No comment provided by engineer. */ +"Sending message…" = "正在发送消息…"; + +/* No comment provided by engineer. */ +"Share" = "共享"; + +/* No comment provided by engineer. */ +"Slow network?" = "网络速度慢?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "未知数据库错误: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "不支持的格式"; + +/* No comment provided by engineer. */ +"Wait" = "等待"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "数据库密码错误"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "您可以在 \"隐私与安全\"/\"SimpleX Lock \"设置中允许共享。"; + diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index b2be41c947..bb45f57992 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -18,7 +18,6 @@ 18415FEFE153C5920BFB7828 /* GroupWelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1841516F0CE5992B0EDFB377 /* GroupWelcomeView.swift */; }; 3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */; }; 3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4727FF621E00354CDD /* CILinkView.swift */; }; - 5C00164428A26FBC0094D739 /* ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C00164328A26FBC0094D739 /* ContextMenu.swift */; }; 5C00168128C4FE760094D739 /* KeyChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C00168028C4FE760094D739 /* KeyChain.swift */; }; 5C029EA82837DBB3004A9677 /* CICallItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA72837DBB3004A9677 /* CICallItemView.swift */; }; 5C029EAA283942EA004A9677 /* CallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA9283942EA004A9677 /* CallController.swift */; }; @@ -36,11 +35,6 @@ 5C35CFC827B2782E00FB6C6D /* BGManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFC727B2782E00FB6C6D /* BGManager.swift */; }; 5C35CFCB27B2E91D00FB6C6D /* NtfManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */; }; 5C36027327F47AD5009F19D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C36027227F47AD5009F19D9 /* AppDelegate.swift */; }; - 5C371E6A2BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C371E652BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a */; }; - 5C371E6B2BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C371E662BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a */; }; - 5C371E6C2BA9FDFA00100AD3 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C371E672BA9FDFA00100AD3 /* libgmpxx.a */; }; - 5C371E6D2BA9FDFA00100AD3 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C371E682BA9FDFA00100AD3 /* libffi.a */; }; - 5C371E6E2BA9FDFA00100AD3 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C371E692BA9FDFA00100AD3 /* libgmp.a */; }; 5C3A88CE27DF50170060F1C2 /* DetermineWidth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88CD27DF50170060F1C2 /* DetermineWidth.swift */; }; 5C3A88D127DF57800060F1C2 /* FramedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88D027DF57800060F1C2 /* FramedItemView.swift */; }; 5C3CCFCC2AE6BD3100C3F0C3 /* ConnectDesktopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3CCFCB2AE6BD3100C3F0C3 /* ConnectDesktopView.swift */; }; @@ -106,7 +100,6 @@ 5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D627A8563F00ACCCDD /* SettingsView.swift */; }; 5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E027A867BA00ACCCDD /* UserProfile.swift */; }; 5CB9250D27A9432000ACCCDD /* ChatListNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB9250C27A9432000ACCCDD /* ChatListNavLink.swift */; }; - 5CBD285A295711D700EC2CF4 /* ImageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */; }; 5CBD285C29575B8E00EC2CF4 /* WhatsNewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD285B29575B8E00EC2CF4 /* WhatsNewView.swift */; }; 5CBE6C12294487F7002D9531 /* VerifyCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBE6C11294487F7002D9531 /* VerifyCodeView.swift */; }; 5CBE6C142944CC12002D9531 /* ScanCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBE6C132944CC12002D9531 /* ScanCodeView.swift */; }; @@ -142,26 +135,25 @@ 5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407827ADB701007B033A /* EmojiItemView.swift */; }; 5CEACCE327DE9246000BD591 /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCE227DE9246000BD591 /* ComposeView.swift */; }; 5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; }; - 5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */; }; 5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */; }; - 5CF9371E2B23429500E1D781 /* ConcurrentQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */; }; 5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */; }; 5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF937212B25034A00E1D781 /* NSESubscriber.swift */; }; 5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; }; - 5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */; }; 5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; 5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; 640417CD2B29B8C200CCB412 /* NewChatMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640417CB2B29B8C200CCB412 /* NewChatMenuButton.swift */; }; 640417CE2B29B8C200CCB412 /* NewChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640417CC2B29B8C200CCB412 /* NewChatView.swift */; }; + 640743612CD360E600158442 /* ChooseServerOperators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640743602CD360E600158442 /* ChooseServerOperators.swift */; }; 6407BA83295DA85D0082BA18 /* CIInvalidJSONView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407BA82295DA85D0082BA18 /* CIInvalidJSONView.swift */; }; 6419EC562AB8BC8B004A607A /* ContextInvitingContactMemberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419EC552AB8BC8B004A607A /* ContextInvitingContactMemberView.swift */; }; 6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */; }; + 642BA82D2CE50495005E9412 /* NewServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 642BA82C2CE50495005E9412 /* NewServerView.swift */; }; 6432857C2925443C00FBE5C8 /* GroupPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */; }; + 643B3B4E2CCFD6400083A2CF /* OperatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 643B3B4D2CCFD6400083A2CF /* OperatorView.swift */; }; 6440CA00288857A10062C672 /* CIEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440C9FF288857A10062C672 /* CIEventView.swift */; }; 6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */; }; 6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6442E0B9287F169300CEC0F9 /* AddGroupView.swift */; }; 6442E0BE2880182D00CEC0F9 /* GroupChatInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6442E0BD2880182D00CEC0F9 /* GroupChatInfoView.swift */; }; - 64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */; }; 64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DCB29FFE3E800E3D48D /* MailView.swift */; }; 6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */; }; 644EFFDE292BCD9D00525D5B /* ComposeVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */; }; @@ -173,27 +165,85 @@ 646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */; }; 647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */; }; 648010AB281ADD15009009B9 /* CIFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648010AA281ADD15009009B9 /* CIFileView.swift */; }; + 648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */; }; 649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; }; 649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; }; 64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; }; 64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; }; 64C06EB52A0A4A7C00792D4D /* ChatItemInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */; }; 64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; }; + 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; }; + 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; }; + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU-ghc9.6.3.a */; }; + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU.a */; }; + 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; }; 64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; }; 64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; }; 64D0C2C629FAC1EC00B38D5F /* AddContactLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C529FAC1EC00B38D5F /* AddContactLearnMore.swift */; }; 64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */; }; + 64EEB0F72C353F1C00972D62 /* ServersSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64EEB0F62C353F1C00972D62 /* ServersSummaryView.swift */; }; 64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; }; - 8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C05382D2B39887E006436DC /* VideoUtils.swift */; }; + 8C01E9C12C8EFC33008A4B0A /* objc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C01E9C02C8EFC33008A4B0A /* objc.m */; }; + 8C01E9C22C8EFF8F008A4B0A /* objc.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C01E9BF2C8EFBB6008A4B0A /* objc.h */; }; 8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; }; + 8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */; }; + 8C74C3E72C1B901900039E77 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C852B072C1086D100BA61E8 /* Color.swift */; }; + 8C74C3E82C1B905B00039E77 /* ChatWallpaperTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C804B1D2C11F966007A63C8 /* ChatWallpaperTypes.swift */; }; + 8C74C3EA2C1B90AF00039E77 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */; }; + 8C74C3EC2C1B92A900039E77 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C74C3EB2C1B92A900039E77 /* Theme.swift */; }; + 8C74C3EE2C1B942300039E77 /* ChatWallpaper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */; }; 8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */; }; 8C7DF3202B7CDB0A00C886D0 /* MigrateFromDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */; }; + 8C7F8F0E2C19C0C100D16888 /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */; }; + 8C8118722C220B5B00E6FC94 /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = 8C8118712C220B5B00E6FC94 /* Yams */; }; + 8C81482C2BD91CD4002CBEC3 /* AudioDevicePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */; }; + 8C9BC2652C240D5200875A27 /* ThemeModeEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */; }; + 8CAD466F2D15A8100078D18F /* ChatScrollHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAD466E2D15A8100078D18F /* ChatScrollHelpers.swift */; }; + 8CAEF1502D11A6A000240F00 /* ChatItemsLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CAEF14F2D11A6A000240F00 /* ChatItemsLoader.swift */; }; + 8CB15EA02CFDA30600C28209 /* ChatItemsMerger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB15E9F2CFDA30600C28209 /* ChatItemsMerger.swift */; }; + 8CB3476C2CF5CFFA006787A5 /* Ink in Frameworks */ = {isa = PBXBuildFile; productRef = 8CB3476B2CF5CFFA006787A5 /* Ink */; }; + 8CB3476E2CF5F58B006787A5 /* ConditionsWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CB3476D2CF5F58B006787A5 /* ConditionsWebView.swift */; }; + 8CBC14862D357CDB00BBD901 /* StorageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CBC14852D357CDB00BBD901 /* StorageView.swift */; }; + 8CC317442D4FEB9B00292A20 /* EndlessScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC317432D4FEB9B00292A20 /* EndlessScrollView.swift */; }; + 8CC317462D4FEBA800292A20 /* ScrollViewCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC317452D4FEBA800292A20 /* ScrollViewCells.swift */; }; + 8CC4ED902BD7B8530078AEE8 /* CallAudioDeviceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */; }; + 8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; }; + 8CE848A32C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE848A22C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift */; }; + B70A39732D24090D00E80A5F /* TagListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70A39722D24090D00E80A5F /* TagListView.swift */; }; + B70CE9E62D4BE5930080F36D /* GroupMentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B70CE9E52D4BE5930080F36D /* GroupMentions.swift */; }; + B728945B2D0C62BF00F7A19A /* ElegantEmojiPicker in Frameworks */ = {isa = PBXBuildFile; productRef = B728945A2D0C62BF00F7A19A /* ElegantEmojiPicker */; }; + B73EFE532CE5FA3500C778EA /* CreateSimpleXAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B73EFE522CE5FA3500C778EA /* CreateSimpleXAddress.swift */; }; + B76E6C312C5C41D900EC11AA /* ContactListNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = B76E6C302C5C41D900EC11AA /* ContactListNavLink.swift */; }; + B79ADAFF2CE4EF930083DFFD /* AddressCreationCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = B79ADAFE2CE4EF930083DFFD /* AddressCreationCard.swift */; }; + CE176F202C87014C00145DBC /* InvertedForegroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE176F1F2C87014C00145DBC /* InvertedForegroundStyle.swift */; }; + CE1EB0E42C459A660099D896 /* ShareAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1EB0E32C459A660099D896 /* ShareAPI.swift */; }; + CE2AD9CE2C452A4D00E844E3 /* ChatUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */; }; + CE3097FB2C4C0C9F00180898 /* ErrorAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */; }; + CE38A29A2C3FCA54005ED185 /* ImageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */; }; + CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = CE38A29B2C3FCD72005ED185 /* SwiftyGif */; }; + CE75480A2C622630009579B7 /* SwipeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7548092C622630009579B7 /* SwipeLabel.swift */; }; + CE984D4B2C36C5D500E3AEFF /* ChatItemClipShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */; }; + CEA6E91C2CBD21B0002B5DB4 /* UserDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA6E91B2CBD21B0002B5DB4 /* UserDefault.swift */; }; + CEDB245B2C9CD71800FBC5F6 /* StickyScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDB245A2C9CD71800FBC5F6 /* StickyScrollView.swift */; }; + CEDE70222C48FD9500233B1F /* SEChatState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDE70212C48FD9500233B1F /* SEChatState.swift */; }; + CEE723AA2C3BD3D70009AE93 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE723A92C3BD3D70009AE93 /* ShareViewController.swift */; }; + CEE723B12C3BD3D70009AE93 /* SimpleX SE.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + CEE723F02C3D25C70009AE93 /* ShareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE723EF2C3D25C70009AE93 /* ShareView.swift */; }; + CEE723F22C3D25ED0009AE93 /* ShareModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE723F12C3D25ED0009AE93 /* ShareModel.swift */; }; + CEFB2EDF2CA1BCC7004B1ECE /* SheetRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFB2EDE2CA1BCC7004B1ECE /* SheetRepresentable.swift */; }; D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; }; D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; }; D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547729AF89AF0022400A /* StoreKit.framework */; }; D741547A29AF90B00022400A /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547929AF90B00022400A /* PushKit.framework */; }; D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; }; D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; }; + E51CC1E62C62085600DB91FE /* OneHandUICard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51CC1E52C62085600DB91FE /* OneHandUICard.swift */; }; + E5DCF8DB2C56FAC1007928CC /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; }; + E5DCF9712C590272007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF96F2C590272007928CC /* Localizable.strings */; }; + E5DCF9842C5902CE007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9822C5902CE007928CC /* Localizable.strings */; }; + E5DCF9982C5906FF007928CC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9962C5906FF007928CC /* InfoPlist.strings */; }; + E5DDBE6E2DC4106800A0EFF0 /* AppAPITypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5DDBE6D2DC4106200A0EFF0 /* AppAPITypes.swift */; }; + E5DDBE702DC4217900A0EFF0 /* NSEAPITypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5DDBE6F2DC4217900A0EFF0 /* NSEAPITypes.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -225,6 +275,20 @@ remoteGlobalIDString = 5CE2BA672845308900EC33A6; remoteInfo = SimpleXChat; }; + CEE723AF2C3BD3D70009AE93 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5CA059BE279559F40002BEB4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CEE723A62C3BD3D70009AE93; + remoteInfo = "SimpleX SE"; + }; + CEE723D12C3C21C90009AE93 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5CA059BE279559F40002BEB4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5CE2BA672845308900EC33A6; + remoteInfo = SimpleXChat; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -245,6 +309,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + CEE723B12C3BD3D70009AE93 /* SimpleX SE.appex in Embed App Extensions */, 5CE2BA9D284555F500EC33A6 /* SimpleX NSE.appex in Embed App Extensions */, ); name = "Embed App Extensions"; @@ -264,7 +329,6 @@ 18415FD2E36F13F596A45BB4 /* CIVideoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CIVideoView.swift; sourceTree = ""; }; 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeLinkView.swift; sourceTree = ""; }; 3CDBCF4727FF621E00354CDD /* CILinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CILinkView.swift; sourceTree = ""; }; - 5C00164328A26FBC0094D739 /* ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenu.swift; sourceTree = ""; }; 5C00168028C4FE760094D739 /* KeyChain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyChain.swift; sourceTree = ""; }; 5C029EA72837DBB3004A9677 /* CICallItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CICallItemView.swift; sourceTree = ""; }; 5C029EA9283942EA004A9677 /* CallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallController.swift; sourceTree = ""; }; @@ -291,11 +355,6 @@ 5C371E4E2BA9AAA200100AD3 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; 5C371E4F2BA9AB6400100AD3 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = "hu.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; 5C371E502BA9AB6400100AD3 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = ""; }; - 5C371E652BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a"; sourceTree = ""; }; - 5C371E662BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a"; sourceTree = ""; }; - 5C371E672BA9FDFA00100AD3 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; - 5C371E682BA9FDFA00100AD3 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; - 5C371E692BA9FDFA00100AD3 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; 5C3A88CD27DF50170060F1C2 /* DetermineWidth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetermineWidth.swift; sourceTree = ""; }; 5C3A88D027DF57800060F1C2 /* FramedItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FramedItemView.swift; sourceTree = ""; }; 5C3CCFCB2AE6BD3100C3F0C3 /* ConnectDesktopView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectDesktopView.swift; sourceTree = ""; }; @@ -392,7 +451,7 @@ 5CB634AC29E46CF70066AD6B /* LocalAuthView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAuthView.swift; sourceTree = ""; }; 5CB634AE29E4BB7D0066AD6B /* SetAppPasscodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetAppPasscodeView.swift; sourceTree = ""; }; 5CB634B029E5EFEA0066AD6B /* PasscodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasscodeView.swift; sourceTree = ""; }; - 5CB924D627A8563F00ACCCDD /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + 5CB924D627A8563F00ACCCDD /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; wrapsLines = 0; }; 5CB924E027A867BA00ACCCDD /* UserProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfile.swift; sourceTree = ""; }; 5CB9250C27A9432000ACCCDD /* ChatListNavLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListNavLink.swift; sourceTree = ""; }; 5CBD285529565CAE00EC2CF4 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; @@ -435,25 +494,24 @@ 5CE6C7B42AAB1527007F345C /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = ""; }; 5CEACCE227DE9246000BD591 /* ComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeView.swift; sourceTree = ""; }; 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = ""; }; - 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPadding.swift; sourceTree = ""; }; 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDeliveryReceiptsView.swift; sourceTree = ""; }; - 5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurrentQueue.swift; sourceTree = ""; }; 5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFileSubscriber.swift; sourceTree = ""; }; 5CF937212B25034A00E1D781 /* NSESubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSESubscriber.swift; sourceTree = ""; }; 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = ""; }; - 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = ""; }; 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; }; 640417CB2B29B8C200CCB412 /* NewChatMenuButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewChatMenuButton.swift; sourceTree = ""; }; 640417CC2B29B8C200CCB412 /* NewChatView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewChatView.swift; sourceTree = ""; }; + 640743602CD360E600158442 /* ChooseServerOperators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChooseServerOperators.swift; sourceTree = ""; }; 6407BA82295DA85D0082BA18 /* CIInvalidJSONView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIInvalidJSONView.swift; sourceTree = ""; }; 6419EC552AB8BC8B004A607A /* ContextInvitingContactMemberView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextInvitingContactMemberView.swift; sourceTree = ""; }; 6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMemberCreatedContactView.swift; sourceTree = ""; }; + 642BA82C2CE50495005E9412 /* NewServerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewServerView.swift; sourceTree = ""; }; 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupPreferencesView.swift; sourceTree = ""; }; + 643B3B4D2CCFD6400083A2CF /* OperatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperatorView.swift; sourceTree = ""; }; 6440C9FF288857A10062C672 /* CIEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIEventView.swift; sourceTree = ""; }; 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupMembersView.swift; sourceTree = ""; }; 6442E0B9287F169300CEC0F9 /* AddGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupView.swift; sourceTree = ""; }; 6442E0BD2880182D00CEC0F9 /* GroupChatInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupChatInfoView.swift; sourceTree = ""; }; - 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSimpleXAddress.swift; sourceTree = ""; }; 64466DCB29FFE3E800E3D48D /* MailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailView.swift; sourceTree = ""; }; 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupLinkView.swift; sourceTree = ""; }; 644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeVoiceView.swift; sourceTree = ""; }; @@ -465,6 +523,7 @@ 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAuthenticationUtils.swift; sourceTree = ""; }; 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMemberInfoView.swift; sourceTree = ""; }; 648010AA281ADD15009009B9 /* CIFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIFileView.swift; sourceTree = ""; }; + 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemForwardingView.swift; sourceTree = ""; }; 6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = ""; }; 649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = ""; }; @@ -472,20 +531,122 @@ 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedItemView.swift; sourceTree = ""; }; 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemInfoView.swift; sourceTree = ""; }; 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = ""; }; + 64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; + 64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU-ghc9.6.3.a"; sourceTree = ""; }; + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU.a"; sourceTree = ""; }; + 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = ""; }; 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = ""; }; 64D0C2C529FAC1EC00B38D5F /* AddContactLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactLearnMore.swift; sourceTree = ""; }; 64DAE1502809D9F5000DA960 /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = ""; }; 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupInvitationView.swift; sourceTree = ""; }; + 64EEB0F62C353F1C00972D62 /* ServersSummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServersSummaryView.swift; sourceTree = ""; }; 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = ""; }; - 8C05382D2B39887E006436DC /* VideoUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoUtils.swift; sourceTree = ""; }; + 8C01E9BF2C8EFBB6008A4B0A /* objc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = objc.h; sourceTree = ""; }; + 8C01E9C02C8EFC33008A4B0A /* objc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = objc.m; sourceTree = ""; }; 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; + 8C74C3EB2C1B92A900039E77 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; + 8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaper.swift; sourceTree = ""; }; 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToDevice.swift; sourceTree = ""; }; 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromDevice.swift; sourceTree = ""; }; + 8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeTypes.swift; sourceTree = ""; }; + 8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = ""; }; + 8C804B1D2C11F966007A63C8 /* ChatWallpaperTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaperTypes.swift; sourceTree = ""; }; + 8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioDevicePicker.swift; sourceTree = ""; }; + 8C852B072C1086D100BA61E8 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = ""; }; + 8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = ""; }; + 8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeModeEditor.swift; sourceTree = ""; }; + 8CAD466E2D15A8100078D18F /* ChatScrollHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatScrollHelpers.swift; sourceTree = ""; }; + 8CAEF14F2D11A6A000240F00 /* ChatItemsLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemsLoader.swift; sourceTree = ""; }; + 8CB15E9F2CFDA30600C28209 /* ChatItemsMerger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemsMerger.swift; sourceTree = ""; }; + 8CB3476D2CF5F58B006787A5 /* ConditionsWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConditionsWebView.swift; sourceTree = ""; }; + 8CBC14852D357CDB00BBD901 /* StorageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageView.swift; sourceTree = ""; }; + 8CC317432D4FEB9B00292A20 /* EndlessScrollView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EndlessScrollView.swift; sourceTree = ""; }; + 8CC317452D4FEBA800292A20 /* ScrollViewCells.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScrollViewCells.swift; sourceTree = ""; }; + 8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallAudioDeviceManager.swift; sourceTree = ""; }; + 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = ""; }; + 8CE848A22C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableChatItemToolbars.swift; sourceTree = ""; }; + B70A39722D24090D00E80A5F /* TagListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagListView.swift; sourceTree = ""; }; + B70CE9E52D4BE5930080F36D /* GroupMentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMentions.swift; sourceTree = ""; }; + B73EFE522CE5FA3500C778EA /* CreateSimpleXAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateSimpleXAddress.swift; sourceTree = ""; }; + B76E6C302C5C41D900EC11AA /* ContactListNavLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactListNavLink.swift; sourceTree = ""; }; + B79ADAFE2CE4EF930083DFFD /* AddressCreationCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressCreationCard.swift; sourceTree = ""; }; + CE176F1F2C87014C00145DBC /* InvertedForegroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvertedForegroundStyle.swift; sourceTree = ""; }; + CE1EB0E32C459A660099D896 /* ShareAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAPI.swift; sourceTree = ""; }; + CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUtils.swift; sourceTree = ""; }; + CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorAlert.swift; sourceTree = ""; }; + CE7548092C622630009579B7 /* SwipeLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeLabel.swift; sourceTree = ""; }; + CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemClipShape.swift; sourceTree = ""; }; + CEA6E91B2CBD21B0002B5DB4 /* UserDefault.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefault.swift; sourceTree = ""; }; + CEDB245A2C9CD71800FBC5F6 /* StickyScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickyScrollView.swift; sourceTree = ""; }; + CEDE70212C48FD9500233B1F /* SEChatState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SEChatState.swift; sourceTree = ""; }; + CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "SimpleX SE.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + CEE723A92C3BD3D70009AE93 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; + CEE723AE2C3BD3D70009AE93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CEE723D42C3C21F50009AE93 /* SimpleX SE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "SimpleX SE.entitlements"; sourceTree = ""; }; + CEE723EF2C3D25C70009AE93 /* ShareView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareView.swift; sourceTree = ""; }; + CEE723F12C3D25ED0009AE93 /* ShareModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareModel.swift; sourceTree = ""; }; + CEFB2EDE2CA1BCC7004B1ECE /* SheetRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetRepresentable.swift; sourceTree = ""; }; D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = ""; }; D741547729AF89AF0022400A /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; }; D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; }; D7AA2C3429A936B400737B40 /* MediaEncryption.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = MediaEncryption.playground; path = Shared/MediaEncryption.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + E51CC1E52C62085600DB91FE /* OneHandUICard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneHandUICard.swift; sourceTree = ""; }; + E5DCF9702C590272007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9722C590274007928CC /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9732C590275007928CC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + E5DCF9742C590276007928CC /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9752C590277007928CC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9762C590278007928CC /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9772C590279007928CC /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9782C590279007928CC /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9792C59027A007928CC /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97A2C59027A007928CC /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97B2C59027B007928CC /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97C2C59027B007928CC /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97D2C59027C007928CC /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97E2C59027C007928CC /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97F2C59027D007928CC /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9802C59027D007928CC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9812C59027D007928CC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9832C5902CE007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9852C5902D4007928CC /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9862C5902D5007928CC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + E5DCF9872C5902D8007928CC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9882C5902DC007928CC /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9892C5902DC007928CC /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98A2C5902DD007928CC /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98B2C5902DD007928CC /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98C2C5902DE007928CC /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98D2C5902DE007928CC /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98E2C5902E0007928CC /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98F2C5902E0007928CC /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9902C5902E1007928CC /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9912C5902E1007928CC /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9922C5902E2007928CC /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9932C5902E2007928CC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9942C5902E3007928CC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9952C59067B007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9972C5906FF007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9992C59072A007928CC /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF99A2C59072B007928CC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; + E5DCF99B2C59072B007928CC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF99C2C59072C007928CC /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF99D2C59072D007928CC /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF99E2C59072E007928CC /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF99F2C59072E007928CC /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A02C59072F007928CC /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A12C59072F007928CC /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A22C590730007928CC /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A32C590730007928CC /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A42C590731007928CC /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A52C590731007928CC /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A62C590731007928CC /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A72C590732007928CC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A82C590732007928CC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DDBE6D2DC4106200A0EFF0 /* AppAPITypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAPITypes.swift; sourceTree = ""; }; + E5DDBE6F2DC4217900A0EFF0 /* NSEAPITypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEAPITypes.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -494,6 +655,9 @@ buildActionMask = 2147483647; files = ( 5CE2BA702845308900EC33A6 /* SimpleXChat.framework in Frameworks */, + B728945B2D0C62BF00F7A19A /* ElegantEmojiPicker in Frameworks */, + 8C8118722C220B5B00E6FC94 /* Yams in Frameworks */, + 8CB3476C2CF5CFFA006787A5 /* Ink in Frameworks */, D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */, D7197A1829AE89660055C05A /* WebRTC in Frameworks */, D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */, @@ -523,13 +687,22 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5C371E6A2BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a in Frameworks */, - 5C371E6E2BA9FDFA00100AD3 /* libgmp.a in Frameworks */, - 5C371E6D2BA9FDFA00100AD3 /* libffi.a in Frameworks */, - 5C371E6C2BA9FDFA00100AD3 /* libgmpxx.a in Frameworks */, - 5C371E6B2BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a in Frameworks */, 5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */, 5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */, + 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */, + 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */, + 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */, + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU-ghc9.6.3.a in Frameworks */, + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU.a in Frameworks */, + CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E5DCF8DA2C56FABA007928CC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E5DCF8DB2C56FAC1007928CC /* SimpleXChat.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -547,6 +720,8 @@ 5C55A922283CEDE600C4E99E /* SoundPlayer.swift */, 18415323A4082FC92887F906 /* WebRTCClient.swift */, 18415B08031E8FB0F7FC27F9 /* CallViewRenderers.swift */, + 8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */, + 8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */, ); path = Call; sourceTree = ""; @@ -554,6 +729,7 @@ 5C2E260D27A30E2400F70299 /* Views */ = { isa = PBXGroup; children = ( + B76E6C2F2C5C41C300EC11AA /* Contacts */, 5CB0BA8C282711BC00B3292C /* Onboarding */, 3C714775281C080100CB4D4B /* Call */, 5C971E1F27AEBF7000C8A3CE /* Helpers */, @@ -573,6 +749,8 @@ 5C5F4AC227A5E9AF00B51EF1 /* Chat */ = { isa = PBXGroup; children = ( + 8CC317452D4FEBA800292A20 /* ScrollViewCells.swift */, + 8CC317432D4FEB9B00292A20 /* EndlessScrollView.swift */, 6440CA01288AEC770062C672 /* Group */, 5CE4407427ADB657007B033A /* ChatItem */, 5CEACCE527DE977C000BD591 /* ComposeMessage */, @@ -585,6 +763,11 @@ 5CBE6C11294487F7002D9531 /* VerifyCodeView.swift */, 5CBE6C132944CC12002D9531 /* ScanCodeView.swift */, 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */, + 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */, + 8CE848A22C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift */, + 8CB15E9F2CFDA30600C28209 /* ChatItemsMerger.swift */, + 8CAEF14F2D11A6A000240F00 /* ChatItemsLoader.swift */, + 8CAD466E2D15A8100078D18F /* ChatScrollHelpers.swift */, ); path = Chat; sourceTree = ""; @@ -592,11 +775,11 @@ 5C764E5C279C70B7000C6508 /* Libraries */ = { isa = PBXGroup; children = ( - 5C371E682BA9FDFA00100AD3 /* libffi.a */, - 5C371E692BA9FDFA00100AD3 /* libgmp.a */, - 5C371E672BA9FDFA00100AD3 /* libgmpxx.a */, - 5C371E662BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a */, - 5C371E652BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a */, + 64C829992D54AEEE006B9E89 /* libffi.a */, + 64C829982D54AEED006B9E89 /* libgmp.a */, + 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */, + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU-ghc9.6.3.a */, + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.3.7.0-3RUUqzi5CrDKdTTh7MfNU.a */, ); path = Libraries; sourceTree = ""; @@ -616,6 +799,7 @@ 5C764E87279CBC8E000C6508 /* Model */ = { isa = PBXGroup; children = ( + E5DDBE6D2DC4106200A0EFF0 /* AppAPITypes.swift */, 5C764E88279CBCB3000C6508 /* ChatModel.swift */, 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */, 5C35CFC727B2782E00FB6C6D /* BGManager.swift */, @@ -624,7 +808,7 @@ 5CF937212B25034A00E1D781 /* NSESubscriber.swift */, 5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */, 5C93293E2928E0FD0090FFF9 /* AudioRecPlay.swift */, - 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */, + 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */, ); path = Model; sourceTree = ""; @@ -641,15 +825,21 @@ 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */, 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */, 5C6BA666289BD954009B8ECC /* DismissSheets.swift */, - 5C00164328A26FBC0094D739 /* ContextMenu.swift */, 5CA7DFC229302AF000F7FDDE /* AppSheet.swift */, 18415A7F0F189D87DEFEABCA /* PressedButtonStyle.swift */, 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */, 18415DAAAD1ADBEDB0EDA852 /* VideoPlayerView.swift */, 64466DCB29FFE3E800E3D48D /* MailView.swift */, 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */, - 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */, - 8C05382D2B39887E006436DC /* VideoUtils.swift */, + 8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */, + 8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */, + 8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */, + CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */, + CE7548092C622630009579B7 /* SwipeLabel.swift */, + CE176F1F2C87014C00145DBC /* InvertedForegroundStyle.swift */, + CEDB245A2C9CD71800FBC5F6 /* StickyScrollView.swift */, + CEFB2EDE2CA1BCC7004B1ECE /* SheetRepresentable.swift */, + CEA6E91B2CBD21B0002B5DB4 /* UserDefault.swift */, ); path = Helpers; sourceTree = ""; @@ -664,6 +854,7 @@ 5C764E5C279C70B7000C6508 /* Libraries */, 5CA059C2279559F40002BEB4 /* Shared */, 5CDCAD462818589900503DA2 /* SimpleX NSE */, + CEE723A82C3BD3D70009AE93 /* SimpleX SE */, 5CA059DA279559F40002BEB4 /* Tests iOS */, 5CE2BA692845308900EC33A6 /* SimpleXChat */, 5CA059CB279559F40002BEB4 /* Products */, @@ -674,6 +865,7 @@ 5CA059C2279559F40002BEB4 /* Shared */ = { isa = PBXGroup; children = ( + 8C74C3E92C1B909200039E77 /* Theme */, 5CA059C3279559F40002BEB4 /* SimpleXApp.swift */, 5C36027227F47AD5009F19D9 /* AppDelegate.swift */, 5CA059C4279559F40002BEB4 /* ContentView.swift */, @@ -693,6 +885,7 @@ 5CA059D7279559F40002BEB4 /* Tests iOS.xctest */, 5CDCAD452818589900503DA2 /* SimpleX NSE.appex */, 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */, + CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */, ); name = Products; sourceTree = ""; @@ -717,13 +910,15 @@ 5CB0BA8C282711BC00B3292C /* Onboarding */ = { isa = PBXGroup; children = ( + B73EFE522CE5FA3500C778EA /* CreateSimpleXAddress.swift */, 5CB0BA8D2827126500B3292C /* OnboardingView.swift */, 5CB0BA8F282713D900B3292C /* SimpleXInfo.swift */, 5CB0BA992827FD8800B3292C /* HowItWorks.swift */, 5CB0BA91282713FD00B3292C /* CreateProfile.swift */, - 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */, 5C9A5BDA2871E05400A5B906 /* SetNotificationsMode.swift */, 5CBD285B29575B8E00EC2CF4 /* WhatsNewView.swift */, + 640743602CD360E600158442 /* ChooseServerOperators.swift */, + B79ADAFE2CE4EF930083DFFD /* AddressCreationCard.swift */, ); path = Onboarding; sourceTree = ""; @@ -754,10 +949,9 @@ 5CB924DF27A8678B00ACCCDD /* UserSettings */ = { isa = PBXGroup; children = ( + 643B3B4C2CCFD34B0083A2CF /* NetworkAndServers */, 5CB924D627A8563F00ACCCDD /* SettingsView.swift */, 5CB346E62868D76D001FD2EF /* NotificationsView.swift */, - 5C9C2DA6289957AE00CC63B1 /* AdvancedNetworkSettings.swift */, - 5C9C2DA82899DA6F00CC63B1 /* NetworkAndServers.swift */, 5CADE79929211BB900072E13 /* PreferencesView.swift */, 5C5DB70D289ABDD200730FFF /* AppearanceSettings.swift */, 5C05DF522840AA1D00C683F9 /* CallSettings.swift */, @@ -765,9 +959,6 @@ 5CC036DF29C488D500C0EF20 /* HiddenProfileView.swift */, 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */, 5C3F1D57284363C400EC8A82 /* PrivacySettings.swift */, - 5C93292E29239A170090FFF9 /* ProtocolServersView.swift */, - 5C93293029239BED0090FFF9 /* ProtocolServerView.swift */, - 5C9329402929248A0090FFF9 /* ScanProtocolServer.swift */, 5CB2084E28DA4B4800D024EC /* RTCServers.swift */, 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */, 18415845648CA4F5A8BCA272 /* UserProfilesView.swift */, @@ -777,6 +968,7 @@ 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */, 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */, 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */, + 8CBC14852D357CDB00BBD901 /* StorageView.swift */, ); path = UserSettings; sourceTree = ""; @@ -792,6 +984,9 @@ 5C13730A28156D2700F43030 /* ContactConnectionView.swift */, 5C10D88728EED12E00E58BF0 /* ContactConnectionInfo.swift */, 18415835CBD939A9ABDC108A /* UserPicker.swift */, + 64EEB0F62C353F1C00972D62 /* ServersSummaryView.swift */, + E51CC1E52C62085600DB91FE /* OneHandUICard.swift */, + B70A39722D24090D00E80A5F /* TagListView.swift */, ); path = ChatList; sourceTree = ""; @@ -800,10 +995,11 @@ isa = PBXGroup; children = ( 5CDCAD5128186DE400503DA2 /* SimpleX NSE.entitlements */, - 5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */, + E5DDBE6F2DC4217900A0EFF0 /* NSEAPITypes.swift */, 5CDCAD472818589900503DA2 /* NotificationService.swift */, 5CDCAD492818589900503DA2 /* Info.plist */, 5CB0BA862826CB3A00B3292C /* InfoPlist.strings */, + E5DCF9822C5902CE007928CC /* Localizable.strings */, ); path = "SimpleX NSE"; sourceTree = ""; @@ -811,13 +1007,17 @@ 5CE2BA692845308900EC33A6 /* SimpleXChat */ = { isa = PBXGroup; children = ( + 8C86EBE32C0DAE3700E12243 /* Theme */, 5CDCAD5228186F9500503DA2 /* AppGroup.swift */, 5CDCAD7228188CFF00503DA2 /* ChatTypes.swift */, 5CDCAD7428188D2900503DA2 /* APITypes.swift */, 5C5E5D3C282447AB00B0488A /* CallTypes.swift */, - 5C9FD96A27A56D4D0075386C /* JSON.swift */, 5CDCAD7D2818941F00503DA2 /* API.swift */, + CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */, + 5C9FD96A27A56D4D0075386C /* JSON.swift */, 5CDCAD80281A7E2700503DA2 /* Notifications.swift */, + 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */, + CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */, 64DAE1502809D9F5000DA960 /* FileUtils.swift */, 5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */, 5C00168028C4FE760094D739 /* KeyChain.swift */, @@ -828,6 +1028,8 @@ 5CE2BA96284537A800EC33A6 /* dummy.m */, 5CD67B8D2B0E858A00C510B1 /* hs_init.h */, 5CD67B8E2B0E858A00C510B1 /* hs_init.c */, + 8C01E9BF2C8EFBB6008A4B0A /* objc.h */, + 8C01E9C02C8EFC33008A4B0A /* objc.m */, ); path = SimpleXChat; sourceTree = ""; @@ -882,7 +1084,6 @@ isa = PBXGroup; children = ( 5C4B3B09285FB130003915F2 /* DatabaseView.swift */, - 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */, 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */, 5C9CC7A828C532AB00BEF955 /* DatabaseErrorView.swift */, 5C9CC7AC28C55D7800BEF955 /* DatabaseEncryptionView.swift */, @@ -890,6 +1091,21 @@ path = Database; sourceTree = ""; }; + 643B3B4C2CCFD34B0083A2CF /* NetworkAndServers */ = { + isa = PBXGroup; + children = ( + 5C9329402929248A0090FFF9 /* ScanProtocolServer.swift */, + 642BA82C2CE50495005E9412 /* NewServerView.swift */, + 5C93293029239BED0090FFF9 /* ProtocolServerView.swift */, + 5C93292E29239A170090FFF9 /* ProtocolServersView.swift */, + 643B3B4D2CCFD6400083A2CF /* OperatorView.swift */, + 5C9C2DA6289957AE00CC63B1 /* AdvancedNetworkSettings.swift */, + 5C9C2DA82899DA6F00CC63B1 /* NetworkAndServers.swift */, + 8CB3476D2CF5F58B006787A5 /* ConditionsWebView.swift */, + ); + path = NetworkAndServers; + sourceTree = ""; + }; 6440CA01288AEC770062C672 /* Group */ = { isa = PBXGroup; children = ( @@ -900,10 +1116,20 @@ 5C9C2DA42894777E00CC63B1 /* GroupProfileView.swift */, 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */, 1841516F0CE5992B0EDFB377 /* GroupWelcomeView.swift */, + B70CE9E52D4BE5930080F36D /* GroupMentions.swift */, ); path = Group; sourceTree = ""; }; + 8C74C3E92C1B909200039E77 /* Theme */ = { + isa = PBXGroup; + children = ( + 8C86EBE42C0DAE4F00E12243 /* ThemeManager.swift */, + 8C74C3EB2C1B92A900039E77 /* Theme.swift */, + ); + path = Theme; + sourceTree = ""; + }; 8C7D94982B8894D300B7B9E1 /* Migration */ = { isa = PBXGroup; children = ( @@ -913,6 +1139,40 @@ path = Migration; sourceTree = ""; }; + 8C86EBE32C0DAE3700E12243 /* Theme */ = { + isa = PBXGroup; + children = ( + 8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */, + 8C852B072C1086D100BA61E8 /* Color.swift */, + 8C804B1D2C11F966007A63C8 /* ChatWallpaperTypes.swift */, + ); + path = Theme; + sourceTree = ""; + }; + B76E6C2F2C5C41C300EC11AA /* Contacts */ = { + isa = PBXGroup; + children = ( + B76E6C302C5C41D900EC11AA /* ContactListNavLink.swift */, + ); + path = Contacts; + sourceTree = ""; + }; + CEE723A82C3BD3D70009AE93 /* SimpleX SE */ = { + isa = PBXGroup; + children = ( + CEE723D42C3C21F50009AE93 /* SimpleX SE.entitlements */, + CEE723AE2C3BD3D70009AE93 /* Info.plist */, + CEDE70212C48FD9500233B1F /* SEChatState.swift */, + CE1EB0E32C459A660099D896 /* ShareAPI.swift */, + CEE723F12C3D25ED0009AE93 /* ShareModel.swift */, + CEE723EF2C3D25C70009AE93 /* ShareView.swift */, + CEE723A92C3BD3D70009AE93 /* ShareViewController.swift */, + E5DCF96F2C590272007928CC /* Localizable.strings */, + E5DCF9962C5906FF007928CC /* InfoPlist.strings */, + ); + path = "SimpleX SE"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -922,6 +1182,7 @@ files = ( 5CE2BA77284530BF00EC33A6 /* SimpleXChat.h in Headers */, 5CD67B8F2B0E858A00C510B1 /* hs_init.h in Headers */, + 8C01E9C22C8EFF8F008A4B0A /* objc.h in Headers */, 5CE2BA952845354B00EC33A6 /* SimpleX.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -944,6 +1205,7 @@ dependencies = ( 5CE2BA6F2845308900EC33A6 /* PBXTargetDependency */, 5CE2BA9F284555F500EC33A6 /* PBXTargetDependency */, + CEE723B02C3BD3D70009AE93 /* PBXTargetDependency */, ); name = "SimpleX (iOS)"; packageProductDependencies = ( @@ -951,6 +1213,9 @@ D77B92DB2952372200A5A1CC /* SwiftyGif */, D7F0E33829964E7E0068AF69 /* LZString */, D7197A1729AE89660055C05A /* WebRTC */, + 8C8118712C220B5B00E6FC94 /* Yams */, + 8CB3476B2CF5CFFA006787A5 /* Ink */, + B728945A2D0C62BF00F7A19A /* ElegantEmojiPicker */, ); productName = "SimpleX (iOS)"; productReference = 5CA059CA279559F40002BEB4 /* SimpleX.app */; @@ -1007,11 +1272,30 @@ ); name = SimpleXChat; packageProductDependencies = ( + CE38A29B2C3FCD72005ED185 /* SwiftyGif */, ); productName = SimpleXChat; productReference = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; productType = "com.apple.product-type.framework"; }; + CEE723A62C3BD3D70009AE93 /* SimpleX SE */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEE723B42C3BD3D70009AE93 /* Build configuration list for PBXNativeTarget "SimpleX SE" */; + buildPhases = ( + CEE723A32C3BD3D70009AE93 /* Sources */, + CEE723A52C3BD3D70009AE93 /* Resources */, + E5DCF8DA2C56FABA007928CC /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + CEE723D22C3C21C90009AE93 /* PBXTargetDependency */, + ); + name = "SimpleX SE"; + productName = "SimpleX SE"; + productReference = CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1019,7 +1303,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1330; + LastSwiftUpdateCheck = 1540; LastUpgradeCheck = 1340; ORGANIZATIONNAME = "SimpleX Chat"; TargetAttributes = { @@ -1039,6 +1323,9 @@ CreatedOnToolsVersion = 13.3; LastSwiftMigration = 1330; }; + CEE723A62C3BD3D70009AE93 = { + CreatedOnToolsVersion = 15.4; + }; }; }; buildConfigurationList = 5CA059C1279559F40002BEB4 /* Build configuration list for PBXProject "SimpleX" */; @@ -1071,6 +1358,9 @@ D77B92DA2952372200A5A1CC /* XCRemoteSwiftPackageReference "SwiftyGif" */, D7F0E33729964E7D0068AF69 /* XCRemoteSwiftPackageReference "lzstring-swift" */, D7197A1629AE89660055C05A /* XCRemoteSwiftPackageReference "WebRTC" */, + 8C73C1162C21E17B00892670 /* XCRemoteSwiftPackageReference "Yams" */, + 8CB3476A2CF5CFFA006787A5 /* XCRemoteSwiftPackageReference "ink" */, + B72894592D0C62BF00F7A19A /* XCRemoteSwiftPackageReference "Elegant-Emoji-Picker" */, ); productRefGroup = 5CA059CB279559F40002BEB4 /* Products */; projectDirPath = ""; @@ -1079,6 +1369,7 @@ 5CA059C9279559F40002BEB4 /* SimpleX (iOS) */, 5CA059D6279559F40002BEB4 /* Tests iOS */, 5CDCAD442818589900503DA2 /* SimpleX NSE */, + CEE723A62C3BD3D70009AE93 /* SimpleX SE */, 5CE2BA672845308900EC33A6 /* SimpleXChat */, ); }; @@ -1108,6 +1399,7 @@ buildActionMask = 2147483647; files = ( 5CB0BA882826CB3A00B3292C /* InfoPlist.strings in Resources */, + E5DCF9842C5902CE007928CC /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1118,6 +1410,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + CEE723A52C3BD3D70009AE93 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E5DCF9712C590272007928CC /* Localizable.strings in Resources */, + E5DCF9982C5906FF007928CC /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1126,13 +1427,17 @@ buildActionMask = 2147483647; files = ( 64C06EB52A0A4A7C00792D4D /* ChatItemInfoView.swift in Sources */, + 8CC317442D4FEB9B00292A20 /* EndlessScrollView.swift in Sources */, 640417CE2B29B8C200CCB412 /* NewChatView.swift in Sources */, 6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */, + 640743612CD360E600158442 /* ChooseServerOperators.swift in Sources */, 5C3F1D58284363C400EC8A82 /* PrivacySettings.swift in Sources */, 5C55A923283CEDE600C4E99E /* SoundPlayer.swift in Sources */, 5C93292F29239A170090FFF9 /* ProtocolServersView.swift in Sources */, 5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */, + B79ADAFF2CE4EF930083DFFD /* AddressCreationCard.swift in Sources */, 5CEACCE327DE9246000BD591 /* ComposeView.swift in Sources */, + E51CC1E62C62085600DB91FE /* OneHandUICard.swift in Sources */, 5C65DAF929D0CC20003CEE45 /* DeveloperView.swift in Sources */, 5C36027327F47AD5009F19D9 /* AppDelegate.swift in Sources */, 5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */, @@ -1140,9 +1445,10 @@ 5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */, 644EFFE0292CFD7F00525D5B /* CIVoiceView.swift in Sources */, 6432857C2925443C00FBE5C8 /* GroupPreferencesView.swift in Sources */, + 8CC317462D4FEBA800292A20 /* ScrollViewCells.swift in Sources */, 5C93293129239BED0090FFF9 /* ProtocolServerView.swift in Sources */, 5C9CC7AD28C55D7800BEF955 /* DatabaseEncryptionView.swift in Sources */, - 5CBD285A295711D700EC2CF4 /* ImageUtils.swift in Sources */, + 8C74C3EC2C1B92A900039E77 /* Theme.swift in Sources */, 6419EC562AB8BC8B004A607A /* ContextInvitingContactMemberView.swift in Sources */, 5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */, 8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */, @@ -1157,16 +1463,19 @@ 644EFFE2292D089800525D5B /* FramedCIVoiceView.swift in Sources */, 5C4B3B0A285FB130003915F2 /* DatabaseView.swift in Sources */, 5CB2084F28DA4B4800D024EC /* RTCServers.swift in Sources */, + B73EFE532CE5FA3500C778EA /* CreateSimpleXAddress.swift in Sources */, 5CB634AF29E4BB7D0066AD6B /* SetAppPasscodeView.swift in Sources */, 5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */, 5CBE6C12294487F7002D9531 /* VerifyCodeView.swift in Sources */, 3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */, - 64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */, + 648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */, 3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */, 5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */, + B76E6C312C5C41D900EC11AA /* ContactListNavLink.swift in Sources */, 5C10D88A28F187F300E58BF0 /* FullScreenMediaView.swift in Sources */, D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */, - 5C00164428A26FBC0094D739 /* ContextMenu.swift in Sources */, + B70CE9E62D4BE5930080F36D /* GroupMentions.swift in Sources */, + CE984D4B2C36C5D500E3AEFF /* ChatItemClipShape.swift in Sources */, 64D0C2C629FAC1EC00B38D5F /* AddContactLearnMore.swift in Sources */, 5C3A88D127DF57800060F1C2 /* FramedItemView.swift in Sources */, 5C65F343297D45E100B67AF3 /* VersionView.swift in Sources */, @@ -1174,11 +1483,14 @@ 5CB0BA90282713D900B3292C /* SimpleXInfo.swift in Sources */, 5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */, 5CC868F329EB540C0017BBFD /* CIRcvDecryptionError.swift in Sources */, + 8C7F8F0E2C19C0C100D16888 /* ViewModifiers.swift in Sources */, 5C35CFCB27B2E91D00FB6C6D /* NtfManager.swift in Sources */, 5C9D13A3282187BB00AB8B43 /* WebRTC.swift in Sources */, + 8CBC14862D357CDB00BBD901 /* StorageView.swift in Sources */, 5C9A5BDB2871E05400A5B906 /* SetNotificationsMode.swift in Sources */, 5CB0BA8E2827126500B3292C /* OnboardingView.swift in Sources */, 6442E0BE2880182D00CEC0F9 /* GroupChatInfoView.swift in Sources */, + 8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */, 5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */, 5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */, 5CA7DFC329302AF000F7FDDE /* AppSheet.swift in Sources */, @@ -1197,60 +1509,73 @@ 5CB346E72868D76D001FD2EF /* NotificationsView.swift in Sources */, 647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */, 646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */, + 8C74C3EA2C1B90AF00039E77 /* ThemeManager.swift in Sources */, 5C7505A227B65FDB00BE3227 /* CIMetaView.swift in Sources */, - 5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */, 5C35CFC827B2782E00FB6C6D /* BGManager.swift in Sources */, 5CB634B129E5EFEA0066AD6B /* PasscodeView.swift in Sources */, 8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */, 5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */, + CEFB2EDF2CA1BCC7004B1ECE /* SheetRepresentable.swift in Sources */, 5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */, 6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */, 5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */, 6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */, 64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */, + 8CE848A32C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift in Sources */, 64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */, + CE75480A2C622630009579B7 /* SwipeLabel.swift in Sources */, 5C971E2127AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */, 5C55A921283CCCB700C4E99E /* IncomingCallView.swift in Sources */, 6454036F2822A9750090DDFF /* ComposeFileView.swift in Sources */, 5C5DB70E289ABDD200730FFF /* AppearanceSettings.swift in Sources */, 5C5F2B6D27EBC3FE006A9D5F /* ImagePicker.swift in Sources */, + 8C74C3EE2C1B942300039E77 /* ChatWallpaper.swift in Sources */, 5C3CCFCC2AE6BD3100C3F0C3 /* ConnectDesktopView.swift in Sources */, 5C9C2DA92899DA6F00CC63B1 /* NetworkAndServers.swift in Sources */, 5C6BA667289BD954009B8ECC /* DismissSheets.swift in Sources */, 5C577F7D27C83AA10006112D /* MarkdownHelp.swift in Sources */, 6407BA83295DA85D0082BA18 /* CIInvalidJSONView.swift in Sources */, + 8CAD466F2D15A8100078D18F /* ChatScrollHelpers.swift in Sources */, 644EFFDE292BCD9D00525D5B /* ComposeVoiceView.swift in Sources */, 5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */, 64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */, 6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */, + E5DDBE6E2DC4106800A0EFF0 /* AppAPITypes.swift in Sources */, + 8C9BC2652C240D5200875A27 /* ThemeModeEditor.swift in Sources */, 5CB346E92869E8BA001FD2EF /* PushEnvironment.swift in Sources */, 5C55A91F283AD0E400C4E99E /* CallManager.swift in Sources */, - 5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */, 649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */, - 8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */, 5CADE79C292131E900072E13 /* ContactPreferencesView.swift in Sources */, + CEA6E91C2CBD21B0002B5DB4 /* UserDefault.swift in Sources */, 5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */, + 8CAEF1502D11A6A000240F00 /* ChatItemsLoader.swift in Sources */, 5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */, 5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */, + 8C81482C2BD91CD4002CBEC3 /* AudioDevicePicker.swift in Sources */, 5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */, 5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */, 5C971E1D27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */, 5CBD285C29575B8E00EC2CF4 /* WhatsNewView.swift in Sources */, + 8CB3476E2CF5F58B006787A5 /* ConditionsWebView.swift in Sources */, 5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */, 5C5E5D3B2824468B00B0488A /* ActiveCallView.swift in Sources */, + B70A39732D24090D00E80A5F /* TagListView.swift in Sources */, 5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */, 6440CA00288857A10062C672 /* CIEventView.swift in Sources */, 5CB0BA92282713FD00B3292C /* CreateProfile.swift in Sources */, 5C5F2B7027EBC704006A9D5F /* ProfileImage.swift in Sources */, 5C9329412929248A0090FFF9 /* ScanProtocolServer.swift in Sources */, 8C7DF3202B7CDB0A00C886D0 /* MigrateFromDevice.swift in Sources */, + 64EEB0F72C353F1C00972D62 /* ServersSummaryView.swift in Sources */, 64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */, 5C93293F2928E0FD0090FFF9 /* AudioRecPlay.swift in Sources */, 5C029EA82837DBB3004A9677 /* CICallItemView.swift in Sources */, 5CE4407227ADB1D0007B033A /* Emoji.swift in Sources */, + CEDB245B2C9CD71800FBC5F6 /* StickyScrollView.swift in Sources */, 5C9CC7A928C532AB00BEF955 /* DatabaseErrorView.swift in Sources */, 5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */, 64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */, + CE176F202C87014C00145DBC /* InvertedForegroundStyle.swift in Sources */, 5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */, 5C9C2DA7289957AE00CC63B1 /* AdvancedNetworkSettings.swift in Sources */, 5CADE79A29211BB900072E13 /* PreferencesView.swift in Sources */, @@ -1261,12 +1586,16 @@ 18415B0585EB5A9A0A7CA8CD /* PressedButtonStyle.swift in Sources */, 1841560FD1CD447955474C1D /* UserProfilesView.swift in Sources */, 64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */, + 8CC4ED902BD7B8530078AEE8 /* CallAudioDeviceManager.swift in Sources */, 18415C6C56DBCEC2CBBD2F11 /* WebRTCClient.swift in Sources */, + 8CB15EA02CFDA30600C28209 /* ChatItemsMerger.swift in Sources */, 184152CEF68D2336FC2EBCB0 /* CallViewRenderers.swift in Sources */, 5CB634AD29E46CF70066AD6B /* LocalAuthView.swift in Sources */, 18415FEFE153C5920BFB7828 /* GroupWelcomeView.swift in Sources */, 18415F9A2D551F9757DA4654 /* CIVideoView.swift in Sources */, + 642BA82D2CE50495005E9412 /* NewServerView.swift in Sources */, 184158C131FDB829D8A117EA /* VideoPlayerView.swift in Sources */, + 643B3B4E2CCFD6400083A2CF /* OperatorView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1284,8 +1613,8 @@ buildActionMask = 2147483647; files = ( 5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */, + E5DDBE702DC4217900A0EFF0 /* NSEAPITypes.swift in Sources */, 5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */, - 5CF9371E2B23429500E1D781 /* ConcurrentQueue.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1294,22 +1623,41 @@ buildActionMask = 2147483647; files = ( 5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */, + CE3097FB2C4C0C9F00180898 /* ErrorAlert.swift in Sources */, 5C00168128C4FE760094D739 /* KeyChain.swift in Sources */, 5CE2BA97284537A800EC33A6 /* dummy.m in Sources */, 5CE2BA922845340900EC33A6 /* FileUtils.swift in Sources */, + 8C74C3E72C1B901900039E77 /* Color.swift in Sources */, 5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */, 5CE2BA91284533A300EC33A6 /* Notifications.swift in Sources */, + 8C01E9C12C8EFC33008A4B0A /* objc.m in Sources */, 5CE2BA79284530CC00EC33A6 /* SimpleXChat.docc in Sources */, 5CE2BA90284533A300EC33A6 /* JSON.swift in Sources */, 5CE2BA8B284533A300EC33A6 /* ChatTypes.swift in Sources */, 5CE2BA8F284533A300EC33A6 /* APITypes.swift in Sources */, + CE38A29A2C3FCA54005ED185 /* ImageUtils.swift in Sources */, 5C9D811A2AA8727A001D49FD /* CryptoFile.swift in Sources */, 5CE2BA8C284533A300EC33A6 /* AppGroup.swift in Sources */, + 8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */, 5CE2BA8D284533A300EC33A6 /* CallTypes.swift in Sources */, + 8C74C3E82C1B905B00039E77 /* ChatWallpaperTypes.swift in Sources */, + CE2AD9CE2C452A4D00E844E3 /* ChatUtils.swift in Sources */, 5CE2BA8E284533A300EC33A6 /* API.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; + CEE723A32C3BD3D70009AE93 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEDE70222C48FD9500233B1F /* SEChatState.swift in Sources */, + CEE723F02C3D25C70009AE93 /* ShareView.swift in Sources */, + CE1EB0E42C459A660099D896 /* ShareAPI.swift in Sources */, + CEE723F22C3D25ED0009AE93 /* ShareModel.swift in Sources */, + CEE723AA2C3BD3D70009AE93 /* ShareViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1334,6 +1682,16 @@ target = 5CE2BA672845308900EC33A6 /* SimpleXChat */; targetProxy = 5CE2BAA82845617C00EC33A6 /* PBXContainerItemProxy */; }; + CEE723B02C3BD3D70009AE93 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CEE723A62C3BD3D70009AE93 /* SimpleX SE */; + targetProxy = CEE723AF2C3BD3D70009AE93 /* PBXContainerItemProxy */; + }; + CEE723D22C3C21C90009AE93 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5CE2BA672845308900EC33A6 /* SimpleXChat */; + targetProxy = CEE723D12C3C21C90009AE93 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -1356,6 +1714,7 @@ 5C5B67932ABAF56000DA9412 /* bg */, 5C245F3E2B501F13001CC39F /* tr */, 5C371E502BA9AB6400100AD3 /* hu */, + E5DCF9952C59067B007928CC /* en */, ); name = InfoPlist.strings; sourceTree = ""; @@ -1407,6 +1766,78 @@ name = "SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; + E5DCF96F2C590272007928CC /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + E5DCF9702C590272007928CC /* en */, + E5DCF9722C590274007928CC /* bg */, + E5DCF9732C590275007928CC /* zh-Hans */, + E5DCF9742C590276007928CC /* nl */, + E5DCF9752C590277007928CC /* cs */, + E5DCF9762C590278007928CC /* fi */, + E5DCF9772C590279007928CC /* fr */, + E5DCF9782C590279007928CC /* de */, + E5DCF9792C59027A007928CC /* hu */, + E5DCF97A2C59027A007928CC /* it */, + E5DCF97B2C59027B007928CC /* ja */, + E5DCF97C2C59027B007928CC /* pl */, + E5DCF97D2C59027C007928CC /* ru */, + E5DCF97E2C59027C007928CC /* es */, + E5DCF97F2C59027D007928CC /* th */, + E5DCF9802C59027D007928CC /* uk */, + E5DCF9812C59027D007928CC /* tr */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + E5DCF9822C5902CE007928CC /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + E5DCF9832C5902CE007928CC /* en */, + E5DCF9852C5902D4007928CC /* bg */, + E5DCF9862C5902D5007928CC /* zh-Hans */, + E5DCF9872C5902D8007928CC /* cs */, + E5DCF9882C5902DC007928CC /* nl */, + E5DCF9892C5902DC007928CC /* fi */, + E5DCF98A2C5902DD007928CC /* de */, + E5DCF98B2C5902DD007928CC /* fr */, + E5DCF98C2C5902DE007928CC /* it */, + E5DCF98D2C5902DE007928CC /* hu */, + E5DCF98E2C5902E0007928CC /* ja */, + E5DCF98F2C5902E0007928CC /* pl */, + E5DCF9902C5902E1007928CC /* ru */, + E5DCF9912C5902E1007928CC /* es */, + E5DCF9922C5902E2007928CC /* th */, + E5DCF9932C5902E2007928CC /* tr */, + E5DCF9942C5902E3007928CC /* uk */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + E5DCF9962C5906FF007928CC /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + E5DCF9972C5906FF007928CC /* en */, + E5DCF9992C59072A007928CC /* bg */, + E5DCF99A2C59072B007928CC /* zh-Hans */, + E5DCF99B2C59072B007928CC /* cs */, + E5DCF99C2C59072C007928CC /* nl */, + E5DCF99D2C59072D007928CC /* fi */, + E5DCF99E2C59072E007928CC /* fr */, + E5DCF99F2C59072E007928CC /* de */, + E5DCF9A02C59072F007928CC /* hu */, + E5DCF9A12C59072F007928CC /* it */, + E5DCF9A22C590730007928CC /* ja */, + E5DCF9A32C590730007928CC /* pl */, + E5DCF9A42C590731007928CC /* ru */, + E5DCF9A52C590731007928CC /* es */, + E5DCF9A62C590731007928CC /* th */, + E5DCF9A72C590732007928CC /* tr */, + E5DCF9A82C590732007928CC /* uk */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -1445,6 +1876,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -1506,6 +1938,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1534,12 +1967,16 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ENABLE_MODULES = YES; + CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; + CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 203; + CURRENT_PROJECT_VERSION = 285; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; + GCC_OPTIMIZATION_LEVEL = s; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "SimpleX--iOS--Info.plist"; INFOPLIST_KEY_NSCameraUsageDescription = "SimpleX needs camera access to scan QR codes to connect to other users and for video calls."; @@ -1558,11 +1995,14 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 5.6; + LLVM_LTO = YES_THIN; + MARKETING_VERSION = 6.3.7; + OTHER_LDFLAGS = "-Wl,-stack_size,0x1000000"; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; SDKROOT = iphoneos; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; @@ -1577,12 +2017,16 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ENABLE_MODULES = YES; + CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; + CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 203; + CURRENT_PROJECT_VERSION = 285; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; - ENABLE_PREVIEWS = YES; + ENABLE_CODE_COVERAGE = NO; + ENABLE_PREVIEWS = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "SimpleX--iOS--Info.plist"; INFOPLIST_KEY_NSCameraUsageDescription = "SimpleX needs camera access to scan QR codes to connect to other users and for video calls."; @@ -1601,7 +2045,9 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 5.6; + LLVM_LTO = YES; + MARKETING_VERSION = 6.3.7; + OTHER_LDFLAGS = "-Wl,-stack_size,0x1000000"; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; SDKROOT = iphoneos; @@ -1617,11 +2063,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 285; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 6.3.7; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -1637,11 +2083,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 285; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 1.0; + MARKETING_VERSION = 6.3.7; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -1657,12 +2103,15 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; + CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 203; + CURRENT_PROJECT_VERSION = 285; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; + GCC_OPTIMIZATION_LEVEL = s; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "SimpleX NSE/Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = "SimpleX NSE"; @@ -1673,13 +2122,15 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 5.6; + LLVM_LTO = YES; + MARKETING_VERSION = 6.3.7; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Osize"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; @@ -1689,12 +2140,15 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; + CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 203; + CURRENT_PROJECT_VERSION = 285; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; + ENABLE_CODE_COVERAGE = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "SimpleX NSE/Info.plist"; INFOPLIST_KEY_CFBundleDisplayName = "SimpleX NSE"; @@ -1705,13 +2159,15 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 5.6; + LLVM_LTO = YES; + MARKETING_VERSION = 6.3.7; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Osize"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; VALIDATE_PRODUCT = YES; @@ -1723,14 +2179,17 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; + CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; + CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 203; + CURRENT_PROJECT_VERSION = 285; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = NO; + GCC_OPTIMIZATION_LEVEL = s; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 SimpleX Chat. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -1748,7 +2207,8 @@ "$(inherited)", "$(PROJECT_DIR)/Libraries/sim", ); - MARKETING_VERSION = 5.6; + LLVM_LTO = YES; + MARKETING_VERSION = 6.3.7; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -1757,6 +2217,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = ""; SWIFT_OBJC_BRIDGING_HEADER = ./SimpleXChat/SimpleX.h; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; @@ -1769,14 +2230,17 @@ buildSettings = { APPLICATION_EXTENSION_API_ONLY = YES; CLANG_ENABLE_MODULES = YES; + CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; + CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 203; + CURRENT_PROJECT_VERSION = 285; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_BITCODE = NO; + ENABLE_CODE_COVERAGE = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 SimpleX Chat. All rights reserved."; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -1794,7 +2258,8 @@ "$(inherited)", "$(PROJECT_DIR)/Libraries/sim", ); - MARKETING_VERSION = 5.6; + LLVM_LTO = YES; + MARKETING_VERSION = 6.3.7; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -1803,6 +2268,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_INCLUDE_PATHS = ""; SWIFT_OBJC_BRIDGING_HEADER = ./SimpleXChat/SimpleX.h; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -1811,6 +2277,74 @@ }; name = Release; }; + CEE723B22C3BD3D70009AE93 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 285; + DEVELOPMENT_TEAM = 5NN7GUYB6T; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "SimpleX SE/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "SimpleX SE"; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 SimpleX Chat. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 6.3.7; + PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CEE723B32C3BD3D70009AE93 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 285; + DEVELOPMENT_TEAM = 5NN7GUYB6T; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "SimpleX SE/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "SimpleX SE"; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 SimpleX Chat. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 6.3.7; + PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -1859,6 +2393,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + CEE723B42C3BD3D70009AE93 /* Build configuration list for PBXNativeTarget "SimpleX SE" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEE723B22C3BD3D70009AE93 /* Debug */, + CEE723B32C3BD3D70009AE93 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ @@ -1866,8 +2409,32 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/twostraws/CodeScanner"; requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.0.0; + kind = exactVersion; + version = 2.5.0; + }; + }; + 8C73C1162C21E17B00892670 /* XCRemoteSwiftPackageReference "Yams" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/jpsim/Yams"; + requirement = { + kind = exactVersion; + version = 5.1.2; + }; + }; + 8CB3476A2CF5CFFA006787A5 /* XCRemoteSwiftPackageReference "ink" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/johnsundell/ink"; + requirement = { + kind = exactVersion; + version = 0.6.0; + }; + }; + B72894592D0C62BF00F7A19A /* XCRemoteSwiftPackageReference "Elegant-Emoji-Picker" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Finalet/Elegant-Emoji-Picker"; + requirement = { + branch = main; + kind = branch; }; }; D7197A1629AE89660055C05A /* XCRemoteSwiftPackageReference "WebRTC" */ = { @@ -1882,8 +2449,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/kirualex/SwiftyGif"; requirement = { - branch = master; - kind = branch; + kind = revision; + revision = 5e8619335d394901379c9add5c4c1c2f420b3800; }; }; D7F0E33729964E7D0068AF69 /* XCRemoteSwiftPackageReference "lzstring-swift" */ = { @@ -1902,6 +2469,26 @@ package = 5C8F01CB27A6F0D8007D2C8D /* XCRemoteSwiftPackageReference "CodeScanner" */; productName = CodeScanner; }; + 8C8118712C220B5B00E6FC94 /* Yams */ = { + isa = XCSwiftPackageProductDependency; + package = 8C73C1162C21E17B00892670 /* XCRemoteSwiftPackageReference "Yams" */; + productName = Yams; + }; + 8CB3476B2CF5CFFA006787A5 /* Ink */ = { + isa = XCSwiftPackageProductDependency; + package = 8CB3476A2CF5CFFA006787A5 /* XCRemoteSwiftPackageReference "ink" */; + productName = Ink; + }; + B728945A2D0C62BF00F7A19A /* ElegantEmojiPicker */ = { + isa = XCSwiftPackageProductDependency; + package = B72894592D0C62BF00F7A19A /* XCRemoteSwiftPackageReference "Elegant-Emoji-Picker" */; + productName = ElegantEmojiPicker; + }; + CE38A29B2C3FCD72005ED185 /* SwiftyGif */ = { + isa = XCSwiftPackageProductDependency; + package = D77B92DA2952372200A5A1CC /* XCRemoteSwiftPackageReference "SwiftyGif" */; + productName = SwiftyGif; + }; D7197A1729AE89660055C05A /* WebRTC */ = { isa = XCSwiftPackageProductDependency; package = D7197A1629AE89660055C05A /* XCRemoteSwiftPackageReference "WebRTC" */; diff --git a/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index cfacb2381a..2bddf5b5b8 100644 --- a/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/apps/ios/SimpleX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,12 +1,31 @@ { + "originHash" : "07434ae88cbf078ce3d27c91c1f605836aaebff0e0cef5f25317795151c77db1", "pins" : [ { "identity" : "codescanner", "kind" : "remoteSourceControl", "location" : "https://github.com/twostraws/CodeScanner", "state" : { - "revision" : "c27a66149b7483fe42e2ec6aad61d5c3fffe522d", - "version" : "2.1.1" + "revision" : "34da57fb63b47add20de8a85da58191523ccce57", + "version" : "2.5.0" + } + }, + { + "identity" : "elegant-emoji-picker", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Finalet/Elegant-Emoji-Picker", + "state" : { + "branch" : "main", + "revision" : "71d2d46092b4d550cc593614efc06438f845f6e6" + } + }, + { + "identity" : "ink", + "kind" : "remoteSourceControl", + "location" : "https://github.com/johnsundell/ink", + "state" : { + "revision" : "bcc9f219900a62c4210e6db726035d7f03ae757b", + "version" : "0.6.0" } }, { @@ -22,7 +41,6 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/kirualex/SwiftyGif", "state" : { - "branch" : "master", "revision" : "5e8619335d394901379c9add5c4c1c2f420b3800" } }, @@ -33,7 +51,16 @@ "state" : { "revision" : "34bedc50f9c58dccf4967ea59c7e6a47d620803b" } + }, + { + "identity" : "yams", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/Yams", + "state" : { + "revision" : "9234124cff5e22e178988c18d8b95a8ae8007f76", + "version" : "5.1.2" + } } ], - "version" : 2 + "version" : 3 } diff --git a/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX SE.xcscheme b/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX SE.xcscheme new file mode 100644 index 0000000000..a2639eb263 --- /dev/null +++ b/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX SE.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/ios/SimpleXChat/API.swift b/apps/ios/SimpleXChat/API.swift index cdd1008c13..0dd3483fd7 100644 --- a/apps/ios/SimpleXChat/API.swift +++ b/apps/ios/SimpleXChat/API.swift @@ -46,7 +46,7 @@ public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: Migratio var cConfirm = confirm.rawValue.cString(using: .utf8)! // the last parameter of chat_migrate_init is used to return the pointer to chat controller let cjson = chat_migrate_init_key(&cPath, &cKey, 1, &cConfirm, backgroundMode ? 1 : 0, &chatController)! - let dbRes = dbMigrationResult(fromCString(cjson)) + let dbRes = dbMigrationResult(dataFromCString(cjson)) let encrypted = dbKey != "" let keychainErr = dbRes == .ok && useKeychain && encrypted && !kcDatabasePassword.set(dbKey) let result = (encrypted, keychainErr ? .errorKeychain : dbRes) @@ -63,7 +63,7 @@ public func chatInitTemporaryDatabase(url: URL, key: String? = nil, confirmation var cKey = dbKey.cString(using: .utf8)! var cConfirm = confirmation.rawValue.cString(using: .utf8)! let cjson = chat_migrate_init_key(&cPath, &cKey, 1, &cConfirm, 0, &temporaryController)! - return (dbMigrationResult(fromCString(cjson)), temporaryController) + return (dbMigrationResult(dataFromCString(cjson)), temporaryController) } public func chatInitControllerRemovingDatabases() { @@ -87,6 +87,11 @@ public func chatInitControllerRemovingDatabases() { public func chatCloseStore() { + // Prevent crash when exiting the app with already closed store (for example, after changing a database passpharase) + guard hasChatCtrl() else { + logger.error("chatCloseStore: already closed, chatCtrl is nil") + return + } let err = fromCString(chat_close_store(getChatCtrl())) if err != "" { logger.error("chatCloseStore error: \(err)") @@ -105,27 +110,42 @@ public func resetChatCtrl() { migrationResult = nil } -public func sendSimpleXCmd(_ cmd: ChatCommand, _ ctrl: chat_ctrl? = nil) -> ChatResponse { - var c = cmd.cmdString.cString(using: .utf8)! - let cjson = chat_send_cmd(ctrl ?? getChatCtrl(), &c)! - return chatResponse(fromCString(cjson)) +@inline(__always) +public func sendSimpleXCmd(_ cmd: ChatCmdProtocol, _ ctrl: chat_ctrl? = nil) -> APIResult { + if let d = sendSimpleXCmdStr(cmd.cmdString, ctrl) { + decodeAPIResult(d) + } else { + APIResult.error(.invalidJSON(json: nil)) + } +} + +@inline(__always) +public func sendSimpleXCmdStr(_ cmd: String, _ ctrl: chat_ctrl? = nil) -> Data? { + var c = cmd.cString(using: .utf8)! + return if let cjson = chat_send_cmd(ctrl ?? getChatCtrl(), &c) { + dataFromCString(cjson) + } else { + nil + } } // in microseconds -let MESSAGE_TIMEOUT: Int32 = 15_000_000 +public let MESSAGE_TIMEOUT: Int32 = 15_000_000 -public func recvSimpleXMsg(_ ctrl: chat_ctrl? = nil) -> ChatResponse? { - if let cjson = chat_recv_msg_wait(ctrl ?? getChatCtrl(), MESSAGE_TIMEOUT) { - let s = fromCString(cjson) - return s == "" ? nil : chatResponse(s) +@inline(__always) +public func recvSimpleXMsg(_ ctrl: chat_ctrl? = nil, messageTimeout: Int32 = MESSAGE_TIMEOUT) -> APIResult? { + if let cjson = chat_recv_msg_wait(ctrl ?? getChatCtrl(), messageTimeout), + let d = dataFromCString(cjson) { + decodeAPIResult(d) + } else { + nil } - return nil } public func parseSimpleXMarkdown(_ s: String) -> [FormattedText]? { var c = s.cString(using: .utf8)! if let cjson = chat_parse_markdown(&c) { - if let d = fromCString(cjson).data(using: .utf8) { + if let d = dataFromCString(cjson) { do { let r = try jsonDecoder.decode(ParsedMarkdown.self, from: d) return r.formattedText @@ -149,7 +169,7 @@ struct ParsedMarkdown: Decodable { public func parseServerAddress(_ s: String) -> ServerAddress? { var c = s.cString(using: .utf8)! if let cjson = chat_parse_server(&c) { - if let d = fromCString(cjson).data(using: .utf8) { + if let d = dataFromCString(cjson) { do { let r = try jsonDecoder.decode(ParsedServerAddress.self, from: d) return r.serverAddress @@ -166,67 +186,34 @@ struct ParsedServerAddress: Decodable { var parseError: String } +@inline(__always) public func fromCString(_ c: UnsafeMutablePointer) -> String { let s = String.init(cString: c) free(c) return s } -public func chatResponse(_ s: String) -> ChatResponse { - let d = s.data(using: .utf8)! - // TODO is there a way to do it without copying the data? e.g: - // let p = UnsafeMutableRawPointer.init(mutating: UnsafeRawPointer(cjson)) - // let d = Data.init(bytesNoCopy: p, count: strlen(cjson), deallocator: .free) - do { - let r = try jsonDecoder.decode(APIResponse.self, from: d) - return r.resp - } catch { - logger.error("chatResponse jsonDecoder.decode error: \(error.localizedDescription)") +@inline(__always) +public func dataFromCString(_ c: UnsafeMutablePointer) -> Data? { + let len = strlen(c) + if len > 0 { + return Data(bytesNoCopy: c, count: len, deallocator: .free) + } else { + free(c) + return nil } - - var type: String? - var json: String? - if let j = try? JSONSerialization.jsonObject(with: d) as? NSDictionary { - if let jResp = j["resp"] as? NSDictionary, jResp.count == 1 || jResp.count == 2 { - type = jResp.allKeys[0] as? String - if jResp.count == 2 && type == "_owsf" { - type = jResp.allKeys[1] as? String - } - if type == "apiChats" { - if let jApiChats = jResp["apiChats"] as? NSDictionary, - let user: UserRef = try? decodeObject(jApiChats["user"] as Any), - let jChats = jApiChats["chats"] as? NSArray { - let chats = jChats.map { jChat in - if let chatData = try? parseChatData(jChat) { - return chatData - } - return ChatData.invalidJSON(prettyJSON(jChat) ?? "") - } - return .apiChats(user: user, chats: chats) - } - } else if type == "apiChat" { - if let jApiChat = jResp["apiChat"] as? NSDictionary, - let user: UserRef = try? decodeObject(jApiChat["user"] as Any), - let jChat = jApiChat["chat"] as? NSDictionary, - let chat = try? parseChatData(jChat) { - return .apiChat(user: user, chat: chat) - } - } else if type == "chatCmdError" { - if let jError = jResp["chatCmdError"] as? NSDictionary { - return .chatCmdError(user_: decodeUser_(jError), chatError: .invalidJSON(json: prettyJSON(jError) ?? "")) - } - } else if type == "chatError" { - if let jError = jResp["chatError"] as? NSDictionary { - return .chatError(user_: decodeUser_(jError), chatError: .invalidJSON(json: prettyJSON(jError) ?? "")) - } - } - } - json = prettyJSON(j) - } - return ChatResponse.response(type: type ?? "invalid", json: json ?? s) } -private func decodeUser_(_ jDict: NSDictionary) -> UserRef? { +@inline(__always) +public func dataToString(_ d: Data?) -> String { + if let d { + String(data: d, encoding: .utf8) ?? "invalid string" + } else { + "no data" + } +} + +public func decodeUser_(_ jDict: NSDictionary) -> UserRef? { if let user_ = jDict["user_"] { try? decodeObject(user_ as Any) } else { @@ -234,10 +221,23 @@ private func decodeUser_(_ jDict: NSDictionary) -> UserRef? { } } -func parseChatData(_ jChat: Any) throws -> ChatData { +public func errorJson(_ jDict: NSDictionary) -> Data? { + if let chatError = jDict["chatError"] { + serializeJSON(chatError) + } else { + serializeJSON(jDict) + } +} + +public func parseChatData(_ jChat: Any, _ jNavInfo: Any? = nil) throws -> (ChatData, NavigationInfo) { let jChatDict = jChat as! NSDictionary let chatInfo: ChatInfo = try decodeObject(jChatDict["chatInfo"]!) let chatStats: ChatStats = try decodeObject(jChatDict["chatStats"]!) + let navInfo: NavigationInfo = if let jNavInfo = jNavInfo as? NSDictionary, let jNav = jNavInfo["navInfo"] { + try decodeObject(jNav) + } else { + NavigationInfo() + } let jChatItems = jChatDict["chatItems"] as! NSArray let chatItems = jChatItems.map { jCI in if let ci: ChatItem = try? decodeObject(jCI) { @@ -246,16 +246,18 @@ func parseChatData(_ jChat: Any) throws -> ChatData { return ChatItem.invalidJSON( chatDir: decodeProperty(jCI, "chatDir"), meta: decodeProperty(jCI, "meta"), - json: prettyJSON(jCI) ?? "" + json: serializeJSON(jCI, options: .prettyPrinted) ) } - return ChatData(chatInfo: chatInfo, chatItems: chatItems, chatStats: chatStats) + return (ChatData(chatInfo: chatInfo, chatItems: chatItems, chatStats: chatStats), navInfo) } -func decodeObject(_ obj: Any) throws -> T { +@inline(__always) +public func decodeObject(_ obj: Any) throws -> T { try jsonDecoder.decode(T.self, from: JSONSerialization.data(withJSONObject: obj)) } +@inline(__always) func decodeProperty(_ obj: Any, _ prop: NSString) -> T? { if let jProp = (obj as? NSDictionary)?[prop] { return try? decodeObject(jProp) @@ -263,28 +265,52 @@ func decodeProperty(_ obj: Any, _ prop: NSString) -> T? { return nil } -func prettyJSON(_ obj: Any) -> String? { - if let d = try? JSONSerialization.data(withJSONObject: obj, options: .prettyPrinted) { - return String(decoding: d, as: UTF8.self) +@inline(__always) +func getOWSF(_ obj: NSDictionary, _ prop: NSString) -> (type: String, object: NSDictionary)? { + if let j = obj[prop] as? NSDictionary, j.count == 1 || j.count == 2 { + var type = j.allKeys[0] as? String + if j.count == 2 && type == "_owsf" { + type = j.allKeys[1] as? String + } + if let type { + return (type, j) + } } return nil } -public func responseError(_ err: Error) -> String { - if let r = err as? ChatResponse { - switch r { - case let .chatCmdError(_, chatError): return chatErrorString(chatError) - case let .chatError(_, chatError): return chatErrorString(chatError) - default: return "\(String(describing: r.responseType)), details: \(String(describing: r.details))" - } +@inline(__always) +public func serializeJSON(_ obj: Any, options: JSONSerialization.WritingOptions = []) -> Data? { + if let d = try? JSONSerialization.data(withJSONObject: obj, options: options) { + dataPrefix(d) } else { - return String(describing: err) + nil } } -func chatErrorString(_ err: ChatError) -> String { - if case let .invalidJSON(json) = err { return json } - return String(describing: err) +let MAX_JSON_VIEW_LENGTH = 2048 + +@inline(__always) +public func dataPrefix(_ d: Data) -> Data { + d.count > MAX_JSON_VIEW_LENGTH + ? Data(d.prefix(MAX_JSON_VIEW_LENGTH)) + : d +} + +public func responseError(_ err: Error) -> String { + if let e = err as? ChatError { + chatErrorString(e) + } else { + String(describing: err) + } +} + +public func chatErrorString(_ err: ChatError) -> String { + switch err { + case let .invalidJSON(json): dataToString(json) + case let .unexpectedResult(type): "unexpected result: \(type)" + default: String(describing: err) + } } public enum DBMigrationResult: Decodable, Equatable { @@ -323,15 +349,15 @@ public enum MTRError: Decodable, Equatable { case different(appMigration: String, dbMigration: String) } -func dbMigrationResult(_ s: String) -> DBMigrationResult { - let d = s.data(using: .utf8)! -// TODO is there a way to do it without copying the data? e.g: -// let p = UnsafeMutableRawPointer.init(mutating: UnsafeRawPointer(cjson)) -// let d = Data.init(bytesNoCopy: p, count: strlen(cjson), deallocator: .free) - do { - return try jsonDecoder.decode(DBMigrationResult.self, from: d) - } catch let error { - logger.error("chatResponse jsonDecoder.decode error: \(error.localizedDescription)") - return .unknown(json: s) +func dbMigrationResult(_ d: Data?) -> DBMigrationResult { + if let d { + do { + return try jsonDecoder.decode(DBMigrationResult.self, from: d) + } catch let error { + logger.error("chatResponse jsonDecoder.decode error: \(error.localizedDescription)") + return .unknown(json: dataToString(d)) + } + } else { + return .unknown(json: "no data") } } diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index f55c69a349..b8d2361ac8 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -8,1210 +8,188 @@ import Foundation import SwiftUI +import Network public let jsonDecoder = getJSONDecoder() -let jsonEncoder = getJSONEncoder() +public let jsonEncoder = getJSONEncoder() -public enum ChatCommand { - case showActiveUser - case createActiveUser(profile: Profile?, sameServers: Bool, pastTimestamp: Bool) - case listUsers - case apiSetActiveUser(userId: Int64, viewPwd: String?) - case setAllContactReceipts(enable: Bool) - case apiSetUserContactReceipts(userId: Int64, userMsgReceiptSettings: UserMsgReceiptSettings) - case apiSetUserGroupReceipts(userId: Int64, userMsgReceiptSettings: UserMsgReceiptSettings) - case apiHideUser(userId: Int64, viewPwd: String) - case apiUnhideUser(userId: Int64, viewPwd: String) - case apiMuteUser(userId: Int64) - case apiUnmuteUser(userId: Int64) - case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?) - case startChat(mainApp: Bool) - case apiStopChat - case apiActivateChat(restoreChat: Bool) - case apiSuspendChat(timeoutMicroseconds: Int) - case setTempFolder(tempFolder: String) - case setFilesFolder(filesFolder: String) - case apiSetEncryptLocalFiles(enable: Bool) - case apiSetPQEncryption(enable: Bool) - case apiSetContactPQ(contactId: Int64, enable: Bool) - case apiExportArchive(config: ArchiveConfig) - case apiImportArchive(config: ArchiveConfig) - case apiDeleteStorage - case apiStorageEncryption(config: DBEncryptionConfig) - case testStorageEncryption(key: String) - case apiSaveSettings(settings: AppSettings) - case apiGetSettings(settings: AppSettings) - case apiGetChats(userId: Int64) - case apiGetChat(type: ChatType, id: Int64, pagination: ChatPagination, search: String) - case apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) - case apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool, ttl: Int?) - case apiCreateChatItem(noteFolderId: Int64, file: CryptoFile?, msg: MsgContent) - case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent, live: Bool) - case apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode) - case apiDeleteMemberChatItem(groupId: Int64, groupMemberId: Int64, itemId: Int64) - case apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction) - case apiGetNtfToken - case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode) - case apiVerifyToken(token: DeviceToken, nonce: String, code: String) - case apiDeleteToken(token: DeviceToken) - case apiGetNtfMessage(nonce: String, encNtfInfo: String) - case apiNewGroup(userId: Int64, incognito: Bool, groupProfile: GroupProfile) - case apiAddMember(groupId: Int64, contactId: Int64, memberRole: GroupMemberRole) - case apiJoinGroup(groupId: Int64) - case apiMemberRole(groupId: Int64, memberId: Int64, memberRole: GroupMemberRole) - case apiBlockMemberForAll(groupId: Int64, memberId: Int64, blocked: Bool) - case apiRemoveMember(groupId: Int64, memberId: Int64) - case apiLeaveGroup(groupId: Int64) - case apiListMembers(groupId: Int64) - case apiUpdateGroupProfile(groupId: Int64, groupProfile: GroupProfile) - case apiCreateGroupLink(groupId: Int64, memberRole: GroupMemberRole) - case apiGroupLinkMemberRole(groupId: Int64, memberRole: GroupMemberRole) - case apiDeleteGroupLink(groupId: Int64) - case apiGetGroupLink(groupId: Int64) - case apiCreateMemberContact(groupId: Int64, groupMemberId: Int64) - case apiSendMemberContactInvitation(contactId: Int64, msg: MsgContent) - case apiGetUserProtoServers(userId: Int64, serverProtocol: ServerProtocol) - case apiSetUserProtoServers(userId: Int64, serverProtocol: ServerProtocol, servers: [ServerCfg]) - case apiTestProtoServer(userId: Int64, server: String) - case apiSetChatItemTTL(userId: Int64, seconds: Int64?) - case apiGetChatItemTTL(userId: Int64) - case apiSetNetworkConfig(networkConfig: NetCfg) - case apiGetNetworkConfig - case reconnectAllServers - case apiSetChatSettings(type: ChatType, id: Int64, chatSettings: ChatSettings) - case apiSetMemberSettings(groupId: Int64, groupMemberId: Int64, memberSettings: GroupMemberSettings) - case apiContactInfo(contactId: Int64) - case apiGroupMemberInfo(groupId: Int64, groupMemberId: Int64) - case apiSwitchContact(contactId: Int64) - case apiSwitchGroupMember(groupId: Int64, groupMemberId: Int64) - case apiAbortSwitchContact(contactId: Int64) - case apiAbortSwitchGroupMember(groupId: Int64, groupMemberId: Int64) - case apiSyncContactRatchet(contactId: Int64, force: Bool) - case apiSyncGroupMemberRatchet(groupId: Int64, groupMemberId: Int64, force: Bool) - case apiGetContactCode(contactId: Int64) - case apiGetGroupMemberCode(groupId: Int64, groupMemberId: Int64) - case apiVerifyContact(contactId: Int64, connectionCode: String?) - case apiVerifyGroupMember(groupId: Int64, groupMemberId: Int64, connectionCode: String?) - case apiAddContact(userId: Int64, incognito: Bool) - case apiSetConnectionIncognito(connId: Int64, incognito: Bool) - case apiConnectPlan(userId: Int64, connReq: String) - case apiConnect(userId: Int64, incognito: Bool, connReq: String) - case apiConnectContactViaAddress(userId: Int64, incognito: Bool, contactId: Int64) - case apiDeleteChat(type: ChatType, id: Int64, notify: Bool?) - case apiClearChat(type: ChatType, id: Int64) - case apiListContacts(userId: Int64) - case apiUpdateProfile(userId: Int64, profile: Profile) - case apiSetContactPrefs(contactId: Int64, preferences: Preferences) - case apiSetContactAlias(contactId: Int64, localAlias: String) - case apiSetConnectionAlias(connId: Int64, localAlias: String) - case apiCreateMyAddress(userId: Int64) - case apiDeleteMyAddress(userId: Int64) - case apiShowMyAddress(userId: Int64) - case apiSetProfileAddress(userId: Int64, on: Bool) - case apiAddressAutoAccept(userId: Int64, autoAccept: AutoAccept?) - case apiAcceptContact(incognito: Bool, contactReqId: Int64) - case apiRejectContact(contactReqId: Int64) - // WebRTC calls - case apiSendCallInvitation(contact: Contact, callType: CallType) - case apiRejectCall(contact: Contact) - case apiSendCallOffer(contact: Contact, callOffer: WebRTCCallOffer) - case apiSendCallAnswer(contact: Contact, answer: WebRTCSession) - case apiSendCallExtraInfo(contact: Contact, extraInfo: WebRTCExtraInfo) - case apiEndCall(contact: Contact) - case apiGetCallInvitations - case apiCallStatus(contact: Contact, callStatus: WebRTCCallStatus) - case apiGetNetworkStatuses - case apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64)) - case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) - case receiveFile(fileId: Int64, encrypted: Bool?, inline: Bool?) - case setFileToReceive(fileId: Int64, encrypted: Bool?) - case cancelFile(fileId: Int64) - // remote desktop commands - case setLocalDeviceName(displayName: String) - case connectRemoteCtrl(xrcpInvitation: String) - case findKnownRemoteCtrl - case confirmRemoteCtrl(remoteCtrlId: Int64) - case verifyRemoteCtrlSession(sessionCode: String) - case listRemoteCtrls - case stopRemoteCtrl - case deleteRemoteCtrl(remoteCtrlId: Int64) - case apiUploadStandaloneFile(userId: Int64, file: CryptoFile) - case apiDownloadStandaloneFile(userId: Int64, url: String, file: CryptoFile) - case apiStandaloneFileInfo(url: String) - // misc - case showVersion - case string(String) +public protocol ChatCmdProtocol { + var cmdString: String { get } +} - public var cmdString: String { - get { - switch self { - case .showActiveUser: return "/u" - case let .createActiveUser(profile, sameServers, pastTimestamp): - let user = NewUser(profile: profile, sameServers: sameServers, pastTimestamp: pastTimestamp) - return "/_create user \(encodeJSON(user))" - case .listUsers: return "/users" - case let .apiSetActiveUser(userId, viewPwd): return "/_user \(userId)\(maybePwd(viewPwd))" - case let .setAllContactReceipts(enable): return "/set receipts all \(onOff(enable))" - case let .apiSetUserContactReceipts(userId, userMsgReceiptSettings): - let umrs = userMsgReceiptSettings - return "/_set receipts contacts \(userId) \(onOff(umrs.enable)) clear_overrides=\(onOff(umrs.clearOverrides))" - case let .apiSetUserGroupReceipts(userId, userMsgReceiptSettings): - let umrs = userMsgReceiptSettings - return "/_set receipts groups \(userId) \(onOff(umrs.enable)) clear_overrides=\(onOff(umrs.clearOverrides))" - case let .apiHideUser(userId, viewPwd): return "/_hide user \(userId) \(encodeJSON(viewPwd))" - case let .apiUnhideUser(userId, viewPwd): return "/_unhide user \(userId) \(encodeJSON(viewPwd))" - case let .apiMuteUser(userId): return "/_mute user \(userId)" - case let .apiUnmuteUser(userId): return "/_unmute user \(userId)" - case let .apiDeleteUser(userId, delSMPQueues, viewPwd): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))\(maybePwd(viewPwd))" - case let .startChat(mainApp): return "/_start main=\(onOff(mainApp))" - case .apiStopChat: return "/_stop" - case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))" - case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)" - case let .setTempFolder(tempFolder): return "/_temp_folder \(tempFolder)" - case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)" - case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))" - case let .apiSetPQEncryption(enable): return "/pq \(onOff(enable))" - case let .apiSetContactPQ(contactId, enable): return "/_pq @\(contactId) \(onOff(enable))" - case let .apiExportArchive(cfg): return "/_db export \(encodeJSON(cfg))" - case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))" - case .apiDeleteStorage: return "/_db delete" - case let .apiStorageEncryption(cfg): return "/_db encryption \(encodeJSON(cfg))" - case let .testStorageEncryption(key): return "/db test key \(key)" - case let .apiSaveSettings(settings): return "/_save app settings \(encodeJSON(settings))" - case let .apiGetSettings(settings): return "/_get app settings \(encodeJSON(settings))" - case let .apiGetChats(userId): return "/_get chats \(userId) pcc=on" - case let .apiGetChat(type, id, pagination, search): return "/_get chat \(ref(type, id)) \(pagination.cmdString)" + - (search == "" ? "" : " search=\(search)") - case let .apiGetChatItemInfo(type, id, itemId): return "/_get item info \(ref(type, id)) \(itemId)" - case let .apiSendMessage(type, id, file, quotedItemId, mc, live, ttl): - let msg = encodeJSON(ComposedMessage(fileSource: file, quotedItemId: quotedItemId, msgContent: mc)) - let ttlStr = ttl != nil ? "\(ttl!)" : "default" - return "/_send \(ref(type, id)) live=\(onOff(live)) ttl=\(ttlStr) json \(msg)" - case let .apiCreateChatItem(noteFolderId, file, mc): - let msg = encodeJSON(ComposedMessage(fileSource: file, msgContent: mc)) - return "/_create *\(noteFolderId) json \(msg)" - case let .apiUpdateChatItem(type, id, itemId, mc, live): return "/_update item \(ref(type, id)) \(itemId) live=\(onOff(live)) \(mc.cmdString)" - case let .apiDeleteChatItem(type, id, itemId, mode): return "/_delete item \(ref(type, id)) \(itemId) \(mode.rawValue)" - case let .apiDeleteMemberChatItem(groupId, groupMemberId, itemId): return "/_delete member item #\(groupId) \(groupMemberId) \(itemId)" - case let .apiChatItemReaction(type, id, itemId, add, reaction): return "/_reaction \(ref(type, id)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))" - case .apiGetNtfToken: return "/_ntf get " - case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)" - case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)" - case let .apiDeleteToken(token): return "/_ntf delete \(token.cmdString)" - case let .apiGetNtfMessage(nonce, encNtfInfo): return "/_ntf message \(nonce) \(encNtfInfo)" - case let .apiNewGroup(userId, incognito, groupProfile): return "/_group \(userId) incognito=\(onOff(incognito)) \(encodeJSON(groupProfile))" - case let .apiAddMember(groupId, contactId, memberRole): return "/_add #\(groupId) \(contactId) \(memberRole)" - case let .apiJoinGroup(groupId): return "/_join #\(groupId)" - case let .apiMemberRole(groupId, memberId, memberRole): return "/_member role #\(groupId) \(memberId) \(memberRole.rawValue)" - case let .apiBlockMemberForAll(groupId, memberId, blocked): return "/_block #\(groupId) \(memberId) blocked=\(onOff(blocked))" - case let .apiRemoveMember(groupId, memberId): return "/_remove #\(groupId) \(memberId)" - case let .apiLeaveGroup(groupId): return "/_leave #\(groupId)" - case let .apiListMembers(groupId): return "/_members #\(groupId)" - case let .apiUpdateGroupProfile(groupId, groupProfile): return "/_group_profile #\(groupId) \(encodeJSON(groupProfile))" - case let .apiCreateGroupLink(groupId, memberRole): return "/_create link #\(groupId) \(memberRole)" - case let .apiGroupLinkMemberRole(groupId, memberRole): return "/_set link role #\(groupId) \(memberRole)" - case let .apiDeleteGroupLink(groupId): return "/_delete link #\(groupId)" - case let .apiGetGroupLink(groupId): return "/_get link #\(groupId)" - case let .apiCreateMemberContact(groupId, groupMemberId): return "/_create member contact #\(groupId) \(groupMemberId)" - case let .apiSendMemberContactInvitation(contactId, mc): return "/_invite member contact @\(contactId) \(mc.cmdString)" - case let .apiGetUserProtoServers(userId, serverProtocol): return "/_servers \(userId) \(serverProtocol)" - case let .apiSetUserProtoServers(userId, serverProtocol, servers): return "/_servers \(userId) \(serverProtocol) \(protoServersStr(servers))" - case let .apiTestProtoServer(userId, server): return "/_server test \(userId) \(server)" - case let .apiSetChatItemTTL(userId, seconds): return "/_ttl \(userId) \(chatItemTTLStr(seconds: seconds))" - case let .apiGetChatItemTTL(userId): return "/_ttl \(userId)" - case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))" - case .apiGetNetworkConfig: return "/network" - case .reconnectAllServers: return "/reconnect" - case let .apiSetChatSettings(type, id, chatSettings): return "/_settings \(ref(type, id)) \(encodeJSON(chatSettings))" - case let .apiSetMemberSettings(groupId, groupMemberId, memberSettings): return "/_member settings #\(groupId) \(groupMemberId) \(encodeJSON(memberSettings))" - case let .apiContactInfo(contactId): return "/_info @\(contactId)" - case let .apiGroupMemberInfo(groupId, groupMemberId): return "/_info #\(groupId) \(groupMemberId)" - case let .apiSwitchContact(contactId): return "/_switch @\(contactId)" - case let .apiSwitchGroupMember(groupId, groupMemberId): return "/_switch #\(groupId) \(groupMemberId)" - case let .apiAbortSwitchContact(contactId): return "/_abort switch @\(contactId)" - case let .apiAbortSwitchGroupMember(groupId, groupMemberId): return "/_abort switch #\(groupId) \(groupMemberId)" - case let .apiSyncContactRatchet(contactId, force): if force { - return "/_sync @\(contactId) force=on" - } else { - return "/_sync @\(contactId)" - } - case let .apiSyncGroupMemberRatchet(groupId, groupMemberId, force): if force { - return "/_sync #\(groupId) \(groupMemberId) force=on" - } else { - return "/_sync #\(groupId) \(groupMemberId)" - } - case let .apiGetContactCode(contactId): return "/_get code @\(contactId)" - case let .apiGetGroupMemberCode(groupId, groupMemberId): return "/_get code #\(groupId) \(groupMemberId)" - case let .apiVerifyContact(contactId, .some(connectionCode)): return "/_verify code @\(contactId) \(connectionCode)" - case let .apiVerifyContact(contactId, .none): return "/_verify code @\(contactId)" - case let .apiVerifyGroupMember(groupId, groupMemberId, .some(connectionCode)): return "/_verify code #\(groupId) \(groupMemberId) \(connectionCode)" - case let .apiVerifyGroupMember(groupId, groupMemberId, .none): return "/_verify code #\(groupId) \(groupMemberId)" - case let .apiAddContact(userId, incognito): return "/_connect \(userId) incognito=\(onOff(incognito))" - case let .apiSetConnectionIncognito(connId, incognito): return "/_set incognito :\(connId) \(onOff(incognito))" - case let .apiConnectPlan(userId, connReq): return "/_connect plan \(userId) \(connReq)" - case let .apiConnect(userId, incognito, connReq): return "/_connect \(userId) incognito=\(onOff(incognito)) \(connReq)" - case let .apiConnectContactViaAddress(userId, incognito, contactId): return "/_connect contact \(userId) incognito=\(onOff(incognito)) \(contactId)" - case let .apiDeleteChat(type, id, notify): if let notify = notify { - return "/_delete \(ref(type, id)) notify=\(onOff(notify))" - } else { - return "/_delete \(ref(type, id))" - } - case let .apiClearChat(type, id): return "/_clear chat \(ref(type, id))" - case let .apiListContacts(userId): return "/_contacts \(userId)" - case let .apiUpdateProfile(userId, profile): return "/_profile \(userId) \(encodeJSON(profile))" - case let .apiSetContactPrefs(contactId, preferences): return "/_set prefs @\(contactId) \(encodeJSON(preferences))" - case let .apiSetContactAlias(contactId, localAlias): return "/_set alias @\(contactId) \(localAlias.trimmingCharacters(in: .whitespaces))" - case let .apiSetConnectionAlias(connId, localAlias): return "/_set alias :\(connId) \(localAlias.trimmingCharacters(in: .whitespaces))" - case let .apiCreateMyAddress(userId): return "/_address \(userId)" - case let .apiDeleteMyAddress(userId): return "/_delete_address \(userId)" - case let .apiShowMyAddress(userId): return "/_show_address \(userId)" - case let .apiSetProfileAddress(userId, on): return "/_profile_address \(userId) \(onOff(on))" - case let .apiAddressAutoAccept(userId, autoAccept): return "/_auto_accept \(userId) \(AutoAccept.cmdString(autoAccept))" - case let .apiAcceptContact(incognito, contactReqId): return "/_accept incognito=\(onOff(incognito)) \(contactReqId)" - case let .apiRejectContact(contactReqId): return "/_reject \(contactReqId)" - case let .apiSendCallInvitation(contact, callType): return "/_call invite @\(contact.apiId) \(encodeJSON(callType))" - case let .apiRejectCall(contact): return "/_call reject @\(contact.apiId)" - case let .apiSendCallOffer(contact, callOffer): return "/_call offer @\(contact.apiId) \(encodeJSON(callOffer))" - case let .apiSendCallAnswer(contact, answer): return "/_call answer @\(contact.apiId) \(encodeJSON(answer))" - case let .apiSendCallExtraInfo(contact, extraInfo): return "/_call extra @\(contact.apiId) \(encodeJSON(extraInfo))" - case let .apiEndCall(contact): return "/_call end @\(contact.apiId)" - case .apiGetCallInvitations: return "/_call get" - case let .apiCallStatus(contact, callStatus): return "/_call status @\(contact.apiId) \(callStatus.rawValue)" - case .apiGetNetworkStatuses: return "/_network_statuses" - case let .apiChatRead(type, id, itemRange: (from, to)): return "/_read chat \(ref(type, id)) from=\(from) to=\(to)" - case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))" - case let .receiveFile(fileId, encrypt, inline): return "/freceive \(fileId)\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))" - case let .setFileToReceive(fileId, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("encrypt", encrypt))" - case let .cancelFile(fileId): return "/fcancel \(fileId)" - case let .setLocalDeviceName(displayName): return "/set device name \(displayName)" - case let .connectRemoteCtrl(xrcpInv): return "/connect remote ctrl \(xrcpInv)" - case .findKnownRemoteCtrl: return "/find remote ctrl" - case let .confirmRemoteCtrl(rcId): return "/confirm remote ctrl \(rcId)" - case let .verifyRemoteCtrlSession(sessCode): return "/verify remote ctrl \(sessCode)" - case .listRemoteCtrls: return "/list remote ctrls" - case .stopRemoteCtrl: return "/stop remote ctrl" - case let .deleteRemoteCtrl(rcId): return "/delete remote ctrl \(rcId)" - case let .apiUploadStandaloneFile(userId, file): return "/_upload \(userId) \(file.filePath)" - case let .apiDownloadStandaloneFile(userId, link, file): return "/_download \(userId) \(link) \(file.filePath)" - case let .apiStandaloneFileInfo(link): return "/_download info \(link)" - case .showVersion: return "/version" - case let .string(str): return str - } - } - } +@inline(__always) +public func onOff(_ b: Bool) -> String { + b ? "on" : "off" +} - public var cmdType: String { - get { - switch self { - case .showActiveUser: return "showActiveUser" - case .createActiveUser: return "createActiveUser" - case .listUsers: return "listUsers" - case .apiSetActiveUser: return "apiSetActiveUser" - case .setAllContactReceipts: return "setAllContactReceipts" - case .apiSetUserContactReceipts: return "apiSetUserContactReceipts" - case .apiSetUserGroupReceipts: return "apiSetUserGroupReceipts" - case .apiHideUser: return "apiHideUser" - case .apiUnhideUser: return "apiUnhideUser" - case .apiMuteUser: return "apiMuteUser" - case .apiUnmuteUser: return "apiUnmuteUser" - case .apiDeleteUser: return "apiDeleteUser" - case .startChat: return "startChat" - case .apiStopChat: return "apiStopChat" - case .apiActivateChat: return "apiActivateChat" - case .apiSuspendChat: return "apiSuspendChat" - case .setTempFolder: return "setTempFolder" - case .setFilesFolder: return "setFilesFolder" - case .apiSetEncryptLocalFiles: return "apiSetEncryptLocalFiles" - case .apiSetPQEncryption: return "apiSetPQEncryption" - case .apiSetContactPQ: return "apiSetContactPQ" - case .apiExportArchive: return "apiExportArchive" - case .apiImportArchive: return "apiImportArchive" - case .apiDeleteStorage: return "apiDeleteStorage" - case .apiStorageEncryption: return "apiStorageEncryption" - case .testStorageEncryption: return "testStorageEncryption" - case .apiSaveSettings: return "apiSaveSettings" - case .apiGetSettings: return "apiGetSettings" - case .apiGetChats: return "apiGetChats" - case .apiGetChat: return "apiGetChat" - case .apiGetChatItemInfo: return "apiGetChatItemInfo" - case .apiSendMessage: return "apiSendMessage" - case .apiCreateChatItem: return "apiCreateChatItem" - case .apiUpdateChatItem: return "apiUpdateChatItem" - case .apiDeleteChatItem: return "apiDeleteChatItem" - case .apiConnectContactViaAddress: return "apiConnectContactViaAddress" - case .apiDeleteMemberChatItem: return "apiDeleteMemberChatItem" - case .apiChatItemReaction: return "apiChatItemReaction" - case .apiGetNtfToken: return "apiGetNtfToken" - case .apiRegisterToken: return "apiRegisterToken" - case .apiVerifyToken: return "apiVerifyToken" - case .apiDeleteToken: return "apiDeleteToken" - case .apiGetNtfMessage: return "apiGetNtfMessage" - case .apiNewGroup: return "apiNewGroup" - case .apiAddMember: return "apiAddMember" - case .apiJoinGroup: return "apiJoinGroup" - case .apiMemberRole: return "apiMemberRole" - case .apiBlockMemberForAll: return "apiBlockMemberForAll" - case .apiRemoveMember: return "apiRemoveMember" - case .apiLeaveGroup: return "apiLeaveGroup" - case .apiListMembers: return "apiListMembers" - case .apiUpdateGroupProfile: return "apiUpdateGroupProfile" - case .apiCreateGroupLink: return "apiCreateGroupLink" - case .apiGroupLinkMemberRole: return "apiGroupLinkMemberRole" - case .apiDeleteGroupLink: return "apiDeleteGroupLink" - case .apiGetGroupLink: return "apiGetGroupLink" - case .apiCreateMemberContact: return "apiCreateMemberContact" - case .apiSendMemberContactInvitation: return "apiSendMemberContactInvitation" - case .apiGetUserProtoServers: return "apiGetUserProtoServers" - case .apiSetUserProtoServers: return "apiSetUserProtoServers" - case .apiTestProtoServer: return "apiTestProtoServer" - case .apiSetChatItemTTL: return "apiSetChatItemTTL" - case .apiGetChatItemTTL: return "apiGetChatItemTTL" - case .apiSetNetworkConfig: return "apiSetNetworkConfig" - case .apiGetNetworkConfig: return "apiGetNetworkConfig" - case .reconnectAllServers: return "reconnectAllServers" - case .apiSetChatSettings: return "apiSetChatSettings" - case .apiSetMemberSettings: return "apiSetMemberSettings" - case .apiContactInfo: return "apiContactInfo" - case .apiGroupMemberInfo: return "apiGroupMemberInfo" - case .apiSwitchContact: return "apiSwitchContact" - case .apiSwitchGroupMember: return "apiSwitchGroupMember" - case .apiAbortSwitchContact: return "apiAbortSwitchContact" - case .apiAbortSwitchGroupMember: return "apiAbortSwitchGroupMember" - case .apiSyncContactRatchet: return "apiSyncContactRatchet" - case .apiSyncGroupMemberRatchet: return "apiSyncGroupMemberRatchet" - case .apiGetContactCode: return "apiGetContactCode" - case .apiGetGroupMemberCode: return "apiGetGroupMemberCode" - case .apiVerifyContact: return "apiVerifyContact" - case .apiVerifyGroupMember: return "apiVerifyGroupMember" - case .apiAddContact: return "apiAddContact" - case .apiSetConnectionIncognito: return "apiSetConnectionIncognito" - case .apiConnectPlan: return "apiConnectPlan" - case .apiConnect: return "apiConnect" - case .apiDeleteChat: return "apiDeleteChat" - case .apiClearChat: return "apiClearChat" - case .apiListContacts: return "apiListContacts" - case .apiUpdateProfile: return "apiUpdateProfile" - case .apiSetContactPrefs: return "apiSetContactPrefs" - case .apiSetContactAlias: return "apiSetContactAlias" - case .apiSetConnectionAlias: return "apiSetConnectionAlias" - case .apiCreateMyAddress: return "apiCreateMyAddress" - case .apiDeleteMyAddress: return "apiDeleteMyAddress" - case .apiShowMyAddress: return "apiShowMyAddress" - case .apiSetProfileAddress: return "apiSetProfileAddress" - case .apiAddressAutoAccept: return "apiAddressAutoAccept" - case .apiAcceptContact: return "apiAcceptContact" - case .apiRejectContact: return "apiRejectContact" - case .apiSendCallInvitation: return "apiSendCallInvitation" - case .apiRejectCall: return "apiRejectCall" - case .apiSendCallOffer: return "apiSendCallOffer" - case .apiSendCallAnswer: return "apiSendCallAnswer" - case .apiSendCallExtraInfo: return "apiSendCallExtraInfo" - case .apiEndCall: return "apiEndCall" - case .apiGetCallInvitations: return "apiGetCallInvitations" - case .apiCallStatus: return "apiCallStatus" - case .apiGetNetworkStatuses: return "apiGetNetworkStatuses" - case .apiChatRead: return "apiChatRead" - case .apiChatUnread: return "apiChatUnread" - case .receiveFile: return "receiveFile" - case .setFileToReceive: return "setFileToReceive" - case .cancelFile: return "cancelFile" - case .setLocalDeviceName: return "setLocalDeviceName" - case .connectRemoteCtrl: return "connectRemoteCtrl" - case .findKnownRemoteCtrl: return "findKnownRemoteCtrl" - case .confirmRemoteCtrl: return "confirmRemoteCtrl" - case .verifyRemoteCtrlSession: return "verifyRemoteCtrlSession" - case .listRemoteCtrls: return "listRemoteCtrls" - case .stopRemoteCtrl: return "stopRemoteCtrl" - case .deleteRemoteCtrl: return "deleteRemoteCtrl" - case .apiUploadStandaloneFile: return "apiUploadStandaloneFile" - case .apiDownloadStandaloneFile: return "apiDownloadStandaloneFile" - case .apiStandaloneFileInfo: return "apiStandaloneFileInfo" - case .showVersion: return "showVersion" - case .string: return "console command" - } - } - } - - func ref(_ type: ChatType, _ id: Int64) -> String { - "\(type.rawValue)\(id)" - } - - func protoServersStr(_ servers: [ServerCfg]) -> String { - encodeJSON(ProtoServersConfig(servers: servers)) - } - - func chatItemTTLStr(seconds: Int64?) -> String { - if let seconds = seconds { - return String(seconds) - } else { - return "none" - } - } - - public var obfuscated: ChatCommand { +public enum APIResult: Decodable where R: Decodable, R: ChatAPIResult { + case result(R) + case error(ChatError) + case invalid(type: String, json: Data) + + public var responseType: String { switch self { - case let .apiStorageEncryption(cfg): - return .apiStorageEncryption(config: DBEncryptionConfig(currentKey: obfuscate(cfg.currentKey), newKey: obfuscate(cfg.newKey))) - case let .apiSetActiveUser(userId, viewPwd): - return .apiSetActiveUser(userId: userId, viewPwd: obfuscate(viewPwd)) - case let .apiHideUser(userId, viewPwd): - return .apiHideUser(userId: userId, viewPwd: obfuscate(viewPwd)) - case let .apiUnhideUser(userId, viewPwd): - return .apiUnhideUser(userId: userId, viewPwd: obfuscate(viewPwd)) - case let .apiDeleteUser(userId, delSMPQueues, viewPwd): - return .apiDeleteUser(userId: userId, delSMPQueues: delSMPQueues, viewPwd: obfuscate(viewPwd)) - case let .testStorageEncryption(key): - return .testStorageEncryption(key: obfuscate(key)) - default: return self + case let .result(r): r.responseType + case let .error(e): "error \(e.errorType)" + case let .invalid(type, _): "* \(type)" + } + } + + public var unexpected: ChatError { + switch self { + case let .result(r): .unexpectedResult(type: r.responseType) + case let .error(e): e + case let .invalid(type, _): .unexpectedResult(type: "* \(type)") } } - private func obfuscate(_ s: String) -> String { - s == "" ? "" : "***" - } - - private func obfuscate(_ s: String?) -> String? { - if let s = s { - return obfuscate(s) + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + if container.contains(.result) { + let result = try container.decode(R.self, forKey: .result) + self = .result(result) + } else { + let error = try container.decode(ChatError.self, forKey: .error) + self = .error(error) } + } + + private enum CodingKeys: String, CodingKey { + case result, error + } +} + +public protocol ChatAPIResult: Decodable { + var responseType: String { get } + var details: String { get } + static func fallbackResult(_ type: String, _ json: NSDictionary) -> Self? +} + +extension ChatAPIResult { + public var noDetails: String { "\(self.responseType): no details" } + + @inline(__always) + public static func fallbackResult(_ type: String, _ json: NSDictionary) -> Self? { + nil + } + + @inline(__always) + public var unexpected: ChatError { + .unexpectedResult(type: self.responseType) + } +} + +public func decodeAPIResult(_ d: Data) -> APIResult { +// print("decodeAPIResult \(String(describing: R.self))") + do { +// return try withStackSizeLimit { try jsonDecoder.decode(APIResult.self, from: d) } + return try jsonDecoder.decode(APIResult.self, from: d) + } catch {} + if let j = try? JSONSerialization.jsonObject(with: d) as? NSDictionary { + if let (_, jErr) = getOWSF(j, "error") { + return APIResult.error(.invalidJSON(json: errorJson(jErr))) as APIResult + } else if let (type, jRes) = getOWSF(j, "result") { + return if let r = R.fallbackResult(type, jRes) { + APIResult.result(r) + } else { + APIResult.invalid(type: type, json: dataPrefix(d)) + } + } + } + return APIResult.invalid(type: "invalid", json: dataPrefix(d)) +} + +// Default stack size for the main thread is 1mb, for secondary threads - 512 kb. +// This function can be used to test what size is used (or to increase available stack size). +// Stack size must be a multiple of system page size (16kb). +//private let stackSizeLimit: Int = 256 * 1024 +// +//private func withStackSizeLimit(_ f: @escaping () throws -> T) throws -> T { +// let semaphore = DispatchSemaphore(value: 0) +// var result: Result? +// let thread = Thread { +// do { +// result = .success(try f()) +// } catch { +// result = .failure(error) +// } +// semaphore.signal() +// } +// +// thread.stackSize = stackSizeLimit +// thread.qualityOfService = Thread.current.qualityOfService +// thread.start() +// +// semaphore.wait() +// +// switch result! { +// case let .success(r): return r +// case let .failure(e): throw e +// } +//} + +public func parseApiChats(_ jResp: NSDictionary) -> (user: UserRef, chats: [ChatData])? { + if let jApiChats = jResp["apiChats"] as? NSDictionary, + let user: UserRef = try? decodeObject(jApiChats["user"] as Any), + let jChats = jApiChats["chats"] as? NSArray { + let chats = jChats.map { jChat in + if let chatData = try? parseChatData(jChat) { + return chatData.0 + } + return ChatData.invalidJSON(serializeJSON(jChat, options: .prettyPrinted)) + } + return (user, chats) + } else { return nil } +} - private func onOff(_ b: Bool) -> String { - b ? "on" : "off" +public func withUser(_ u: (any UserLike)?, _ s: String) -> String { + if let id = u?.userId { + return "userId: \(id)\n\(s)" + } + return s +} + +public struct CreatedConnLink: Decodable, Hashable { + public var connFullLink: String + public var connShortLink: String? + + public init(connFullLink: String, connShortLink: String?) { + self.connFullLink = connFullLink + self.connShortLink = connShortLink } - private func onOffParam(_ param: String, _ b: Bool?) -> String { - if let b = b { - return " \(param)=\(onOff(b))" - } - return "" - } - - private func maybePwd(_ pwd: String?) -> String { - pwd == "" || pwd == nil ? "" : " " + encodeJSON(pwd) + public func simplexChatUri(short: Bool = true) -> String { + short ? (connShortLink ?? simplexChatLink(connFullLink)) : simplexChatLink(connFullLink) } } -public struct APIResponse: Decodable { - var resp: ChatResponse +public func simplexChatLink(_ uri: String) -> String { + uri.starts(with: "simplex:/") + ? uri.replacingOccurrences(of: "simplex:/", with: "https://simplex.chat/") + : uri } -public enum ChatResponse: Decodable, Error { - case response(type: String, json: String) - case activeUser(user: User) - case usersList(users: [UserInfo]) - case chatStarted - case chatRunning - case chatStopped - case chatSuspended - case apiChats(user: UserRef, chats: [ChatData]) - case apiChat(user: UserRef, chat: ChatData) - case chatItemInfo(user: UserRef, chatItem: AChatItem, chatItemInfo: ChatItemInfo) - case userProtoServers(user: UserRef, servers: UserProtoServers) - case serverTestResult(user: UserRef, testServer: String, testFailure: ProtocolTestFailure?) - case chatItemTTL(user: UserRef, chatItemTTL: Int64?) - case networkConfig(networkConfig: NetCfg) - case contactInfo(user: UserRef, contact: Contact, connectionStats_: ConnectionStats?, customUserProfile: Profile?) - case groupMemberInfo(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats_: ConnectionStats?) - case contactSwitchStarted(user: UserRef, contact: Contact, connectionStats: ConnectionStats) - case groupMemberSwitchStarted(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats) - case contactSwitchAborted(user: UserRef, contact: Contact, connectionStats: ConnectionStats) - case groupMemberSwitchAborted(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats) - case contactSwitch(user: UserRef, contact: Contact, switchProgress: SwitchProgress) - case groupMemberSwitch(user: UserRef, groupInfo: GroupInfo, member: GroupMember, switchProgress: SwitchProgress) - case contactRatchetSyncStarted(user: UserRef, contact: Contact, connectionStats: ConnectionStats) - case groupMemberRatchetSyncStarted(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats) - case contactRatchetSync(user: UserRef, contact: Contact, ratchetSyncProgress: RatchetSyncProgress) - case groupMemberRatchetSync(user: UserRef, groupInfo: GroupInfo, member: GroupMember, ratchetSyncProgress: RatchetSyncProgress) - case contactVerificationReset(user: UserRef, contact: Contact) - case groupMemberVerificationReset(user: UserRef, groupInfo: GroupInfo, member: GroupMember) - case contactCode(user: UserRef, contact: Contact, connectionCode: String) - case groupMemberCode(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionCode: String) - case connectionVerified(user: UserRef, verified: Bool, expectedCode: String) - case invitation(user: UserRef, connReqInvitation: String, connection: PendingContactConnection) - case connectionIncognitoUpdated(user: UserRef, toConnection: PendingContactConnection) - case connectionPlan(user: UserRef, connectionPlan: ConnectionPlan) - case sentConfirmation(user: UserRef, connection: PendingContactConnection) - case sentInvitation(user: UserRef, connection: PendingContactConnection) - case sentInvitationToContact(user: UserRef, contact: Contact, customUserProfile: Profile?) - case contactAlreadyExists(user: UserRef, contact: Contact) - case contactRequestAlreadyAccepted(user: UserRef, contact: Contact) - case contactDeleted(user: UserRef, contact: Contact) - case contactDeletedByContact(user: UserRef, contact: Contact) - case chatCleared(user: UserRef, chatInfo: ChatInfo) - case userProfileNoChange(user: User) - case userProfileUpdated(user: User, fromProfile: Profile, toProfile: Profile, updateSummary: UserProfileUpdateSummary) - case userPrivacy(user: User, updatedUser: User) - case contactAliasUpdated(user: UserRef, toContact: Contact) - case connectionAliasUpdated(user: UserRef, toConnection: PendingContactConnection) - case contactPrefsUpdated(user: User, fromContact: Contact, toContact: Contact) - case userContactLink(user: User, contactLink: UserContactLink) - case userContactLinkUpdated(user: User, contactLink: UserContactLink) - case userContactLinkCreated(user: User, connReqContact: String) - case userContactLinkDeleted(user: User) - case contactConnected(user: UserRef, contact: Contact, userCustomProfile: Profile?) - case contactConnecting(user: UserRef, contact: Contact) - case receivedContactRequest(user: UserRef, contactRequest: UserContactRequest) - case acceptingContactRequest(user: UserRef, contact: Contact) - case contactRequestRejected(user: UserRef) - case contactUpdated(user: UserRef, toContact: Contact) - case groupMemberUpdated(user: UserRef, groupInfo: GroupInfo, fromMember: GroupMember, toMember: GroupMember) - // TODO remove events below - case contactsSubscribed(server: String, contactRefs: [ContactRef]) - case contactsDisconnected(server: String, contactRefs: [ContactRef]) - case contactSubSummary(user: UserRef, contactSubscriptions: [ContactSubStatus]) - // TODO remove events above - case networkStatus(networkStatus: NetworkStatus, connections: [String]) - case networkStatuses(user_: UserRef?, networkStatuses: [ConnNetworkStatus]) - case groupSubscribed(user: UserRef, groupInfo: GroupRef) - case memberSubErrors(user: UserRef, memberSubErrors: [MemberSubError]) - case groupEmpty(user: UserRef, groupInfo: GroupInfo) - case userContactLinkSubscribed - case newChatItem(user: UserRef, chatItem: AChatItem) - case chatItemStatusUpdated(user: UserRef, chatItem: AChatItem) - case chatItemUpdated(user: UserRef, chatItem: AChatItem) - case chatItemNotChanged(user: UserRef, chatItem: AChatItem) - case chatItemReaction(user: UserRef, added: Bool, reaction: ACIReaction) - case chatItemDeleted(user: UserRef, deletedChatItem: AChatItem, toChatItem: AChatItem?, byUser: Bool) - case contactsList(user: UserRef, contacts: [Contact]) - // group events - case groupCreated(user: UserRef, groupInfo: GroupInfo) - case sentGroupInvitation(user: UserRef, groupInfo: GroupInfo, contact: Contact, member: GroupMember) - case userAcceptedGroupSent(user: UserRef, groupInfo: GroupInfo, hostContact: Contact?) - case groupLinkConnecting(user: UserRef, groupInfo: GroupInfo, hostMember: GroupMember) - case userDeletedMember(user: UserRef, groupInfo: GroupInfo, member: GroupMember) - case leftMemberUser(user: UserRef, groupInfo: GroupInfo) - case groupMembers(user: UserRef, group: Group) - case receivedGroupInvitation(user: UserRef, groupInfo: GroupInfo, contact: Contact, memberRole: GroupMemberRole) - case groupDeletedUser(user: UserRef, groupInfo: GroupInfo) - case joinedGroupMemberConnecting(user: UserRef, groupInfo: GroupInfo, hostMember: GroupMember, member: GroupMember) - case memberRole(user: UserRef, groupInfo: GroupInfo, byMember: GroupMember, member: GroupMember, fromRole: GroupMemberRole, toRole: GroupMemberRole) - case memberRoleUser(user: UserRef, groupInfo: GroupInfo, member: GroupMember, fromRole: GroupMemberRole, toRole: GroupMemberRole) - case memberBlockedForAll(user: UserRef, groupInfo: GroupInfo, byMember: GroupMember, member: GroupMember, blocked: Bool) - case memberBlockedForAllUser(user: UserRef, groupInfo: GroupInfo, member: GroupMember, blocked: Bool) - case deletedMemberUser(user: UserRef, groupInfo: GroupInfo, member: GroupMember) - case deletedMember(user: UserRef, groupInfo: GroupInfo, byMember: GroupMember, deletedMember: GroupMember) - case leftMember(user: UserRef, groupInfo: GroupInfo, member: GroupMember) - case groupDeleted(user: UserRef, groupInfo: GroupInfo, member: GroupMember) - case contactsMerged(user: UserRef, intoContact: Contact, mergedContact: Contact) - case groupInvitation(user: UserRef, groupInfo: GroupInfo) // unused - case userJoinedGroup(user: UserRef, groupInfo: GroupInfo) - case joinedGroupMember(user: UserRef, groupInfo: GroupInfo, member: GroupMember) - case connectedToGroupMember(user: UserRef, groupInfo: GroupInfo, member: GroupMember, memberContact: Contact?) - case groupRemoved(user: UserRef, groupInfo: GroupInfo) // unused - case groupUpdated(user: UserRef, toGroup: GroupInfo) - case groupLinkCreated(user: UserRef, groupInfo: GroupInfo, connReqContact: String, memberRole: GroupMemberRole) - case groupLink(user: UserRef, groupInfo: GroupInfo, connReqContact: String, memberRole: GroupMemberRole) - case groupLinkDeleted(user: UserRef, groupInfo: GroupInfo) - case newMemberContact(user: UserRef, contact: Contact, groupInfo: GroupInfo, member: GroupMember) - case newMemberContactSentInv(user: UserRef, contact: Contact, groupInfo: GroupInfo, member: GroupMember) - case newMemberContactReceivedInv(user: UserRef, contact: Contact, groupInfo: GroupInfo, member: GroupMember) - // receiving file events - case rcvFileAccepted(user: UserRef, chatItem: AChatItem) - case rcvFileAcceptedSndCancelled(user: UserRef, rcvFileTransfer: RcvFileTransfer) - case standaloneFileInfo(fileMeta: MigrationFileLinkData?) - case rcvStandaloneFileCreated(user: UserRef, rcvFileTransfer: RcvFileTransfer) - case rcvFileStart(user: UserRef, chatItem: AChatItem) // send by chats - case rcvFileProgressXFTP(user: UserRef, chatItem_: AChatItem?, receivedSize: Int64, totalSize: Int64, rcvFileTransfer: RcvFileTransfer) - case rcvFileComplete(user: UserRef, chatItem: AChatItem) - case rcvStandaloneFileComplete(user: UserRef, targetPath: String, rcvFileTransfer: RcvFileTransfer) - case rcvFileCancelled(user: UserRef, chatItem_: AChatItem?, rcvFileTransfer: RcvFileTransfer) - case rcvFileSndCancelled(user: UserRef, chatItem: AChatItem, rcvFileTransfer: RcvFileTransfer) - case rcvFileError(user: UserRef, chatItem_: AChatItem?, rcvFileTransfer: RcvFileTransfer) - // sending file events - case sndFileStart(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer) - case sndFileComplete(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer) - case sndFileRcvCancelled(user: UserRef, chatItem_: AChatItem?, sndFileTransfer: SndFileTransfer) - case sndFileCancelled(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, sndFileTransfers: [SndFileTransfer]) - case sndStandaloneFileCreated(user: UserRef, fileTransferMeta: FileTransferMeta) // returned by _upload - case sndFileStartXFTP(user: UserRef, chatItem: AChatItem, fileTransferMeta: FileTransferMeta) // not used - case sndFileProgressXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, sentSize: Int64, totalSize: Int64) - case sndFileRedirectStartXFTP(user: UserRef, fileTransferMeta: FileTransferMeta, redirectMeta: FileTransferMeta) - case sndFileCompleteXFTP(user: UserRef, chatItem: AChatItem, fileTransferMeta: FileTransferMeta) - case sndStandaloneFileComplete(user: UserRef, fileTransferMeta: FileTransferMeta, rcvURIs: [String]) - case sndFileCancelledXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta) - case sndFileError(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta) - // call events - case callInvitation(callInvitation: RcvCallInvitation) - case callOffer(user: UserRef, contact: Contact, callType: CallType, offer: WebRTCSession, sharedKey: String?, askConfirmation: Bool) - case callAnswer(user: UserRef, contact: Contact, answer: WebRTCSession) - case callExtraInfo(user: UserRef, contact: Contact, extraInfo: WebRTCExtraInfo) - case callEnded(user: UserRef, contact: Contact) - case callInvitations(callInvitations: [RcvCallInvitation]) - case ntfTokenStatus(status: NtfTknStatus) - case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode, ntfServer: String) - case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo]) - case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgInfo) - case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection) - // remote desktop responses/events - case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo]) - case remoteCtrlFound(remoteCtrl: RemoteCtrlInfo, ctrlAppInfo_: CtrlAppInfo?, appVersion: String, compatible: Bool) - case remoteCtrlConnecting(remoteCtrl_: RemoteCtrlInfo?, ctrlAppInfo: CtrlAppInfo, appVersion: String) - case remoteCtrlSessionCode(remoteCtrl_: RemoteCtrlInfo?, sessionCode: String) - case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo) - case remoteCtrlStopped(rcsState: RemoteCtrlSessionState, rcStopReason: RemoteCtrlStopReason) - // pq - case contactPQAllowed(user: UserRef, contact: Contact, pqEncryption: Bool) - case contactPQEnabled(user: UserRef, contact: Contact, pqEnabled: Bool) - // misc - case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration]) - case cmdOk(user: UserRef?) - case chatCmdError(user_: UserRef?, chatError: ChatError) - case chatError(user_: UserRef?, chatError: ChatError) - case archiveImported(archiveErrors: [ArchiveError]) - case appSettings(appSettings: AppSettings) - - public var responseType: String { - get { - switch self { - case let .response(type, _): return "* \(type)" - case .activeUser: return "activeUser" - case .usersList: return "usersList" - case .chatStarted: return "chatStarted" - case .chatRunning: return "chatRunning" - case .chatStopped: return "chatStopped" - case .chatSuspended: return "chatSuspended" - case .apiChats: return "apiChats" - case .apiChat: return "apiChat" - case .chatItemInfo: return "chatItemInfo" - case .userProtoServers: return "userProtoServers" - case .serverTestResult: return "serverTestResult" - case .chatItemTTL: return "chatItemTTL" - case .networkConfig: return "networkConfig" - case .contactInfo: return "contactInfo" - case .groupMemberInfo: return "groupMemberInfo" - case .contactSwitchStarted: return "contactSwitchStarted" - case .groupMemberSwitchStarted: return "groupMemberSwitchStarted" - case .contactSwitchAborted: return "contactSwitchAborted" - case .groupMemberSwitchAborted: return "groupMemberSwitchAborted" - case .contactSwitch: return "contactSwitch" - case .groupMemberSwitch: return "groupMemberSwitch" - case .contactRatchetSyncStarted: return "contactRatchetSyncStarted" - case .groupMemberRatchetSyncStarted: return "groupMemberRatchetSyncStarted" - case .contactRatchetSync: return "contactRatchetSync" - case .groupMemberRatchetSync: return "groupMemberRatchetSync" - case .contactVerificationReset: return "contactVerificationReset" - case .groupMemberVerificationReset: return "groupMemberVerificationReset" - case .contactCode: return "contactCode" - case .groupMemberCode: return "groupMemberCode" - case .connectionVerified: return "connectionVerified" - case .invitation: return "invitation" - case .connectionIncognitoUpdated: return "connectionIncognitoUpdated" - case .connectionPlan: return "connectionPlan" - case .sentConfirmation: return "sentConfirmation" - case .sentInvitation: return "sentInvitation" - case .sentInvitationToContact: return "sentInvitationToContact" - case .contactAlreadyExists: return "contactAlreadyExists" - case .contactRequestAlreadyAccepted: return "contactRequestAlreadyAccepted" - case .contactDeleted: return "contactDeleted" - case .contactDeletedByContact: return "contactDeletedByContact" - case .chatCleared: return "chatCleared" - case .userProfileNoChange: return "userProfileNoChange" - case .userProfileUpdated: return "userProfileUpdated" - case .userPrivacy: return "userPrivacy" - case .contactAliasUpdated: return "contactAliasUpdated" - case .connectionAliasUpdated: return "connectionAliasUpdated" - case .contactPrefsUpdated: return "contactPrefsUpdated" - case .userContactLink: return "userContactLink" - case .userContactLinkUpdated: return "userContactLinkUpdated" - case .userContactLinkCreated: return "userContactLinkCreated" - case .userContactLinkDeleted: return "userContactLinkDeleted" - case .contactConnected: return "contactConnected" - case .contactConnecting: return "contactConnecting" - case .receivedContactRequest: return "receivedContactRequest" - case .acceptingContactRequest: return "acceptingContactRequest" - case .contactRequestRejected: return "contactRequestRejected" - case .contactUpdated: return "contactUpdated" - case .groupMemberUpdated: return "groupMemberUpdated" - case .contactsSubscribed: return "contactsSubscribed" - case .contactsDisconnected: return "contactsDisconnected" - case .contactSubSummary: return "contactSubSummary" - case .networkStatus: return "networkStatus" - case .networkStatuses: return "networkStatuses" - case .groupSubscribed: return "groupSubscribed" - case .memberSubErrors: return "memberSubErrors" - case .groupEmpty: return "groupEmpty" - case .userContactLinkSubscribed: return "userContactLinkSubscribed" - case .newChatItem: return "newChatItem" - case .chatItemStatusUpdated: return "chatItemStatusUpdated" - case .chatItemUpdated: return "chatItemUpdated" - case .chatItemNotChanged: return "chatItemNotChanged" - case .chatItemReaction: return "chatItemReaction" - case .chatItemDeleted: return "chatItemDeleted" - case .contactsList: return "contactsList" - case .groupCreated: return "groupCreated" - case .sentGroupInvitation: return "sentGroupInvitation" - case .userAcceptedGroupSent: return "userAcceptedGroupSent" - case .groupLinkConnecting: return "groupLinkConnecting" - case .userDeletedMember: return "userDeletedMember" - case .leftMemberUser: return "leftMemberUser" - case .groupMembers: return "groupMembers" - case .receivedGroupInvitation: return "receivedGroupInvitation" - case .groupDeletedUser: return "groupDeletedUser" - case .joinedGroupMemberConnecting: return "joinedGroupMemberConnecting" - case .memberRole: return "memberRole" - case .memberRoleUser: return "memberRoleUser" - case .memberBlockedForAll: return "memberBlockedForAll" - case .memberBlockedForAllUser: return "memberBlockedForAllUser" - case .deletedMemberUser: return "deletedMemberUser" - case .deletedMember: return "deletedMember" - case .leftMember: return "leftMember" - case .groupDeleted: return "groupDeleted" - case .contactsMerged: return "contactsMerged" - case .groupInvitation: return "groupInvitation" - case .userJoinedGroup: return "userJoinedGroup" - case .joinedGroupMember: return "joinedGroupMember" - case .connectedToGroupMember: return "connectedToGroupMember" - case .groupRemoved: return "groupRemoved" - case .groupUpdated: return "groupUpdated" - case .groupLinkCreated: return "groupLinkCreated" - case .groupLink: return "groupLink" - case .groupLinkDeleted: return "groupLinkDeleted" - case .newMemberContact: return "newMemberContact" - case .newMemberContactSentInv: return "newMemberContactSentInv" - case .newMemberContactReceivedInv: return "newMemberContactReceivedInv" - case .rcvFileAccepted: return "rcvFileAccepted" - case .rcvFileAcceptedSndCancelled: return "rcvFileAcceptedSndCancelled" - case .standaloneFileInfo: return "standaloneFileInfo" - case .rcvStandaloneFileCreated: return "rcvStandaloneFileCreated" - case .rcvFileStart: return "rcvFileStart" - case .rcvFileProgressXFTP: return "rcvFileProgressXFTP" - case .rcvFileComplete: return "rcvFileComplete" - case .rcvStandaloneFileComplete: return "rcvStandaloneFileComplete" - case .rcvFileCancelled: return "rcvFileCancelled" - case .rcvFileSndCancelled: return "rcvFileSndCancelled" - case .rcvFileError: return "rcvFileError" - case .sndFileStart: return "sndFileStart" - case .sndFileComplete: return "sndFileComplete" - case .sndFileCancelled: return "sndFileCancelled" - case .sndStandaloneFileCreated: return "sndStandaloneFileCreated" - case .sndFileStartXFTP: return "sndFileStartXFTP" - case .sndFileProgressXFTP: return "sndFileProgressXFTP" - case .sndFileRedirectStartXFTP: return "sndFileRedirectStartXFTP" - case .sndFileRcvCancelled: return "sndFileRcvCancelled" - case .sndFileCompleteXFTP: return "sndFileCompleteXFTP" - case .sndStandaloneFileComplete: return "sndStandaloneFileComplete" - case .sndFileCancelledXFTP: return "sndFileCancelledXFTP" - case .sndFileError: return "sndFileError" - case .callInvitation: return "callInvitation" - case .callOffer: return "callOffer" - case .callAnswer: return "callAnswer" - case .callExtraInfo: return "callExtraInfo" - case .callEnded: return "callEnded" - case .callInvitations: return "callInvitations" - case .ntfTokenStatus: return "ntfTokenStatus" - case .ntfToken: return "ntfToken" - case .ntfMessages: return "ntfMessages" - case .ntfMessage: return "ntfMessage" - case .contactConnectionDeleted: return "contactConnectionDeleted" - case .remoteCtrlList: return "remoteCtrlList" - case .remoteCtrlFound: return "remoteCtrlFound" - case .remoteCtrlConnecting: return "remoteCtrlConnecting" - case .remoteCtrlSessionCode: return "remoteCtrlSessionCode" - case .remoteCtrlConnected: return "remoteCtrlConnected" - case .remoteCtrlStopped: return "remoteCtrlStopped" - case .contactPQAllowed: return "contactPQAllowed" - case .contactPQEnabled: return "contactPQAllowed" - case .versionInfo: return "versionInfo" - case .cmdOk: return "cmdOk" - case .chatCmdError: return "chatCmdError" - case .chatError: return "chatError" - case .archiveImported: return "archiveImported" - case .appSettings: return "appSettings" - } - } - } - - public var details: String { - get { - switch self { - case let .response(_, json): return json - case let .activeUser(user): return String(describing: user) - case let .usersList(users): return String(describing: users) - case .chatStarted: return noDetails - case .chatRunning: return noDetails - case .chatStopped: return noDetails - case .chatSuspended: return noDetails - case let .apiChats(u, chats): return withUser(u, String(describing: chats)) - case let .apiChat(u, chat): return withUser(u, String(describing: chat)) - case let .chatItemInfo(u, chatItem, chatItemInfo): return withUser(u, "chatItem: \(String(describing: chatItem))\nchatItemInfo: \(String(describing: chatItemInfo))") - case let .userProtoServers(u, servers): return withUser(u, "servers: \(String(describing: servers))") - case let .serverTestResult(u, server, testFailure): return withUser(u, "server: \(server)\nresult: \(String(describing: testFailure))") - case let .chatItemTTL(u, chatItemTTL): return withUser(u, String(describing: chatItemTTL)) - case let .networkConfig(networkConfig): return String(describing: networkConfig) - case let .contactInfo(u, contact, connectionStats_, customUserProfile): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats_: \(String(describing: connectionStats_))\ncustomUserProfile: \(String(describing: customUserProfile))") - case let .groupMemberInfo(u, groupInfo, member, connectionStats_): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats_: \(String(describing: connectionStats_))") - case let .contactSwitchStarted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))") - case let .groupMemberSwitchStarted(u, groupInfo, member, connectionStats): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats: \(String(describing: connectionStats))") - case let .contactSwitchAborted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))") - case let .groupMemberSwitchAborted(u, groupInfo, member, connectionStats): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats: \(String(describing: connectionStats))") - case let .contactSwitch(u, contact, switchProgress): return withUser(u, "contact: \(String(describing: contact))\nswitchProgress: \(String(describing: switchProgress))") - case let .groupMemberSwitch(u, groupInfo, member, switchProgress): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nswitchProgress: \(String(describing: switchProgress))") - case let .contactRatchetSyncStarted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))") - case let .groupMemberRatchetSyncStarted(u, groupInfo, member, connectionStats): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats: \(String(describing: connectionStats))") - case let .contactRatchetSync(u, contact, ratchetSyncProgress): return withUser(u, "contact: \(String(describing: contact))\nratchetSyncProgress: \(String(describing: ratchetSyncProgress))") - case let .groupMemberRatchetSync(u, groupInfo, member, ratchetSyncProgress): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nratchetSyncProgress: \(String(describing: ratchetSyncProgress))") - case let .contactVerificationReset(u, contact): return withUser(u, "contact: \(String(describing: contact))") - case let .groupMemberVerificationReset(u, groupInfo, member): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))") - case let .contactCode(u, contact, connectionCode): return withUser(u, "contact: \(String(describing: contact))\nconnectionCode: \(connectionCode)") - case let .groupMemberCode(u, groupInfo, member, connectionCode): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionCode: \(connectionCode)") - case let .connectionVerified(u, verified, expectedCode): return withUser(u, "verified: \(verified)\nconnectionCode: \(expectedCode)") - case let .invitation(u, connReqInvitation, connection): return withUser(u, "connReqInvitation: \(connReqInvitation)\nconnection: \(connection)") - case let .connectionIncognitoUpdated(u, toConnection): return withUser(u, String(describing: toConnection)) - case let .connectionPlan(u, connectionPlan): return withUser(u, String(describing: connectionPlan)) - case let .sentConfirmation(u, connection): return withUser(u, String(describing: connection)) - case let .sentInvitation(u, connection): return withUser(u, String(describing: connection)) - case let .sentInvitationToContact(u, contact, _): return withUser(u, String(describing: contact)) - case let .contactAlreadyExists(u, contact): return withUser(u, String(describing: contact)) - case let .contactRequestAlreadyAccepted(u, contact): return withUser(u, String(describing: contact)) - case let .contactDeleted(u, contact): return withUser(u, String(describing: contact)) - case let .contactDeletedByContact(u, contact): return withUser(u, String(describing: contact)) - case let .chatCleared(u, chatInfo): return withUser(u, String(describing: chatInfo)) - case .userProfileNoChange: return noDetails - case let .userProfileUpdated(u, _, toProfile, _): return withUser(u, String(describing: toProfile)) - case let .userPrivacy(u, updatedUser): return withUser(u, String(describing: updatedUser)) - case let .contactAliasUpdated(u, toContact): return withUser(u, String(describing: toContact)) - case let .connectionAliasUpdated(u, toConnection): return withUser(u, String(describing: toConnection)) - case let .contactPrefsUpdated(u, fromContact, toContact): return withUser(u, "fromContact: \(String(describing: fromContact))\ntoContact: \(String(describing: toContact))") - case let .userContactLink(u, contactLink): return withUser(u, contactLink.responseDetails) - case let .userContactLinkUpdated(u, contactLink): return withUser(u, contactLink.responseDetails) - case let .userContactLinkCreated(u, connReq): return withUser(u, connReq) - case .userContactLinkDeleted: return noDetails - case let .contactConnected(u, contact, _): return withUser(u, String(describing: contact)) - case let .contactConnecting(u, contact): return withUser(u, String(describing: contact)) - case let .receivedContactRequest(u, contactRequest): return withUser(u, String(describing: contactRequest)) - case let .acceptingContactRequest(u, contact): return withUser(u, String(describing: contact)) - case .contactRequestRejected: return noDetails - case let .contactUpdated(u, toContact): return withUser(u, String(describing: toContact)) - case let .groupMemberUpdated(u, groupInfo, fromMember, toMember): return withUser(u, "groupInfo: \(groupInfo)\nfromMember: \(fromMember)\ntoMember: \(toMember)") - case let .contactsSubscribed(server, contactRefs): return "server: \(server)\ncontacts:\n\(String(describing: contactRefs))" - case let .contactsDisconnected(server, contactRefs): return "server: \(server)\ncontacts:\n\(String(describing: contactRefs))" - case let .contactSubSummary(u, contactSubscriptions): return withUser(u, String(describing: contactSubscriptions)) - case let .networkStatus(status, conns): return "networkStatus: \(String(describing: status))\nconnections: \(String(describing: conns))" - case let .networkStatuses(u, statuses): return withUser(u, String(describing: statuses)) - case let .groupSubscribed(u, groupInfo): return withUser(u, String(describing: groupInfo)) - case let .memberSubErrors(u, memberSubErrors): return withUser(u, String(describing: memberSubErrors)) - case let .groupEmpty(u, groupInfo): return withUser(u, String(describing: groupInfo)) - case .userContactLinkSubscribed: return noDetails - case let .newChatItem(u, chatItem): return withUser(u, String(describing: chatItem)) - case let .chatItemStatusUpdated(u, chatItem): return withUser(u, String(describing: chatItem)) - case let .chatItemUpdated(u, chatItem): return withUser(u, String(describing: chatItem)) - case let .chatItemNotChanged(u, chatItem): return withUser(u, String(describing: chatItem)) - case let .chatItemReaction(u, added, reaction): return withUser(u, "added: \(added)\n\(String(describing: reaction))") - case let .chatItemDeleted(u, deletedChatItem, toChatItem, byUser): return withUser(u, "deletedChatItem:\n\(String(describing: deletedChatItem))\ntoChatItem:\n\(String(describing: toChatItem))\nbyUser: \(byUser)") - case let .contactsList(u, contacts): return withUser(u, String(describing: contacts)) - case let .groupCreated(u, groupInfo): return withUser(u, String(describing: groupInfo)) - case let .sentGroupInvitation(u, groupInfo, contact, member): return withUser(u, "groupInfo: \(groupInfo)\ncontact: \(contact)\nmember: \(member)") - case let .userAcceptedGroupSent(u, groupInfo, hostContact): return withUser(u, "groupInfo: \(groupInfo)\nhostContact: \(String(describing: hostContact))") - case let .groupLinkConnecting(u, groupInfo, hostMember): return withUser(u, "groupInfo: \(groupInfo)\nhostMember: \(String(describing: hostMember))") - case let .userDeletedMember(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)") - case let .leftMemberUser(u, groupInfo): return withUser(u, String(describing: groupInfo)) - case let .groupMembers(u, group): return withUser(u, String(describing: group)) - case let .receivedGroupInvitation(u, groupInfo, contact, memberRole): return withUser(u, "groupInfo: \(groupInfo)\ncontact: \(contact)\nmemberRole: \(memberRole)") - case let .groupDeletedUser(u, groupInfo): return withUser(u, String(describing: groupInfo)) - case let .joinedGroupMemberConnecting(u, groupInfo, hostMember, member): return withUser(u, "groupInfo: \(groupInfo)\nhostMember: \(hostMember)\nmember: \(member)") - case let .memberRole(u, groupInfo, byMember, member, fromRole, toRole): return withUser(u, "groupInfo: \(groupInfo)\nbyMember: \(byMember)\nmember: \(member)\nfromRole: \(fromRole)\ntoRole: \(toRole)") - case let .memberRoleUser(u, groupInfo, member, fromRole, toRole): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)\nfromRole: \(fromRole)\ntoRole: \(toRole)") - case let .memberBlockedForAll(u, groupInfo, byMember, member, blocked): return withUser(u, "groupInfo: \(groupInfo)\nbyMember: \(byMember)\nmember: \(member)\nblocked: \(blocked)") - case let .memberBlockedForAllUser(u, groupInfo, member, blocked): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)\nblocked: \(blocked)") - case let .deletedMemberUser(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)") - case let .deletedMember(u, groupInfo, byMember, deletedMember): return withUser(u, "groupInfo: \(groupInfo)\nbyMember: \(byMember)\ndeletedMember: \(deletedMember)") - case let .leftMember(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)") - case let .groupDeleted(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)") - case let .contactsMerged(u, intoContact, mergedContact): return withUser(u, "intoContact: \(intoContact)\nmergedContact: \(mergedContact)") - case let .groupInvitation(u, groupInfo): return withUser(u, String(describing: groupInfo)) - case let .userJoinedGroup(u, groupInfo): return withUser(u, String(describing: groupInfo)) - case let .joinedGroupMember(u, groupInfo, member): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)") - case let .connectedToGroupMember(u, groupInfo, member, memberContact): return withUser(u, "groupInfo: \(groupInfo)\nmember: \(member)\nmemberContact: \(String(describing: memberContact))") - case let .groupRemoved(u, groupInfo): return withUser(u, String(describing: groupInfo)) - case let .groupUpdated(u, toGroup): return withUser(u, String(describing: toGroup)) - case let .groupLinkCreated(u, groupInfo, connReqContact, memberRole): return withUser(u, "groupInfo: \(groupInfo)\nconnReqContact: \(connReqContact)\nmemberRole: \(memberRole)") - case let .groupLink(u, groupInfo, connReqContact, memberRole): return withUser(u, "groupInfo: \(groupInfo)\nconnReqContact: \(connReqContact)\nmemberRole: \(memberRole)") - case let .groupLinkDeleted(u, groupInfo): return withUser(u, String(describing: groupInfo)) - case let .newMemberContact(u, contact, groupInfo, member): return withUser(u, "contact: \(contact)\ngroupInfo: \(groupInfo)\nmember: \(member)") - case let .newMemberContactSentInv(u, contact, groupInfo, member): return withUser(u, "contact: \(contact)\ngroupInfo: \(groupInfo)\nmember: \(member)") - case let .newMemberContactReceivedInv(u, contact, groupInfo, member): return withUser(u, "contact: \(contact)\ngroupInfo: \(groupInfo)\nmember: \(member)") - case let .rcvFileAccepted(u, chatItem): return withUser(u, String(describing: chatItem)) - case .rcvFileAcceptedSndCancelled: return noDetails - case let .standaloneFileInfo(fileMeta): return String(describing: fileMeta) - case .rcvStandaloneFileCreated: return noDetails - case let .rcvFileStart(u, chatItem): return withUser(u, String(describing: chatItem)) - case let .rcvFileProgressXFTP(u, chatItem, receivedSize, totalSize, _): return withUser(u, "chatItem: \(String(describing: chatItem))\nreceivedSize: \(receivedSize)\ntotalSize: \(totalSize)") - case let .rcvStandaloneFileComplete(u, targetPath, _): return withUser(u, targetPath) - case let .rcvFileComplete(u, chatItem): return withUser(u, String(describing: chatItem)) - case let .rcvFileCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) - case let .rcvFileSndCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) - case let .rcvFileError(u, chatItem, _): return withUser(u, String(describing: chatItem)) - case let .sndFileStart(u, chatItem, _): return withUser(u, String(describing: chatItem)) - case let .sndFileComplete(u, chatItem, _): return withUser(u, String(describing: chatItem)) - case let .sndFileCancelled(u, chatItem, _, _): return withUser(u, String(describing: chatItem)) - case .sndStandaloneFileCreated: return noDetails - case let .sndFileStartXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem)) - case let .sndFileRcvCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) - case let .sndFileProgressXFTP(u, chatItem, _, sentSize, totalSize): return withUser(u, "chatItem: \(String(describing: chatItem))\nsentSize: \(sentSize)\ntotalSize: \(totalSize)") - case let .sndFileRedirectStartXFTP(u, _, redirectMeta): return withUser(u, String(describing: redirectMeta)) - case let .sndFileCompleteXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem)) - case let .sndStandaloneFileComplete(u, _, rcvURIs): return withUser(u, String(rcvURIs.count)) - case let .sndFileCancelledXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem)) - case let .sndFileError(u, chatItem, _): return withUser(u, String(describing: chatItem)) - case let .callInvitation(inv): return String(describing: inv) - case let .callOffer(u, contact, callType, offer, sharedKey, askConfirmation): return withUser(u, "contact: \(contact.id)\ncallType: \(String(describing: callType))\nsharedKey: \(sharedKey ?? "")\naskConfirmation: \(askConfirmation)\noffer: \(String(describing: offer))") - case let .callAnswer(u, contact, answer): return withUser(u, "contact: \(contact.id)\nanswer: \(String(describing: answer))") - case let .callExtraInfo(u, contact, extraInfo): return withUser(u, "contact: \(contact.id)\nextraInfo: \(String(describing: extraInfo))") - case let .callEnded(u, contact): return withUser(u, "contact: \(contact.id)") - case let .callInvitations(invs): return String(describing: invs) - case let .ntfTokenStatus(status): return String(describing: status) - case let .ntfToken(token, status, ntfMode, ntfServer): return "token: \(token)\nstatus: \(status.rawValue)\nntfMode: \(ntfMode.rawValue)\nntfServer: \(ntfServer)" - case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))") - case let .ntfMessage(u, connEntity, ntfMessage): return withUser(u, "connEntity: \(String(describing: connEntity))\nntfMessage: \(String(describing: ntfMessage))") - case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection)) - case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls) - case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): return "remoteCtrl:\n\(String(describing: remoteCtrl))\nctrlAppInfo_:\n\(String(describing: ctrlAppInfo_))\nappVersion: \(appVersion)\ncompatible: \(compatible)" - case let .remoteCtrlConnecting(remoteCtrl_, ctrlAppInfo, appVersion): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nctrlAppInfo:\n\(String(describing: ctrlAppInfo))\nappVersion: \(appVersion)" - case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)" - case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl) - case .remoteCtrlStopped: return noDetails - case let .contactPQAllowed(u, contact, pqEncryption): return withUser(u, "contact: \(String(describing: contact))\npqEncryption: \(pqEncryption)") - case let .contactPQEnabled(u, contact, pqEnabled): return withUser(u, "contact: \(String(describing: contact))\npqEnabled: \(pqEnabled)") - case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))" - case .cmdOk: return noDetails - case let .chatCmdError(u, chatError): return withUser(u, String(describing: chatError)) - case let .chatError(u, chatError): return withUser(u, String(describing: chatError)) - case let .archiveImported(archiveErrors): return String(describing: archiveErrors) - case let .appSettings(appSettings): return String(describing: appSettings) - } - } - } - - private var noDetails: String { get { "\(responseType): no details" } } - - private func withUser(_ u: (any UserLike)?, _ s: String) -> String { - if let id = u?.userId { - return "userId: \(id)\n\(s)" - } - return s - } -} - -public func chatError(_ chatResponse: ChatResponse) -> ChatErrorType? { - switch chatResponse { - case let .chatCmdError(_, .error(error)): return error - case let .chatError(_, .error(error)): return error - default: return nil - } -} - -public enum ConnectionPlan: Decodable { - case invitationLink(invitationLinkPlan: InvitationLinkPlan) - case contactAddress(contactAddressPlan: ContactAddressPlan) - case groupLink(groupLinkPlan: GroupLinkPlan) -} - -public enum InvitationLinkPlan: Decodable { - case ok - case ownLink - case connecting(contact_: Contact?) - case known(contact: Contact) -} - -public enum ContactAddressPlan: Decodable { - case ok - case ownLink - case connectingConfirmReconnect - case connectingProhibit(contact: Contact) - case known(contact: Contact) - case contactViaAddress(contact: Contact) -} - -public enum GroupLinkPlan: Decodable { - case ok - case ownLink(groupInfo: GroupInfo) - case connectingConfirmReconnect - case connectingProhibit(groupInfo_: GroupInfo?) - case known(groupInfo: GroupInfo) -} - -struct NewUser: Encodable { - var profile: Profile? - var sameServers: Bool - var pastTimestamp: Bool -} - -public enum ChatPagination { - case last(count: Int) - case after(chatItemId: Int64, count: Int) - case before(chatItemId: Int64, count: Int) - - var cmdString: String { - switch self { - case let .last(count): return "count=\(count)" - case let .after(chatItemId, count): return "after=\(chatItemId) count=\(count)" - case let .before(chatItemId, count): return "before=\(chatItemId) count=\(count)" - } - } -} - -struct ComposedMessage: Encodable { - var fileSource: CryptoFile? +public struct ComposedMessage: Encodable { + public var fileSource: CryptoFile? var quotedItemId: Int64? - var msgContent: MsgContent -} + public var msgContent: MsgContent + public var mentions: [String: Int64] -public struct ArchiveConfig: Encodable { - var archivePath: String - var disableCompression: Bool? - - public init(archivePath: String, disableCompression: Bool? = nil) { - self.archivePath = archivePath - self.disableCompression = disableCompression + public init(fileSource: CryptoFile? = nil, quotedItemId: Int64? = nil, msgContent: MsgContent, mentions: [String: Int64] = [:]) { + self.fileSource = fileSource + self.quotedItemId = quotedItemId + self.msgContent = msgContent + self.mentions = mentions } } -public struct DBEncryptionConfig: Codable { - public init(currentKey: String, newKey: String) { - self.currentKey = currentKey - self.newKey = newKey - } - - public var currentKey: String - public var newKey: String -} - -struct SMPServersConfig: Encodable { - var smpServers: [ServerCfg] -} - public enum ServerProtocol: String, Decodable { case smp case xftp } -public struct ProtoServersConfig: Codable { - public var servers: [ServerCfg] -} - -public struct UserProtoServers: Decodable { - public var serverProtocol: ServerProtocol - public var protoServers: [ServerCfg] - public var presetServers: [String] -} - -public struct ServerCfg: Identifiable, Equatable, Codable { - public var server: String - public var preset: Bool - public var tested: Bool? - public var enabled: Bool - var createdAt = Date() -// public var sendEnabled: Bool // can we potentially want to prevent sending on the servers we use to receive? -// Even if we don't see the use case, it's probably better to allow it in the model -// In any case, "trusted/known" servers are out of scope of this change - - public init(server: String, preset: Bool, tested: Bool?, enabled: Bool) { - self.server = server - self.preset = preset - self.tested = tested - self.enabled = enabled - } - - public static func == (l: ServerCfg, r: ServerCfg) -> Bool { - l.server == r.server && l.preset == r.preset && l.tested == r.tested && l.enabled == r.enabled - } - - public var id: String { "\(server) \(createdAt)" } - - public static var empty = ServerCfg(server: "", preset: false, tested: nil, enabled: true) - - public var isEmpty: Bool { - server.trimmingCharacters(in: .whitespaces) == "" - } - - public struct SampleData { - public var preset: ServerCfg - public var custom: ServerCfg - public var untested: ServerCfg - } - - public static var sampleData = SampleData( - preset: ServerCfg( - server: "smp://abcd@smp8.simplex.im", - preset: true, - tested: true, - enabled: true - ), - custom: ServerCfg( - server: "smp://abcd@smp9.simplex.im", - preset: false, - tested: false, - enabled: false - ), - untested: ServerCfg( - server: "smp://abcd@smp10.simplex.im", - preset: false, - tested: nil, - enabled: true - ) - ) - - enum CodingKeys: CodingKey { - case server - case preset - case tested - case enabled - } -} - -public enum ProtocolTestStep: String, Decodable, Equatable { - case connect - case disconnect - case createQueue - case secureQueue - case deleteQueue - case createFile - case uploadFile - case downloadFile - case compareFile - case deleteFile - - var text: String { - switch self { - case .connect: return NSLocalizedString("Connect", comment: "server test step") - case .disconnect: return NSLocalizedString("Disconnect", comment: "server test step") - case .createQueue: return NSLocalizedString("Create queue", comment: "server test step") - case .secureQueue: return NSLocalizedString("Secure queue", comment: "server test step") - case .deleteQueue: return NSLocalizedString("Delete queue", comment: "server test step") - case .createFile: return NSLocalizedString("Create file", comment: "server test step") - case .uploadFile: return NSLocalizedString("Upload file", comment: "server test step") - case .downloadFile: return NSLocalizedString("Download file", comment: "server test step") - case .compareFile: return NSLocalizedString("Compare file", comment: "server test step") - case .deleteFile: return NSLocalizedString("Delete file", comment: "server test step") - } - } -} - -public struct ProtocolTestFailure: Decodable, Error, Equatable { - var testStep: ProtocolTestStep - var testError: AgentErrorType - - public static func == (l: ProtocolTestFailure, r: ProtocolTestFailure) -> Bool { - l.testStep == r.testStep - } - - public var localizedDescription: String { - let err = String.localizedStringWithFormat(NSLocalizedString("Test failed at step %@.", comment: "server test failure"), testStep.text) - switch testError { - case .SMP(.AUTH): - return err + " " + NSLocalizedString("Server requires authorization to create queues, check password", comment: "server test error") - case .XFTP(.AUTH): - return err + " " + NSLocalizedString("Server requires authorization to upload, check password", comment: "server test error") - case .BROKER(_, .NETWORK): - return err + " " + NSLocalizedString("Possibly, certificate fingerprint in server address is incorrect", comment: "server test error") - default: - return err - } - } -} - public struct ServerAddress: Decodable { public var serverProtocol: ServerProtocol public var hostnames: [String] @@ -1256,41 +234,56 @@ public struct ServerAddress: Decodable { public struct NetCfg: Codable, Equatable { public var socksProxy: String? = nil + var socksMode: SocksMode = .always public var hostMode: HostMode = .publicHost public var requiredHostMode = true - public var sessionMode: TransportSessionMode + public var sessionMode = TransportSessionMode.user + public var smpProxyMode: SMPProxyMode = .always + public var smpProxyFallback: SMPProxyFallback = .allowProtected + public var smpWebPortServers: SMPWebPortServers = .preset public var tcpConnectTimeout: Int // microseconds public var tcpTimeout: Int // microseconds public var tcpTimeoutPerKb: Int // microseconds - public var tcpKeepAlive: KeepAliveOpts? + public var rcvConcurrency: Int // pool size + public var tcpKeepAlive: KeepAliveOpts? = KeepAliveOpts.defaults public var smpPingInterval: Int // microseconds - public var smpPingCount: Int // times - public var logTLSErrors: Bool + public var smpPingCount: Int = 3 // times + public var logTLSErrors: Bool = false public static let defaults: NetCfg = NetCfg( - socksProxy: nil, - sessionMode: TransportSessionMode.user, - tcpConnectTimeout: 20_000_000, + tcpConnectTimeout: 25_000_000, tcpTimeout: 15_000_000, - tcpTimeoutPerKb: 45_000, - tcpKeepAlive: KeepAliveOpts.defaults, - smpPingInterval: 1200_000_000, - smpPingCount: 3, - logTLSErrors: false + tcpTimeoutPerKb: 10_000, + rcvConcurrency: 12, + smpPingInterval: 1200_000_000 ) - public static let proxyDefaults: NetCfg = NetCfg( - socksProxy: nil, - sessionMode: TransportSessionMode.user, - tcpConnectTimeout: 30_000_000, + static let proxyDefaults: NetCfg = NetCfg( + tcpConnectTimeout: 35_000_000, tcpTimeout: 20_000_000, - tcpTimeoutPerKb: 60_000, - tcpKeepAlive: KeepAliveOpts.defaults, - smpPingInterval: 1200_000_000, - smpPingCount: 3, - logTLSErrors: false + tcpTimeoutPerKb: 15_000, + rcvConcurrency: 8, + smpPingInterval: 1200_000_000 ) + public var withProxyTimeouts: NetCfg { + var cfg = self + cfg.tcpConnectTimeout = NetCfg.proxyDefaults.tcpConnectTimeout + cfg.tcpTimeout = NetCfg.proxyDefaults.tcpTimeout + cfg.tcpTimeoutPerKb = NetCfg.proxyDefaults.tcpTimeoutPerKb + cfg.rcvConcurrency = NetCfg.proxyDefaults.rcvConcurrency + cfg.smpPingInterval = NetCfg.proxyDefaults.smpPingInterval + return cfg + } + + public var hasProxyTimeouts: Bool { + tcpConnectTimeout == NetCfg.proxyDefaults.tcpConnectTimeout && + tcpTimeout == NetCfg.proxyDefaults.tcpTimeout && + tcpTimeoutPerKb == NetCfg.proxyDefaults.tcpTimeoutPerKb && + rcvConcurrency == NetCfg.proxyDefaults.rcvConcurrency && + smpPingInterval == NetCfg.proxyDefaults.smpPingInterval + } + public var enableKeepAlive: Bool { tcpKeepAlive != nil } } @@ -1300,6 +293,63 @@ public enum HostMode: String, Codable { case publicHost = "public" } +public enum SocksMode: String, Codable { + case always = "always" + case onion = "onion" +} + +public enum SMPProxyMode: String, Codable, SelectableItem { + case always = "always" + case unknown = "unknown" + case unprotected = "unprotected" + case never = "never" + + public var label: LocalizedStringKey { + switch self { + case .always: return "always" + case .unknown: return "unknown servers" + case .unprotected: return "unprotected" + case .never: return "never" + } + } + + public var id: SMPProxyMode { self } + + public static let values: [SMPProxyMode] = [.always, .unknown, .unprotected, .never] +} + +public enum SMPProxyFallback: String, Codable, SelectableItem { + case allow = "allow" + case allowProtected = "allowProtected" + case prohibit = "prohibit" + + public var label: LocalizedStringKey { + switch self { + case .allow: return "yes" + case .allowProtected: return "when IP hidden" + case .prohibit: return "no" + } + } + + public var id: SMPProxyFallback { self } + + public static let values: [SMPProxyFallback] = [.allow, .allowProtected, .prohibit] +} + +public enum SMPWebPortServers: String, Codable, CaseIterable { + case all = "all" + case preset = "preset" + case off = "off" + + public var text: LocalizedStringKey { + switch self { + case .all: "All servers" + case .preset: "Preset servers" + case .off: "Off" + } + } +} + public enum OnionHosts: String, Identifiable { case no case prefer @@ -1336,18 +386,22 @@ public enum OnionHosts: String, Identifiable { public enum TransportSessionMode: String, Codable, Identifiable { case user + case session + case server case entity public var text: LocalizedStringKey { switch self { - case .user: return "User profile" + case .user: return "Chat profile" + case .session: return "App session" + case .server: return "Server" case .entity: return "Connection" } } public var id: TransportSessionMode { self } - public static let values: [TransportSessionMode] = [.user, .entity] + public static let values: [TransportSessionMode] = [.user, .session, .server, .entity] } public struct KeepAliveOpts: Codable, Equatable { @@ -1358,50 +412,64 @@ public struct KeepAliveOpts: Codable, Equatable { public static let defaults: KeepAliveOpts = KeepAliveOpts(keepIdle: 30, keepIntvl: 15, keepCnt: 4) } -public enum NetworkStatus: Decodable, Equatable { - case unknown - case connected - case disconnected - case error(connectionError: String) +public struct NetworkProxy: Equatable, Codable { + public var host: String = "" + public var port: Int = 0 + public var auth: NetworkProxyAuth = .username + public var username: String = "" + public var password: String = "" - public var statusString: LocalizedStringKey { - get { - switch self { - case .connected: return "connected" - case .error: return "error" - default: return "connecting" - } - } + public static var def: NetworkProxy { + NetworkProxy() } - public var statusExplanation: LocalizedStringKey { - get { - switch self { - case .connected: return "You are connected to the server used to receive messages from this contact." - case let .error(err): return "Trying to connect to the server used to receive messages from this contact (error: \(err))." - default: return "Trying to connect to the server used to receive messages from this contact." - } + public var valid: Bool { + let hostOk = switch NWEndpoint.Host(host) { + case .ipv4: true + case .ipv6: true + default: false } + return hostOk && + port > 0 && port <= 65535 && + NetworkProxy.validCredential(username) && NetworkProxy.validCredential(password) } - public var imageName: String { - get { - switch self { - case .unknown: return "circle.dotted" - case .connected: return "circle.fill" - case .disconnected: return "ellipsis.circle.fill" - case .error: return "exclamationmark.circle.fill" + public static func validCredential(_ s: String) -> Bool { + !s.contains(":") && !s.contains("@") + } + + public func toProxyString() -> String? { + if !valid { return nil } + var res = "" + switch auth { + case .username: + let usernameTrimmed = username.trimmingCharacters(in: .whitespaces) + let passwordTrimmed = password.trimmingCharacters(in: .whitespaces) + if usernameTrimmed != "" || passwordTrimmed != "" { + res += usernameTrimmed + ":" + passwordTrimmed + "@" + } else { + res += "@" + } + case .isolate: () + } + if host != "" { + if host.contains(":") { + res += "[\(host.trimmingCharacters(in: [" ", "[", "]"]))]" + } else { + res += host.trimmingCharacters(in: .whitespaces) } } + res += ":\(port)" + return res } } -public struct ConnNetworkStatus: Decodable { - public var agentConnId: String - public var networkStatus: NetworkStatus +public enum NetworkProxyAuth: String, Codable { + case username + case isolate } -public struct ChatSettings: Codable { +public struct ChatSettings: Codable, Hashable { public var enableNtfs: MsgFilter public var sendRcpts: Bool? public var favorite: Bool @@ -1415,23 +483,58 @@ public struct ChatSettings: Codable { public static let defaults: ChatSettings = ChatSettings(enableNtfs: .all, sendRcpts: nil, favorite: false) } -public enum MsgFilter: String, Codable { - case none - case all - case mentions -} +public struct NavigationInfo: Decodable { + public var afterUnread: Int = 0 + public var afterTotal: Int = 0 -public struct UserMsgReceiptSettings: Codable { - public var enable: Bool - public var clearOverrides: Bool - - public init(enable: Bool, clearOverrides: Bool) { - self.enable = enable - self.clearOverrides = clearOverrides + public init(afterUnread: Int = 0, afterTotal: Int = 0) { + self.afterUnread = afterUnread + self.afterTotal = afterTotal } } -public struct ConnectionStats: Decodable { +public enum MsgFilter: String, Codable, Hashable { + case none + case all + case mentions + + public func nextMode(mentions: Bool) -> MsgFilter { + switch self { + case .all: mentions ? .mentions : .none + case .mentions: .none + case .none: .all + } + } + + public func text(mentions: Bool) -> String { + switch self { + case .all: NSLocalizedString("Unmute", comment: "notification label action") + case .mentions: NSLocalizedString("Mute", comment: "notification label action") + case .none: + mentions + ? NSLocalizedString("Mute all", comment: "notification label action") + : NSLocalizedString("Mute", comment: "notification label action") + } + } + + public var icon: String { + return switch self { + case .all: "speaker.wave.2" + case .mentions: "speaker.badge.exclamationmark" + case .none: "speaker.slash" + } + } + + public var iconFilled: String { + return switch self { + case .all: "speaker.wave.2.fill" + case .mentions: "speaker.badge.exclamationmark.fill" + case .none: "speaker.slash.fill" + } + } +} + +public struct ConnectionStats: Decodable, Hashable { public var connAgentVersion: Int public var rcvQueuesInfo: [RcvQueueInfo] public var sndQueuesInfo: [SndQueueInfo] @@ -1445,27 +548,31 @@ public struct ConnectionStats: Decodable { public var ratchetSyncSendProhibited: Bool { [.required, .started, .agreed].contains(ratchetSyncState) } + + public var ratchetSyncInProgress: Bool { + [.started, .agreed].contains(ratchetSyncState) + } } -public struct RcvQueueInfo: Codable { +public struct RcvQueueInfo: Codable, Hashable { public var rcvServer: String public var rcvSwitchStatus: RcvSwitchStatus? public var canAbortSwitch: Bool } -public enum RcvSwitchStatus: String, Codable { +public enum RcvSwitchStatus: String, Codable, Hashable { case switchStarted = "switch_started" case sendingQADD = "sending_qadd" case sendingQUSE = "sending_quse" case receivedMessage = "received_message" } -public struct SndQueueInfo: Codable { +public struct SndQueueInfo: Codable, Hashable { public var sndServer: String public var sndSwitchStatus: SndSwitchStatus? } -public enum SndSwitchStatus: String, Codable { +public enum SndSwitchStatus: String, Codable, Hashable { case sendingQKEY = "sending_qkey" case sendingQTEST = "sending_qtest" } @@ -1494,93 +601,11 @@ public enum RatchetSyncState: String, Decodable { case agreed } -public struct UserContactLink: Decodable { - public var connReqContact: String - public var autoAccept: AutoAccept? - - public init(connReqContact: String, autoAccept: AutoAccept? = nil) { - self.connReqContact = connReqContact - self.autoAccept = autoAccept - } - - var responseDetails: String { - "connReqContact: \(connReqContact)\nautoAccept: \(AutoAccept.cmdString(autoAccept))" - } -} - -public struct AutoAccept: Codable { - public var acceptIncognito: Bool - public var autoReply: MsgContent? - - public init(acceptIncognito: Bool, autoReply: MsgContent? = nil) { - self.acceptIncognito = acceptIncognito - self.autoReply = autoReply - } - - static func cmdString(_ autoAccept: AutoAccept?) -> String { - guard let autoAccept = autoAccept else { return "off" } - let s = "on" + (autoAccept.acceptIncognito ? " incognito=on" : "") - guard let msg = autoAccept.autoReply else { return s } - return s + " " + msg.cmdString - } -} - -public protocol SelectableItem: Hashable, Identifiable { +public protocol SelectableItem: Identifiable, Equatable { var label: LocalizedStringKey { get } static var values: [Self] { get } } -public struct DeviceToken: Decodable { - var pushProvider: PushProvider - var token: String - - public init(pushProvider: PushProvider, token: String) { - self.pushProvider = pushProvider - self.token = token - } - - public var cmdString: String { - "\(pushProvider) \(token)" - } -} - -public enum PushEnvironment: String { - case development - case production -} - -public enum PushProvider: String, Decodable { - case apns_dev - case apns_prod - - public init(env: PushEnvironment) { - switch env { - case .development: self = .apns_dev - case .production: self = .apns_prod - } - } -} - -// This notification mode is for app core, UI uses AppNotificationsMode.off to mean completely disable, -// and .local for periodic background checks -public enum NotificationsMode: String, Decodable, SelectableItem { - case off = "OFF" - case periodic = "PERIODIC" - case instant = "INSTANT" - - public var label: LocalizedStringKey { - switch self { - case .off: "Local" - case .periodic: "Periodically" - case .instant: "Instantly" - } - } - - public var id: String { self.rawValue } - - public static var values: [NotificationsMode] = [.instant, .periodic, .off] -} - public enum NotificationPreviewMode: String, SelectableItem, Codable { case hidden case contact @@ -1599,47 +624,6 @@ public enum NotificationPreviewMode: String, SelectableItem, Codable { public static var values: [NotificationPreviewMode] = [.message, .contact, .hidden] } -public struct RemoteCtrlInfo: Decodable { - public var remoteCtrlId: Int64 - public var ctrlDeviceName: String - public var sessionState: RemoteCtrlSessionState? - - public var deviceViewName: String { - ctrlDeviceName == "" ? "\(remoteCtrlId)" : ctrlDeviceName - } -} - -public enum RemoteCtrlSessionState: Decodable { - case starting - case searching - case connecting - case pendingConfirmation(sessionCode: String) - case connected(sessionCode: String) -} - -public enum RemoteCtrlStopReason: Decodable { - case discoveryFailed(chatError: ChatError) - case connectionFailed(chatError: ChatError) - case setupFailed(chatError: ChatError) - case disconnected -} - -public struct CtrlAppInfo: Decodable { - public var appVersionRange: AppVersionRange - public var deviceName: String -} - -public struct AppVersionRange: Decodable { - public var minVersion: String - public var maxVersion: String -} - -public struct CoreVersionInfo: Decodable { - public var version: String - public var simplexmqVersion: String - public var simplexmqCommit: String -} - public func decodeJSON(_ json: String) -> T? { if let data = json.data(using: .utf8) { return try? jsonDecoder.decode(T.self, from: data) @@ -1656,16 +640,29 @@ private func encodeCJSON(_ value: T) -> [CChar] { encodeJSON(value).cString(using: .utf8)! } -public enum ChatError: Decodable { +public enum ChatError: Decodable, Hashable, Error { case error(errorType: ChatErrorType) case errorAgent(agentError: AgentErrorType) case errorStore(storeError: StoreError) case errorDatabase(databaseError: DatabaseError) case errorRemoteCtrl(remoteCtrlError: RemoteCtrlError) - case invalidJSON(json: String) + case invalidJSON(json: Data?) // additional case used to pass errors that failed to parse + case unexpectedResult(type: String) // additional case used to pass unexpected responses + + public var errorType: String { + switch self { + case .error: "chat" + case .errorAgent: "agent" + case .errorStore: "store" + case .errorDatabase: "database" + case .errorRemoteCtrl: "remoteCtrl" + case .invalidJSON: "invalid" + case let .unexpectedResult(type): "! \(type)" + } + } } -public enum ChatErrorType: Decodable { +public enum ChatErrorType: Decodable, Hashable { case noActiveUser case noConnectionUser(agentConnId: String) case noSndFileUser(agentSndFileId: String) @@ -1685,8 +682,8 @@ public enum ChatErrorType: Decodable { case chatNotStarted case chatNotStopped case chatStoreChanged - case connectionPlan(connectionPlan: ConnectionPlan) case invalidConnReq + case unsupportedConnReq case invalidChatMessage(connection: Connection, message: String) case contactNotReady(contact: Contact) case contactNotActive(contact: Contact) @@ -1703,7 +700,6 @@ public enum ChatErrorType: Decodable { case groupMemberNotActive case groupMemberUserRemoved case groupMemberNotFound - case groupMemberIntroNotFound(contactName: ContactName) case groupCantResendInvitation(groupInfo: GroupInfo, contactName: ContactName) case groupInternal(message: String) case fileNotFound(message: String) @@ -1720,11 +716,11 @@ public enum ChatErrorType: Decodable { case fileImageType(filePath: String) case fileImageSize(filePath: String) case fileNotReceived(fileId: Int64) - // case xFTPRcvFile - // case xFTPSndFile + case fileNotApproved(fileId: Int64, unknownServers: [String]) case fallbackToSMPProhibited(fileId: Int64) case inlineFileProhibited(fileId: Int64) case invalidQuote + case invalidForward case invalidChatItemUpdate case invalidChatItemDelete case hasCurrentCall @@ -1739,12 +735,13 @@ public enum ChatErrorType: Decodable { case agentCommandError(message: String) case invalidFileDescription(message: String) case connectionIncognitoChangeProhibited + case connectionUserChangeProhibited case peerChatVRangeIncompatible case internalError(message: String) case exception(message: String) } -public enum StoreError: Decodable { +public enum StoreError: Decodable, Hashable { case duplicateName case userNotFound(userId: Int64) case userNotFoundByName(contactName: ContactName) @@ -1802,9 +799,10 @@ public enum StoreError: Decodable { case hostMemberIdNotFound(groupId: Int64) case contactNotFoundByFileId(fileId: Int64) case noGroupSndStatus(itemId: Int64, groupMemberId: Int64) + case dBException(message: String) } -public enum DatabaseError: Decodable { +public enum DatabaseError: Decodable, Hashable { case errorEncrypted case errorPlaintext case errorNoFile(dbFile: String) @@ -1812,25 +810,27 @@ public enum DatabaseError: Decodable { case errorOpen(sqliteError: SQLiteError) } -public enum SQLiteError: Decodable { +public enum SQLiteError: Decodable, Hashable { case errorNotADatabase - case error(String) + case error(dbError: String) } -public enum AgentErrorType: Decodable { - case CMD(cmdErr: CommandErrorType) +public enum AgentErrorType: Decodable, Hashable { + case CMD(cmdErr: CommandErrorType, errContext: String) case CONN(connErr: ConnectionErrorType) - case SMP(smpErr: ProtocolErrorType) + case SMP(serverAddress: String, smpErr: ProtocolErrorType) case NTF(ntfErr: ProtocolErrorType) case XFTP(xftpErr: XFTPErrorType) + case PROXY(proxyServer: String, relayServer: String, proxyErr: ProxyClientError) case RCP(rcpErr: RCErrorType) case BROKER(brokerAddress: String, brokerErr: BrokerErrorType) case AGENT(agentErr: SMPAgentError) case INTERNAL(internalErr: String) + case CRITICAL(offerRestart: Bool, criticalErr: String) case INACTIVE } -public enum CommandErrorType: Decodable { +public enum CommandErrorType: Decodable, Hashable { case PROHIBITED case SYNTAX case NO_CONN @@ -1838,7 +838,7 @@ public enum CommandErrorType: Decodable { case LARGE } -public enum ConnectionErrorType: Decodable { +public enum ConnectionErrorType: Decodable, Hashable { case NOT_FOUND case DUPLICATE case SIMPLEX @@ -1846,7 +846,7 @@ public enum ConnectionErrorType: Decodable { case NOT_AVAILABLE } -public enum BrokerErrorType: Decodable { +public enum BrokerErrorType: Decodable, Hashable { case RESPONSE(smpErr: String) case UNEXPECTED case NETWORK @@ -1855,22 +855,51 @@ public enum BrokerErrorType: Decodable { case TIMEOUT } -public enum ProtocolErrorType: Decodable { +public enum ProtocolErrorType: Decodable, Hashable { case BLOCK case SESSION case CMD(cmdErr: ProtocolCommandError) + indirect case PROXY(proxyErr: ProxyError) case AUTH + case BLOCKED(blockInfo: BlockingInfo) + case CRYPTO case QUOTA + case STORE(storeErr: String) case NO_MSG case LARGE_MSG + case EXPIRED case INTERNAL } -public enum XFTPErrorType: Decodable { +public enum ProxyError: Decodable, Hashable { + case PROTOCOL(protocolErr: ProtocolErrorType) + case BROKER(brokerErr: BrokerErrorType) + case BASIC_AUTH + case NO_SESSION +} + +public struct BlockingInfo: Decodable, Equatable, Hashable { + public var reason: BlockingReason +} + +public enum BlockingReason: String, Decodable { + case spam + case content + + public var text: String { + switch self { + case .spam: NSLocalizedString("Spam", comment: "blocking reason") + case .content: NSLocalizedString("Content violates conditions of use", comment: "blocking reason") + } + } +} + +public enum XFTPErrorType: Decodable, Hashable { case BLOCK case SESSION case CMD(cmdErr: ProtocolCommandError) case AUTH + case BLOCKED(blockInfo: BlockingInfo) case SIZE case QUOTA case DIGEST @@ -1878,13 +907,23 @@ public enum XFTPErrorType: Decodable { case NO_FILE case HAS_FILE case FILE_IO + case TIMEOUT + case REDIRECT(redirectError: String) case INTERNAL } -public enum RCErrorType: Decodable { +public enum ProxyClientError: Decodable, Hashable { + case protocolError(protocolErr: ProtocolErrorType) + case unexpectedResponse(responseStr: String) + case responseError(responseErr: ProtocolErrorType) +} + +public enum RCErrorType: Decodable, Hashable { case `internal`(internalErr: String) case identity case noLocalAddress + case newController + case notDiscovered case tlsStartFailed case exception(exception: String) case ctrlAuth @@ -1897,7 +936,7 @@ public enum RCErrorType: Decodable { case syntax(syntaxErr: String) } -public enum ProtocolCommandError: Decodable { +public enum ProtocolCommandError: Decodable, Hashable { case UNKNOWN case SYNTAX case PROHIBITED @@ -1906,20 +945,23 @@ public enum ProtocolCommandError: Decodable { case NO_ENTITY } -public enum ProtocolTransportError: Decodable { +public enum ProtocolTransportError: Decodable, Hashable { case badBlock + case version case largeMsg case badSession + case noServerAuth case handshake(handshakeErr: SMPHandshakeError) } -public enum SMPHandshakeError: Decodable { +public enum SMPHandshakeError: Decodable, Hashable { case PARSE case VERSION case IDENTITY + case BAD_AUTH } -public enum SMPAgentError: Decodable { +public enum SMPAgentError: Decodable, Hashable { case A_MESSAGE case A_PROHIBITED case A_VERSION @@ -1928,162 +970,33 @@ public enum SMPAgentError: Decodable { case A_QUEUE(queueErr: String) } -public enum ArchiveError: Decodable { - case `import`(chatError: ChatError) - case importFile(file: String, chatError: ChatError) +public enum ArchiveError: Decodable, Hashable { + case `import`(importError: String) + case fileError(file: String, fileError: String) } -public enum RemoteCtrlError: Decodable { +public enum RemoteCtrlError: Decodable, Hashable { case inactive case badState case busy case timeout + case noKnownControllers + case badController case disconnected(remoteCtrlId: Int64, reason: String) case badInvitation case badVersion(appVersion: String) -// case protocolError(protocolError: RemoteProtocolError) + case hTTP2Error(http2Error: String) + case protocolError } -public struct MigrationFileLinkData: Codable { - let networkConfig: NetworkConfig? - - public init(networkConfig: NetworkConfig) { - self.networkConfig = networkConfig - } - - public struct NetworkConfig: Codable { - let socksProxy: String? - let hostMode: HostMode? - let requiredHostMode: Bool? - - public init(socksProxy: String?, hostMode: HostMode?, requiredHostMode: Bool?) { - self.socksProxy = socksProxy - self.hostMode = hostMode - self.requiredHostMode = requiredHostMode - } - - public func transformToPlatformSupported() -> NetworkConfig { - return if let hostMode, let requiredHostMode { - NetworkConfig( - socksProxy: nil, - hostMode: hostMode == .onionViaSocks ? .onionHost : hostMode, - requiredHostMode: requiredHostMode - ) - } else { self } - } - } - - public func addToLink(link: String) -> String { - "\(link)&data=\(encodeJSON(self).addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!)" - } - - public static func readFromLink(link: String) -> MigrationFileLinkData? { -// standaloneFileInfo(link) - nil +public struct AppFilePaths: Encodable { + public let appFilesFolder: String + public let appTempFolder: String + public let appAssetsFolder: String + + public init(appFilesFolder: String, appTempFolder: String, appAssetsFolder: String) { + self.appFilesFolder = appFilesFolder + self.appTempFolder = appTempFolder + self.appAssetsFolder = appAssetsFolder } } - -public struct AppSettings: Codable, Equatable { - public var networkConfig: NetCfg? = nil - public var privacyEncryptLocalFiles: Bool? = nil - public var privacyAcceptImages: Bool? = nil - public var privacyLinkPreviews: Bool? = nil - public var privacyShowChatPreviews: Bool? = nil - public var privacySaveLastDraft: Bool? = nil - public var privacyProtectScreen: Bool? = nil - public var notificationMode: AppSettingsNotificationMode? = nil - public var notificationPreviewMode: NotificationPreviewMode? = nil - public var webrtcPolicyRelay: Bool? = nil - public var webrtcICEServers: [String]? = nil - public var confirmRemoteSessions: Bool? = nil - public var connectRemoteViaMulticast: Bool? = nil - public var connectRemoteViaMulticastAuto: Bool? = nil - public var developerTools: Bool? = nil - public var confirmDBUpgrades: Bool? = nil - public var androidCallOnLockScreen: AppSettingsLockScreenCalls? = nil - public var iosCallKitEnabled: Bool? = nil - public var iosCallKitCallsInRecents: Bool? = nil - - public func prepareForExport() -> AppSettings { - var empty = AppSettings() - let def = AppSettings.defaults - if networkConfig != def.networkConfig { empty.networkConfig = networkConfig } - if privacyEncryptLocalFiles != def.privacyEncryptLocalFiles { empty.privacyEncryptLocalFiles = privacyEncryptLocalFiles } - if privacyAcceptImages != def.privacyAcceptImages { empty.privacyAcceptImages = privacyAcceptImages } - if privacyLinkPreviews != def.privacyLinkPreviews { empty.privacyLinkPreviews = privacyLinkPreviews } - if privacyShowChatPreviews != def.privacyShowChatPreviews { empty.privacyShowChatPreviews = privacyShowChatPreviews } - if privacySaveLastDraft != def.privacySaveLastDraft { empty.privacySaveLastDraft = privacySaveLastDraft } - if privacyProtectScreen != def.privacyProtectScreen { empty.privacyProtectScreen = privacyProtectScreen } - if notificationMode != def.notificationMode { empty.notificationMode = notificationMode } - if notificationPreviewMode != def.notificationPreviewMode { empty.notificationPreviewMode = notificationPreviewMode } - if webrtcPolicyRelay != def.webrtcPolicyRelay { empty.webrtcPolicyRelay = webrtcPolicyRelay } - if webrtcICEServers != def.webrtcICEServers { empty.webrtcICEServers = webrtcICEServers } - if confirmRemoteSessions != def.confirmRemoteSessions { empty.confirmRemoteSessions = confirmRemoteSessions } - if connectRemoteViaMulticast != def.connectRemoteViaMulticast {empty.connectRemoteViaMulticast = connectRemoteViaMulticast } - if connectRemoteViaMulticastAuto != def.connectRemoteViaMulticastAuto { empty.connectRemoteViaMulticastAuto = connectRemoteViaMulticastAuto } - if developerTools != def.developerTools { empty.developerTools = developerTools } - if confirmDBUpgrades != def.confirmDBUpgrades { empty.confirmDBUpgrades = confirmDBUpgrades } - if androidCallOnLockScreen != def.androidCallOnLockScreen { empty.androidCallOnLockScreen = androidCallOnLockScreen } - if iosCallKitEnabled != def.iosCallKitEnabled { empty.iosCallKitEnabled = iosCallKitEnabled } - if iosCallKitCallsInRecents != def.iosCallKitCallsInRecents { empty.iosCallKitCallsInRecents = iosCallKitCallsInRecents } - return empty - } - - public static var defaults: AppSettings { - AppSettings ( - networkConfig: NetCfg.defaults, - privacyEncryptLocalFiles: true, - privacyAcceptImages: true, - privacyLinkPreviews: true, - privacyShowChatPreviews: true, - privacySaveLastDraft: true, - privacyProtectScreen: false, - notificationMode: AppSettingsNotificationMode.instant, - notificationPreviewMode: NotificationPreviewMode.message, - webrtcPolicyRelay: true, - webrtcICEServers: [], - confirmRemoteSessions: false, - connectRemoteViaMulticast: true, - connectRemoteViaMulticastAuto: true, - developerTools: false, - confirmDBUpgrades: false, - androidCallOnLockScreen: AppSettingsLockScreenCalls.show, - iosCallKitEnabled: true, - iosCallKitCallsInRecents: false - ) - } -} - -public enum AppSettingsNotificationMode: String, Codable { - case off - case periodic - case instant - - public func toNotificationsMode() -> NotificationsMode { - switch self { - case .instant: .instant - case .periodic: .periodic - case .off: .off - } - } - - public static func from(_ mode: NotificationsMode) -> AppSettingsNotificationMode { - switch mode { - case .instant: .instant - case .periodic: .periodic - case .off: .off - } - } -} - -//public enum NotificationPreviewMode: Codable { -// case hidden -// case contact -// case message -//} - -public enum AppSettingsLockScreenCalls: String, Codable { - case disable - case show - case accept -} diff --git a/apps/ios/SimpleXChat/AppGroup.swift b/apps/ios/SimpleXChat/AppGroup.swift index 4fbe78dc7a..29ccab7357 100644 --- a/apps/ios/SimpleXChat/AppGroup.swift +++ b/apps/ios/SimpleXChat/AppGroup.swift @@ -11,24 +11,40 @@ import SwiftUI public let appSuspendTimeout: Int = 15 // seconds +public let defaultProfileImageCorner: Double = 22.5 + let GROUP_DEFAULT_APP_STATE = "appState" let GROUP_DEFAULT_NSE_STATE = "nseState" +let GROUP_DEFAULT_SE_STATE = "seState" let GROUP_DEFAULT_DB_CONTAINER = "dbContainer" public let GROUP_DEFAULT_CHAT_LAST_START = "chatLastStart" public let GROUP_DEFAULT_CHAT_LAST_BACKGROUND_RUN = "chatLastBackgroundRun" let GROUP_DEFAULT_NTF_PREVIEW_MODE = "ntfPreviewMode" public let GROUP_DEFAULT_NTF_ENABLE_LOCAL = "ntfEnableLocal" // no longer used public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic" // no longer used +// replaces DEFAULT_PERFORM_LA +let GROUP_DEFAULT_APP_LOCAL_AUTH_ENABLED = "appLocalAuthEnabled" +public let GROUP_DEFAULT_ALLOW_SHARE_EXTENSION = "allowShareExtension" +// replaces DEFAULT_PRIVACY_LINK_PREVIEWS +let GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS = "privacyLinkPreviews" // This setting is a main one, while having an unused duplicate from the past: DEFAULT_PRIVACY_ACCEPT_IMAGES let GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages" public let GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE = "privacyTransferImagesInline" // no longer used public let GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES = "privacyEncryptLocalFiles" +public let GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS = "privacyAskToApproveRelays" +// replaces DEFAULT_PROFILE_IMAGE_CORNER_RADIUS +public let GROUP_DEFAULT_PROFILE_IMAGE_CORNER_RADIUS = "profileImageCornerRadius" let GROUP_DEFAULT_NTF_BADGE_COUNT = "ntgBadgeCount" +public let GROUP_DEFAULT_NETWORK_SOCKS_PROXY = "networkSocksProxy" let GROUP_DEFAULT_NETWORK_USE_ONION_HOSTS = "networkUseOnionHosts" let GROUP_DEFAULT_NETWORK_SESSION_MODE = "networkSessionMode" +let GROUP_DEFAULT_NETWORK_SMP_PROXY_MODE = "networkSMPProxyMode" +let GROUP_DEFAULT_NETWORK_SMP_PROXY_FALLBACK = "networkSMPProxyFallback" +let GROUP_DEFAULT_NETWORK_SMP_WEB_PORT_SERVERS = "networkSMPWebPortServers" let GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT = "networkTCPConnectTimeout" let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT = "networkTCPTimeout" let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB = "networkTCPTimeoutPerKb" +let GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY = "networkRcvConcurrency" let GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL = "networkSMPPingInterval" let GROUP_DEFAULT_NETWORK_SMP_PING_COUNT = "networkSMPPingCount" let GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE = "networkEnableKeepAlive" @@ -40,7 +56,9 @@ let GROUP_DEFAULT_STORE_DB_PASSPHRASE = "storeDBPassphrase" public let GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE = "initialRandomDBPassphrase" public let GROUP_DEFAULT_CONFIRM_DB_UPGRADES = "confirmDBUpgrades" public let GROUP_DEFAULT_CALL_KIT_ENABLED = "callKitEnabled" -public let GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED = "pqExperimentalEnabled" +public let GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED = "pqExperimentalEnabled" // no longer used +public let GROUP_DEFAULT_ONE_HAND_UI = "oneHandUI" +public let GROUP_DEFAULT_CHAT_BOTTOM_BAR = "chatBottomBar" public let APP_GROUP_NAME = "group.chat.simplex.app" @@ -51,10 +69,14 @@ public func registerGroupDefaults() { GROUP_DEFAULT_NTF_ENABLE_LOCAL: false, GROUP_DEFAULT_NTF_ENABLE_PERIODIC: false, GROUP_DEFAULT_NETWORK_USE_ONION_HOSTS: OnionHosts.no.rawValue, - GROUP_DEFAULT_NETWORK_SESSION_MODE: TransportSessionMode.user.rawValue, + GROUP_DEFAULT_NETWORK_SESSION_MODE: TransportSessionMode.session.rawValue, + GROUP_DEFAULT_NETWORK_SMP_PROXY_MODE: SMPProxyMode.unknown.rawValue, + GROUP_DEFAULT_NETWORK_SMP_PROXY_FALLBACK: SMPProxyFallback.allowProtected.rawValue, + GROUP_DEFAULT_NETWORK_SMP_WEB_PORT_SERVERS: SMPWebPortServers.preset.rawValue, GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT: NetCfg.defaults.tcpConnectTimeout, GROUP_DEFAULT_NETWORK_TCP_TIMEOUT: NetCfg.defaults.tcpTimeout, GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB: NetCfg.defaults.tcpTimeoutPerKb, + GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY: NetCfg.defaults.rcvConcurrency, GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL: NetCfg.defaults.smpPingInterval, GROUP_DEFAULT_NETWORK_SMP_PING_COUNT: NetCfg.defaults.smpPingCount, GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE: NetCfg.defaults.enableKeepAlive, @@ -64,12 +86,19 @@ public func registerGroupDefaults() { GROUP_DEFAULT_INCOGNITO: false, GROUP_DEFAULT_STORE_DB_PASSPHRASE: true, GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE: false, + GROUP_DEFAULT_APP_LOCAL_AUTH_ENABLED: true, + GROUP_DEFAULT_ALLOW_SHARE_EXTENSION: false, + GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS: true, GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES: true, GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE: false, GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES: true, + GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS: true, + GROUP_DEFAULT_PROFILE_IMAGE_CORNER_RADIUS: defaultProfileImageCorner, GROUP_DEFAULT_CONFIRM_DB_UPGRADES: false, GROUP_DEFAULT_CALL_KIT_ENABLED: true, GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED: false, + GROUP_DEFAULT_ONE_HAND_UI: true, + GROUP_DEFAULT_CHAT_BOTTOM_BAR: true ]) } @@ -128,6 +157,11 @@ public enum NSEState: String, Codable { } } +public enum SEState: String, Codable { + case inactive + case sendingMessage +} + public enum DBContainer: String { case documents case group @@ -147,6 +181,12 @@ public let nseStateGroupDefault = EnumDefault( withDefault: .suspended // so that NSE that was never launched does not delay the app from resuming ) +public let seStateGroupDefault = EnumDefault( + defaults: groupDefaults, + forKey: GROUP_DEFAULT_SE_STATE, + withDefault: .inactive +) + // inactive app states do not include "stopped" state public func allowBackgroundRefresh() -> Bool { appStateGroupDefault.get().inactive && nseStateGroupDefault.get().inactive @@ -170,11 +210,21 @@ public let ntfPreviewModeGroupDefault = EnumDefault( public let incognitoGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_INCOGNITO) +public let appLocalAuthEnabledGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_APP_LOCAL_AUTH_ENABLED) + +public let allowShareExtensionGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_ALLOW_SHARE_EXTENSION) + +public let privacyLinkPreviewsGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS) + // This setting is a main one, while having an unused duplicate from the past: DEFAULT_PRIVACY_ACCEPT_IMAGES public let privacyAcceptImagesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES) public let privacyEncryptLocalFilesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES) +public let privacyAskToApproveRelaysGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS) + +public let profileImageCornerRadiusGroupDefault = Default(defaults: groupDefaults, forKey: GROUP_DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) + public let ntfBadgeCountGroupDefault = IntDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_NTF_BADGE_COUNT) public let networkUseOnionHostsGroupDefault = EnumDefault( @@ -186,7 +236,25 @@ public let networkUseOnionHostsGroupDefault = EnumDefault( public let networkSessionModeGroupDefault = EnumDefault( defaults: groupDefaults, forKey: GROUP_DEFAULT_NETWORK_SESSION_MODE, - withDefault: .user + withDefault: .session +) + +public let networkSMPProxyModeGroupDefault = EnumDefault( + defaults: groupDefaults, + forKey: GROUP_DEFAULT_NETWORK_SMP_PROXY_MODE, + withDefault: .unknown +) + +public let networkSMPProxyFallbackGroupDefault = EnumDefault( + defaults: groupDefaults, + forKey: GROUP_DEFAULT_NETWORK_SMP_PROXY_FALLBACK, + withDefault: .allowProtected +) + +public let networkSMPWebPortServersDefault = EnumDefault( + defaults: groupDefaults, + forKey: GROUP_DEFAULT_NETWORK_SMP_WEB_PORT_SERVERS, + withDefault: .preset ) public let storeDBPassphraseGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_STORE_DB_PASSPHRASE) @@ -197,8 +265,6 @@ public let confirmDBUpgradesGroupDefault = BoolDefault(defaults: groupDefaults, public let callKitEnabledGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CALL_KIT_ENABLED) -public let pqExperimentalEnabledDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES) - public class DateDefault { var defaults: UserDefaults var key: String @@ -245,12 +311,14 @@ public class EnumDefault where T.RawValue == String { } public class BoolDefault: Default { + @inline(__always) public func get() -> Bool { self.defaults.bool(forKey: self.key) } } public class IntDefault: Default { + @inline(__always) public func get() -> Int { self.defaults.integer(forKey: self.key) } @@ -260,11 +328,13 @@ public class Default { var defaults: UserDefaults var key: String + @inline(__always) public init(defaults: UserDefaults = UserDefaults.standard, forKey: String) { self.defaults = defaults self.key = forKey } + @inline(__always) public func set(_ value: T) { defaults.set(value, forKey: key) defaults.synchronize() @@ -272,12 +342,17 @@ public class Default { } public func getNetCfg() -> NetCfg { + let socksProxy = groupDefaults.string(forKey: GROUP_DEFAULT_NETWORK_SOCKS_PROXY) let onionHosts = networkUseOnionHostsGroupDefault.get() let (hostMode, requiredHostMode) = onionHosts.hostMode let sessionMode = networkSessionModeGroupDefault.get() + let smpProxyMode = networkSMPProxyModeGroupDefault.get() + let smpProxyFallback = networkSMPProxyFallbackGroupDefault.get() + let smpWebPortServers = networkSMPWebPortServersDefault.get() let tcpConnectTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT) let tcpTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT) let tcpTimeoutPerKb = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB) + let rcvConcurrency = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY) let smpPingInterval = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL) let smpPingCount = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT) let enableKeepAlive = groupDefaults.bool(forKey: GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE) @@ -291,12 +366,17 @@ public func getNetCfg() -> NetCfg { tcpKeepAlive = nil } return NetCfg( + socksProxy: socksProxy, hostMode: hostMode, requiredHostMode: requiredHostMode, sessionMode: sessionMode, + smpProxyMode: smpProxyMode, + smpProxyFallback: smpProxyFallback, + smpWebPortServers: smpWebPortServers, tcpConnectTimeout: tcpConnectTimeout, tcpTimeout: tcpTimeout, tcpTimeoutPerKb: tcpTimeoutPerKb, + rcvConcurrency: rcvConcurrency, tcpKeepAlive: tcpKeepAlive, smpPingInterval: smpPingInterval, smpPingCount: smpPingCount, @@ -304,12 +384,18 @@ public func getNetCfg() -> NetCfg { ) } -public func setNetCfg(_ cfg: NetCfg) { +public func setNetCfg(_ cfg: NetCfg, networkProxy: NetworkProxy?) { networkUseOnionHostsGroupDefault.set(OnionHosts(netCfg: cfg)) networkSessionModeGroupDefault.set(cfg.sessionMode) + networkSMPProxyModeGroupDefault.set(cfg.smpProxyMode) + networkSMPProxyFallbackGroupDefault.set(cfg.smpProxyFallback) + let socksProxy = networkProxy?.toProxyString() + groupDefaults.set(socksProxy, forKey: GROUP_DEFAULT_NETWORK_SOCKS_PROXY) + networkSMPWebPortServersDefault.set(cfg.smpWebPortServers) groupDefaults.set(cfg.tcpConnectTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT) groupDefaults.set(cfg.tcpTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT) groupDefaults.set(cfg.tcpTimeoutPerKb, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB) + groupDefaults.set(cfg.rcvConcurrency, forKey: GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY) groupDefaults.set(cfg.smpPingInterval, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL) groupDefaults.set(cfg.smpPingCount, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT) if let tcpKeepAlive = cfg.tcpKeepAlive { diff --git a/apps/ios/SimpleXChat/CallTypes.swift b/apps/ios/SimpleXChat/CallTypes.swift index 227a1fbda5..da1720c134 100644 --- a/apps/ios/SimpleXChat/CallTypes.swift +++ b/apps/ios/SimpleXChat/CallTypes.swift @@ -42,6 +42,7 @@ public struct RcvCallInvitation: Decodable { public var contact: Contact public var callType: CallType public var sharedKey: String? + public var callUUID: String? public var callTs: Date public var callTypeText: LocalizedStringKey { get { @@ -52,10 +53,8 @@ public struct RcvCallInvitation: Decodable { } } - public var callkitUUID: UUID? = UUID() - private enum CodingKeys: String, CodingKey { - case user, contact, callType, sharedKey, callTs + case user, contact, callType, sharedKey, callUUID, callTs } public static let sampleData = RcvCallInvitation( @@ -81,6 +80,14 @@ public enum CallMediaType: String, Codable, Equatable { case audio = "audio" } +public enum CallMediaSource: String, Codable, Equatable { + case mic = "mic" + case camera = "camera" + case screenAudio = "screenAudio" + case screenVideo = "screenVideo" + case unknown = "unknown" +} + public enum VideoCamera: String, Codable, Equatable { case user = "user" case environment = "environment" diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index b74a2517c7..88246465e1 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -9,13 +9,23 @@ import Foundation import SwiftUI -public struct User: Identifiable, Decodable, UserLike, NamedChat { +// version to establishing direct connection with a group member (xGrpDirectInvVRange in core) +public let CREATE_MEMBER_CONTACT_VERSION = 2 + +// version to receive reports (MCReport) +public let REPORTS_VERSION = 12 + +public let contentModerationPostLink = URL(string: "https://simplex.chat/blog/20250114-simplex-network-large-groups-privacy-preserving-content-moderation.html#preventing-server-abuse-without-compromising-e2e-encryption")! + +public struct User: Identifiable, Decodable, UserLike, NamedChat, Hashable { public var userId: Int64 + public var agentUserId: String var userContactId: Int64 var localDisplayName: ContactName public var profile: LocalProfile public var fullPreferences: FullPreferences public var activeUser: Bool + public var activeOrder: Int64 public var displayName: String { get { profile.displayName } } public var fullName: String { get { profile.fullName } } @@ -26,6 +36,7 @@ public struct User: Identifiable, Decodable, UserLike, NamedChat { public var sendRcptsContacts: Bool public var sendRcptsSmallGroups: Bool public var viewPwdHash: UserPwdHash? + public var uiThemes: ThemeModeOverrides? public var id: Int64 { userId } @@ -41,18 +52,20 @@ public struct User: Identifiable, Decodable, UserLike, NamedChat { public static let sampleData = User( userId: 1, + agentUserId: "abc", userContactId: 1, localDisplayName: "alice", profile: LocalProfile.sampleData, fullPreferences: FullPreferences.sampleData, activeUser: true, + activeOrder: 0, showNtfs: true, sendRcptsContacts: true, sendRcptsSmallGroups: false ) } -public struct UserRef: Identifiable, Decodable, UserLike { +public struct UserRef: Identifiable, Decodable, UserLike, Hashable { public var userId: Int64 public var localDisplayName: ContactName @@ -63,12 +76,12 @@ public protocol UserLike: Identifiable { var userId: Int64 { get } } -public struct UserPwdHash: Decodable { +public struct UserPwdHash: Decodable, Hashable { public var hash: String public var salt: String } -public struct UserInfo: Decodable, Identifiable { +public struct UserInfo: Decodable, Identifiable, Hashable { public var user: User public var unreadCount: Int @@ -89,7 +102,7 @@ public typealias ContactName = String public typealias GroupName = String -public struct Profile: Codable, NamedChat { +public struct Profile: Codable, NamedChat, Hashable { public init( displayName: String, fullName: String, @@ -121,7 +134,7 @@ public struct Profile: Codable, NamedChat { ) } -public struct LocalProfile: Codable, NamedChat { +public struct LocalProfile: Codable, NamedChat, Hashable { public init( profileId: Int64, displayName: String, @@ -171,13 +184,13 @@ public func fromLocalProfile (_ profile: LocalProfile) -> Profile { Profile(displayName: profile.displayName, fullName: profile.fullName, image: profile.image, contactLink: profile.contactLink, preferences: profile.preferences) } -public struct UserProfileUpdateSummary: Decodable { +public struct UserProfileUpdateSummary: Decodable, Hashable { public var updateSuccesses: Int public var updateFailures: Int public var changedContacts: [Contact] } -public enum ChatType: String { +public enum ChatType: String, Hashable { case direct = "@" case group = "#" case local = "*" @@ -202,7 +215,7 @@ extension NamedChat { public typealias ChatId = String -public struct FullPreferences: Decodable, Equatable { +public struct FullPreferences: Decodable, Equatable, Hashable { public var timedMessages: TimedMessagesPreference public var fullDelete: SimplePreference public var reactions: SimplePreference @@ -232,7 +245,7 @@ public struct FullPreferences: Decodable, Equatable { ) } -public struct Preferences: Codable { +public struct Preferences: Codable, Hashable { public var timedMessages: TimedMessagesPreference? public var fullDelete: SimplePreference? public var reactions: SimplePreference? @@ -308,11 +321,11 @@ public func contactUserPreferencesToPreferences(_ contactUserPreferences: Contac ) } -public protocol Preference: Codable, Equatable { +public protocol Preference: Codable, Equatable, Hashable { var allow: FeatureAllowed { get set } } -public struct SimplePreference: Preference { +public struct SimplePreference: Preference, Hashable { public var allow: FeatureAllowed public init(allow: FeatureAllowed) { @@ -320,7 +333,7 @@ public struct SimplePreference: Preference { } } -public struct TimedMessagesPreference: Preference { +public struct TimedMessagesPreference: Preference, Hashable { public var allow: FeatureAllowed public var ttl: Int? @@ -334,7 +347,7 @@ public struct TimedMessagesPreference: Preference { } } -public enum CustomTimeUnit { +public enum CustomTimeUnit: Hashable { case second case minute case hour @@ -433,7 +446,7 @@ public func shortTimeText(_ seconds: Int?) -> LocalizedStringKey { return CustomTimeUnit.toShortText(seconds: seconds) } -public struct ContactUserPreferences: Decodable { +public struct ContactUserPreferences: Decodable, Hashable { public var timedMessages: ContactUserPreference public var fullDelete: ContactUserPreference public var reactions: ContactUserPreference @@ -483,7 +496,7 @@ public struct ContactUserPreferences: Decodable { ) } -public struct ContactUserPreference: Decodable { +public struct ContactUserPreference: Decodable, Hashable { public var enabled: FeatureEnabled public var userPreference: ContactUserPref

public var contactPreference: P @@ -495,7 +508,7 @@ public struct ContactUserPreference: Decodable { } } -public struct FeatureEnabled: Decodable { +public struct FeatureEnabled: Decodable, Hashable { public var forUser: Bool public var forContact: Bool @@ -521,12 +534,12 @@ public struct FeatureEnabled: Decodable { : NSLocalizedString("off", comment: "enabled status") } - public var iconColor: Color { - forUser ? .green : forContact ? .yellow : .secondary + public func iconColor(_ secondaryColor: Color) -> Color { + forUser ? .green : forContact ? .yellow : secondaryColor } } -public enum ContactUserPref: Decodable { +public enum ContactUserPref: Decodable, Hashable { case contact(preference: P) // contact override is set case user(preference: P) // global user default is used @@ -543,10 +556,11 @@ public protocol Feature { var iconFilled: String { get } var iconScale: CGFloat { get } var hasParam: Bool { get } + var hasRole: Bool { get } var text: String { get } } -public enum ChatFeature: String, Decodable, Feature { +public enum ChatFeature: String, Decodable, Feature, Hashable { case timedMessages case fullDelete case reactions @@ -569,6 +583,8 @@ public enum ChatFeature: String, Decodable, Feature { } } + public var hasRole: Bool { false } + public var text: String { switch self { case .timedMessages: return NSLocalizedString("Disappearing messages", comment: "chat feature") @@ -687,13 +703,15 @@ public enum ChatFeature: String, Decodable, Feature { } } -public enum GroupFeature: String, Decodable, Feature { +public enum GroupFeature: String, Decodable, Feature, Hashable { case timedMessages case directMessages case fullDelete case reactions case voice case files + case simplexLinks + case reports case history public var id: Self { self } @@ -705,6 +723,20 @@ public enum GroupFeature: String, Decodable, Feature { } } + public var hasRole: Bool { + switch self { + case .timedMessages: false + case .directMessages: true + case .fullDelete: false + case .reactions: false + case .voice: true + case .files: true + case .simplexLinks: true + case .reports: false + case .history: false + } + } + public var text: String { switch self { case .timedMessages: return NSLocalizedString("Disappearing messages", comment: "chat feature") @@ -713,6 +745,8 @@ public enum GroupFeature: String, Decodable, Feature { case .reactions: return NSLocalizedString("Message reactions", comment: "chat feature") case .voice: return NSLocalizedString("Voice messages", comment: "chat feature") case .files: return NSLocalizedString("Files and media", comment: "chat feature") + case .simplexLinks: return NSLocalizedString("SimpleX links", comment: "chat feature") + case .reports: return NSLocalizedString("Member reports", comment: "chat feature") case .history: return NSLocalizedString("Visible history", comment: "chat feature") } } @@ -725,6 +759,8 @@ public enum GroupFeature: String, Decodable, Feature { case .reactions: return "face.smiling" case .voice: return "mic" case .files: return "doc" + case .simplexLinks: return "link.circle" + case .reports: return "flag" case .history: return "clock" } } @@ -737,6 +773,8 @@ public enum GroupFeature: String, Decodable, Feature { case .reactions: return "face.smiling.fill" case .voice: return "mic.fill" case .files: return "doc.fill" + case .simplexLinks: return "link.circle.fill" + case .reports: return "flag.fill" case .history: return "clock.fill" } } @@ -781,6 +819,16 @@ public enum GroupFeature: String, Decodable, Feature { case .on: return "Allow to send files and media." case .off: return "Prohibit sending files and media." } + case .simplexLinks: + switch enabled { + case .on: return "Allow to send SimpleX links." + case .off: return "Prohibit sending SimpleX links." + } + case .reports: + switch enabled { + case .on: return "Allow to report messsages to moderators." + case .off: return "Prohibit reporting messages to moderators." + } case .history: switch enabled { case .on: return "Send up to 100 last messages to new members." @@ -791,33 +839,43 @@ public enum GroupFeature: String, Decodable, Feature { switch self { case .timedMessages: switch enabled { - case .on: return "Group members can send disappearing messages." - case .off: return "Disappearing messages are prohibited in this group." + case .on: return "Members can send disappearing messages." + case .off: return "Disappearing messages are prohibited." } case .directMessages: switch enabled { - case .on: return "Group members can send direct messages." - case .off: return "Direct messages between members are prohibited in this group." + case .on: return "Members can send direct messages." + case .off: return "Direct messages between members are prohibited." } case .fullDelete: switch enabled { - case .on: return "Group members can irreversibly delete sent messages. (24 hours)" - case .off: return "Irreversible message deletion is prohibited in this group." + case .on: return "Members can irreversibly delete sent messages. (24 hours)" + case .off: return "Irreversible message deletion is prohibited." } case .reactions: switch enabled { - case .on: return "Group members can add message reactions." - case .off: return "Message reactions are prohibited in this group." + case .on: return "Members can add message reactions." + case .off: return "Message reactions are prohibited." } case .voice: switch enabled { - case .on: return "Group members can send voice messages." - case .off: return "Voice messages are prohibited in this group." + case .on: return "Members can send voice messages." + case .off: return "Voice messages are prohibited." } case .files: switch enabled { - case .on: return "Group members can send files and media." - case .off: return "Files and media are prohibited in this group." + case .on: return "Members can send files and media." + case .off: return "Files and media are prohibited." + } + case .simplexLinks: + switch enabled { + case .on: return "Members can send SimpleX links." + case .off: return "SimpleX links are prohibited." + } + case .reports: + switch enabled { + case .on: return "Members can report messsages to moderators." + case .off: return "Reporting messages to moderators is prohibited." } case .history: switch enabled { @@ -860,7 +918,7 @@ public enum ContactFeatureAllowed: Identifiable, Hashable { } } -public struct ContactFeaturesAllowed: Equatable { +public struct ContactFeaturesAllowed: Equatable, Hashable { public var timedMessagesAllowed: Bool public var timedMessagesTTL: Int? public var fullDelete: ContactFeatureAllowed @@ -938,7 +996,7 @@ public func contactFeatureAllowedToPref(_ contactFeatureAllowed: ContactFeatureA } } -public enum FeatureAllowed: String, Codable, Identifiable { +public enum FeatureAllowed: String, Codable, Identifiable, Hashable { case always case yes case no @@ -956,22 +1014,26 @@ public enum FeatureAllowed: String, Codable, Identifiable { } } -public struct FullGroupPreferences: Decodable, Equatable { +public struct FullGroupPreferences: Decodable, Equatable, Hashable { public var timedMessages: TimedMessagesGroupPreference - public var directMessages: GroupPreference + public var directMessages: RoleGroupPreference public var fullDelete: GroupPreference public var reactions: GroupPreference - public var voice: GroupPreference - public var files: GroupPreference + public var voice: RoleGroupPreference + public var files: RoleGroupPreference + public var simplexLinks: RoleGroupPreference + public var reports: GroupPreference public var history: GroupPreference public init( timedMessages: TimedMessagesGroupPreference, - directMessages: GroupPreference, + directMessages: RoleGroupPreference, fullDelete: GroupPreference, reactions: GroupPreference, - voice: GroupPreference, - files: GroupPreference, + voice: RoleGroupPreference, + files: RoleGroupPreference, + simplexLinks: RoleGroupPreference, + reports: GroupPreference, history: GroupPreference ) { self.timedMessages = timedMessages @@ -980,36 +1042,44 @@ public struct FullGroupPreferences: Decodable, Equatable { self.reactions = reactions self.voice = voice self.files = files + self.simplexLinks = simplexLinks + self.reports = reports self.history = history } public static let sampleData = FullGroupPreferences( timedMessages: TimedMessagesGroupPreference(enable: .off), - directMessages: GroupPreference(enable: .off), + directMessages: RoleGroupPreference(enable: .off, role: nil), fullDelete: GroupPreference(enable: .off), reactions: GroupPreference(enable: .on), - voice: GroupPreference(enable: .on), - files: GroupPreference(enable: .on), + voice: RoleGroupPreference(enable: .on, role: nil), + files: RoleGroupPreference(enable: .on, role: nil), + simplexLinks: RoleGroupPreference(enable: .on, role: nil), + reports: GroupPreference(enable: .on), history: GroupPreference(enable: .on) ) } -public struct GroupPreferences: Codable { +public struct GroupPreferences: Codable, Hashable { public var timedMessages: TimedMessagesGroupPreference? - public var directMessages: GroupPreference? + public var directMessages: RoleGroupPreference? public var fullDelete: GroupPreference? public var reactions: GroupPreference? - public var voice: GroupPreference? - public var files: GroupPreference? + public var voice: RoleGroupPreference? + public var files: RoleGroupPreference? + public var simplexLinks: RoleGroupPreference? + public var reports: GroupPreference? public var history: GroupPreference? public init( timedMessages: TimedMessagesGroupPreference? = nil, - directMessages: GroupPreference? = nil, + directMessages: RoleGroupPreference? = nil, fullDelete: GroupPreference? = nil, reactions: GroupPreference? = nil, - voice: GroupPreference? = nil, - files: GroupPreference? = nil, + voice: RoleGroupPreference? = nil, + files: RoleGroupPreference? = nil, + simplexLinks: RoleGroupPreference? = nil, + reports: GroupPreference? = nil, history: GroupPreference? = nil ) { self.timedMessages = timedMessages @@ -1018,16 +1088,20 @@ public struct GroupPreferences: Codable { self.reactions = reactions self.voice = voice self.files = files + self.simplexLinks = simplexLinks + self.reports = reports self.history = history } public static let sampleData = GroupPreferences( timedMessages: TimedMessagesGroupPreference(enable: .off), - directMessages: GroupPreference(enable: .off), + directMessages: RoleGroupPreference(enable: .off, role: nil), fullDelete: GroupPreference(enable: .off), reactions: GroupPreference(enable: .on), - voice: GroupPreference(enable: .on), - files: GroupPreference(enable: .on), + voice: RoleGroupPreference(enable: .on, role: nil), + files: RoleGroupPreference(enable: .on, role: nil), + simplexLinks: RoleGroupPreference(enable: .on, role: nil), + reports: GroupPreference(enable: .on), history: GroupPreference(enable: .on) ) } @@ -1040,23 +1114,51 @@ public func toGroupPreferences(_ fullPreferences: FullGroupPreferences) -> Group reactions: fullPreferences.reactions, voice: fullPreferences.voice, files: fullPreferences.files, + simplexLinks: fullPreferences.simplexLinks, + reports: fullPreferences.reports, history: fullPreferences.history ) } -public struct GroupPreference: Codable, Equatable { +public struct GroupPreference: Codable, Equatable, Hashable { public var enable: GroupFeatureEnabled public var on: Bool { enable == .on } + public func enabled(_ role: GroupMemberRole?, for m: GroupMember?) -> GroupFeatureEnabled { + switch enable { + case .off: .off + case .on: + if let role, let m { + m.memberRole >= role ? .on : .off + } else { + .on + } + } + } + public init(enable: GroupFeatureEnabled) { self.enable = enable } } -public struct TimedMessagesGroupPreference: Codable, Equatable { +public struct RoleGroupPreference: Codable, Equatable, Hashable { + public var enable: GroupFeatureEnabled + public var role: GroupMemberRole? + + public func on(for m: GroupMember) -> Bool { + enable == .on && m.memberRole >= (role ?? .observer) + } + + public init(enable: GroupFeatureEnabled, role: GroupMemberRole?) { + self.enable = enable + self.role = role + } +} + +public struct TimedMessagesGroupPreference: Codable, Equatable, Hashable { public var enable: GroupFeatureEnabled public var ttl: Int? @@ -1070,7 +1172,7 @@ public struct TimedMessagesGroupPreference: Codable, Equatable { } } -public enum GroupFeatureEnabled: String, Codable, Identifiable { +public enum GroupFeatureEnabled: String, Codable, Identifiable, Hashable { case on case off @@ -1085,21 +1187,21 @@ public enum GroupFeatureEnabled: String, Codable, Identifiable { } } - public var iconColor: Color { + public func iconColor(_ secondaryColor: Color) -> Color { switch self { case .on: return .green - case .off: return .secondary + case .off: return secondaryColor } } } -public enum ChatInfo: Identifiable, Decodable, NamedChat { +public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable { case direct(contact: Contact) case group(groupInfo: GroupInfo) case local(noteFolder: NoteFolder) case contactRequest(contactRequest: UserContactRequest) case contactConnection(contactConnection: PendingContactConnection) - case invalidJSON(json: String) + case invalidJSON(json: Data?) private static let invalidChatName = NSLocalizedString("invalid chat", comment: "invalid chat data") @@ -1221,6 +1323,28 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat { } } } + + public var chatDeleted: Bool { + get { + switch self { + case let .direct(contact): return contact.chatDeleted + default: return false + } + } + } + + public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { + get { + switch self { + case let .direct(contact): return contact.userCantSendReason + case let .group(groupInfo): return groupInfo.userCantSendReason + case let .local(noteFolder): return noteFolder.userCantSendReason + case let .contactRequest(contactRequest): return contactRequest.userCantSendReason + case let .contactConnection(contactConnection): return contactConnection.userCantSendReason + case .invalidJSON: return ("can't send messages", nil) + } + } + } public var sendMsgEnabled: Bool { get { @@ -1255,6 +1379,13 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat { } } + public var contactCard: Bool { + switch self { + case let .direct(contact): contact.activeConn == nil && contact.profile.contactLink != nil && contact.active + default: false + } + } + public var groupInfo: GroupInfo? { switch self { case let .group(groupInfo): return groupInfo @@ -1280,7 +1411,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat { case .timedMessages: return prefs.timedMessages.on case .fullDelete: return prefs.fullDelete.on case .reactions: return prefs.reactions.on - case .voice: return prefs.voice.on + case .voice: return prefs.voice.on(for: groupInfo.membership) case .calls: return false } case .local: @@ -1305,7 +1436,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat { } } - public enum ShowEnableVoiceMessagesAlert { + public enum ShowEnableVoiceMessagesAlert: Hashable { case userEnable case askContact case groupOwnerCan @@ -1323,7 +1454,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat { return .other } case let .group(groupInfo): - if !groupInfo.fullGroupPreferences.voice.on { + if !groupInfo.fullGroupPreferences.voice.on(for: groupInfo.membership) { return .groupOwnerCan } else { return .other @@ -1333,8 +1464,37 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat { } } - public var ntfsEnabled: Bool { - self.chatSettings?.enableNtfs == .all + public enum ShowEnableCallsAlert: Hashable { + case userEnable + case askContact + case other + } + + public var showEnableCallsAlert: ShowEnableCallsAlert { + switch self { + case let .direct(contact): + if contact.mergedPreferences.calls.userPreference.preference.allow == .no { + return .userEnable + } else if contact.mergedPreferences.calls.contactPreference.allow == .no { + return .askContact + } else { + return .other + } + default: + return .other + } + } + + public func ntfsEnabled(chatItem: ChatItem) -> Bool { + ntfsEnabled(chatItem.meta.userMention) + } + + public func ntfsEnabled(_ userMention: Bool) -> Bool { + switch self.chatSettings?.enableNtfs { + case .all: true + case .mentions: userMention + default: false + } } public var chatSettings: ChatSettings? { @@ -1344,6 +1504,22 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat { default: return nil } } + + public var nextNtfMode: MsgFilter? { + self.chatSettings?.enableNtfs.nextMode(mentions: hasMentions) + } + + public var hasMentions: Bool { + if case .group = self { true } else { false } + } + + public var chatTags: [Int64]? { + switch self { + case let .direct(contact): return contact.chatTags + case let .group(groupInfo): return groupInfo.chatTags + default: return nil + } + } var createdAt: Date { switch self { @@ -1377,8 +1553,26 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat { case .invalidJSON: return .now } } + + public func ttl(_ globalTTL: ChatItemTTL) -> ChatTTL { + switch self { + case let .direct(contact): + return if let ciTTL = contact.chatItemTTL { + ChatTTL.chat(ChatItemTTL(ciTTL)) + } else { + ChatTTL.userDefault(globalTTL) + } + case let .group(groupInfo): + return if let ciTTL = groupInfo.chatItemTTL { + ChatTTL.chat(ChatItemTTL(ciTTL)) + } else { + ChatTTL.userDefault(globalTTL) + } + default: return ChatTTL.userDefault(globalTTL) + } + } - public struct SampleData { + public struct SampleData: Hashable { public var direct: ChatInfo public var group: ChatInfo public var local: ChatInfo @@ -1395,14 +1589,20 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat { ) } -public struct ChatData: Decodable, Identifiable { +public struct ChatData: Decodable, Identifiable, Hashable, ChatLike { public var chatInfo: ChatInfo public var chatItems: [ChatItem] public var chatStats: ChatStats public var id: ChatId { get { chatInfo.id } } - public static func invalidJSON(_ json: String) -> ChatData { + public init(chatInfo: ChatInfo, chatItems: [ChatItem], chatStats: ChatStats = ChatStats()) { + self.chatInfo = chatInfo + self.chatItems = chatItems + self.chatStats = chatStats + } + + public static func invalidJSON(_ json: Data?) -> ChatData { ChatData( chatInfo: .invalidJSON(json: json), chatItems: [], @@ -1411,19 +1611,25 @@ public struct ChatData: Decodable, Identifiable { } } -public struct ChatStats: Decodable { - public init(unreadCount: Int = 0, minUnreadItemId: Int64 = 0, unreadChat: Bool = false) { +public struct ChatStats: Decodable, Hashable { + public init(unreadCount: Int = 0, unreadMentions: Int = 0, reportsCount: Int = 0, minUnreadItemId: Int64 = 0, unreadChat: Bool = false) { self.unreadCount = unreadCount + self.unreadMentions = unreadMentions + self.reportsCount = reportsCount self.minUnreadItemId = minUnreadItemId self.unreadChat = unreadChat } public var unreadCount: Int = 0 + public var unreadMentions: Int = 0 + // actual only via getChats() and getChat(.initial), otherwise, zero + public var reportsCount: Int = 0 public var minUnreadItemId: Int64 = 0 + // actual only via getChats(), otherwise, false public var unreadChat: Bool = false } -public struct Contact: Identifiable, Decodable, NamedChat { +public struct Contact: Identifiable, Decodable, NamedChat, Hashable { public var contactId: Int64 var localDisplayName: ContactName public var profile: LocalProfile @@ -1439,15 +1645,26 @@ public struct Contact: Identifiable, Decodable, NamedChat { var chatTs: Date? var contactGroupMemberId: Int64? var contactGrpInvSent: Bool - + public var chatTags: [Int64] + public var chatItemTTL: Int64? + public var uiThemes: ThemeModeOverrides? + public var chatDeleted: Bool + public var id: ChatId { get { "@\(contactId)" } } public var apiId: Int64 { get { contactId } } public var ready: Bool { get { activeConn?.connStatus == .ready } } + public var sndReady: Bool { get { ready || activeConn?.connStatus == .sndReady } } public var active: Bool { get { contactStatus == .active } } - public var sendMsgEnabled: Bool { get { - (ready && active && !(activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false)) - || nextSendGrpInv - } } + public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { + // TODO [short links] this will have additional statuses for pending contact requests before they are accepted + if nextSendGrpInv { return nil } + if !active { return ("contact deleted", nil) } + if !sndReady { return ("contact not ready", nil) } + if activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false { return ("not synchronized", nil) } + if activeConn?.connDisabled ?? true { return ("contact disabled", nil) } + return nil + } + public var sendMsgEnabled: Bool { userCantSendReason == nil } public var nextSendGrpInv: Bool { get { contactGroupMemberId != nil && !contactGrpInvSent } } public var displayName: String { localAlias == "" ? profile.displayName : localAlias } public var fullName: String { get { profile.fullName } } @@ -1500,16 +1717,19 @@ public struct Contact: Identifiable, Decodable, NamedChat { mergedPreferences: ContactUserPreferences.sampleData, createdAt: .now, updatedAt: .now, - contactGrpInvSent: false + contactGrpInvSent: false, + chatTags: [], + chatDeleted: false ) } -public enum ContactStatus: String, Decodable { +public enum ContactStatus: String, Decodable, Hashable { case active = "active" case deleted = "deleted" + case deletedByUser = "deletedByUser" } -public struct ContactRef: Decodable, Equatable { +public struct ContactRef: Decodable, Equatable, Hashable { var contactId: Int64 public var agentConnId: String var connId: Int64 @@ -1518,12 +1738,12 @@ public struct ContactRef: Decodable, Equatable { public var id: ChatId { get { "@\(contactId)" } } } -public struct ContactSubStatus: Decodable { +public struct ContactSubStatus: Decodable, Hashable { public var contact: Contact public var contactError: ChatError? } -public struct Connection: Decodable { +public struct Connection: Decodable, Hashable { public var connId: Int64 public var agentConnId: String public var peerChatVRange: VersionRange @@ -1536,15 +1756,25 @@ public struct Connection: Decodable { public var pqEncryption: Bool public var pqSndEnabled: Bool? public var pqRcvEnabled: Bool? + public var authErrCounter: Int + public var quotaErrCounter: Int public var connectionStats: ConnectionStats? = nil private enum CodingKeys: String, CodingKey { - case connId, agentConnId, peerChatVRange, connStatus, connLevel, viaGroupLink, customUserProfileId, connectionCode, pqSupport, pqEncryption, pqSndEnabled, pqRcvEnabled + case connId, agentConnId, peerChatVRange, connStatus, connLevel, viaGroupLink, customUserProfileId, connectionCode, pqSupport, pqEncryption, pqSndEnabled, pqRcvEnabled, authErrCounter, quotaErrCounter } public var id: ChatId { get { ":\(connId)" } } + public var connDisabled: Bool { + authErrCounter >= 10 // authErrDisableCount in core + } + + public var connInactive: Bool { + quotaErrCounter >= 5 // quotaErrInactiveCount in core + } + public var connPQEnabled: Bool { pqSndEnabled == true && pqRcvEnabled == true } @@ -1552,30 +1782,28 @@ public struct Connection: Decodable { static let sampleData = Connection( connId: 1, agentConnId: "abc", - peerChatVRange: VersionRange(minVersion: 1, maxVersion: 1), + peerChatVRange: VersionRange(1, 1), connStatus: .ready, connLevel: 0, viaGroupLink: false, pqSupport: false, - pqEncryption: false + pqEncryption: false, + authErrCounter: 0, + quotaErrCounter: 0 ) } -public struct VersionRange: Decodable { - public init(minVersion: Int, maxVersion: Int) { +public struct VersionRange: Decodable, Hashable { + public init(_ minVersion: Int, _ maxVersion: Int) { self.minVersion = minVersion self.maxVersion = maxVersion } public var minVersion: Int public var maxVersion: Int - - public func isCompatibleRange(_ vRange: VersionRange) -> Bool { - self.minVersion <= vRange.maxVersion && vRange.minVersion <= self.maxVersion - } } -public struct SecurityCode: Decodable, Equatable { +public struct SecurityCode: Decodable, Equatable, Hashable { public init(securityCode: String, verifiedAt: Date) { self.securityCode = securityCode self.verifiedAt = verifiedAt @@ -1585,7 +1813,7 @@ public struct SecurityCode: Decodable, Equatable { public var verifiedAt: Date } -public struct UserContact: Decodable { +public struct UserContact: Decodable, Hashable { public var userContactLinkId: Int64 // public var connReqContact: String public var groupId: Int64? @@ -1603,7 +1831,7 @@ public struct UserContact: Decodable { } } -public struct UserContactRequest: Decodable, NamedChat { +public struct UserContactRequest: Decodable, NamedChat, Hashable { var contactRequestId: Int64 public var userContactLinkId: Int64 public var cReqChatVRange: VersionRange @@ -1615,6 +1843,7 @@ public struct UserContactRequest: Decodable, NamedChat { public var id: ChatId { get { "<@\(contactRequestId)" } } public var apiId: Int64 { get { contactRequestId } } var ready: Bool { get { true } } + public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { ("can't send messages", nil) } public var sendMsgEnabled: Bool { get { false } } public var displayName: String { get { profile.displayName } } public var fullName: String { get { profile.fullName } } @@ -1624,7 +1853,7 @@ public struct UserContactRequest: Decodable, NamedChat { public static let sampleData = UserContactRequest( contactRequestId: 1, userContactLinkId: 1, - cReqChatVRange: VersionRange(minVersion: 1, maxVersion: 1), + cReqChatVRange: VersionRange(1, 1), localDisplayName: "alice", profile: Profile.sampleData, createdAt: .now, @@ -1632,14 +1861,14 @@ public struct UserContactRequest: Decodable, NamedChat { ) } -public struct PendingContactConnection: Decodable, NamedChat { +public struct PendingContactConnection: Decodable, NamedChat, Hashable { public var pccConnId: Int64 var pccAgentConnId: String var pccConnStatus: ConnStatus public var viaContactUri: Bool public var groupLinkId: String? public var customUserProfileId: Int64? - public var connReqInv: String? + public var connLinkInv: CreatedConnLink? public var localAlias: String var createdAt: Date public var updatedAt: Date @@ -1647,6 +1876,7 @@ public struct PendingContactConnection: Decodable, NamedChat { public var id: ChatId { get { ":\(pccConnId)" } } public var apiId: Int64 { get { pccConnId } } var ready: Bool { get { false } } + public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { ("can't send messages", nil) } public var sendMsgEnabled: Bool { get { false } } var localDisplayName: String { get { String.localizedStringWithFormat(NSLocalizedString("connection:%@", comment: "connection information"), pccConnId) } @@ -1654,9 +1884,11 @@ public struct PendingContactConnection: Decodable, NamedChat { public var displayName: String { get { if let initiated = pccConnStatus.initiated { - return initiated && !viaContactUri + return viaContactUri + ? NSLocalizedString("requested to connect", comment: "chat list item title") + : initiated ? NSLocalizedString("invited to connect", comment: "chat list item title") - : NSLocalizedString("connecting…", comment: "chat list item title") + : NSLocalizedString("accepted invitation", comment: "chat list item title") } else { // this should not be in the list return NSLocalizedString("connection established", comment: "chat list item title (it should not be shown") @@ -1722,8 +1954,9 @@ public struct PendingContactConnection: Decodable, NamedChat { } } -public enum ConnStatus: String, Decodable { +public enum ConnStatus: String, Decodable, Hashable { case new = "new" + case prepared = "prepared" case joined = "joined" case requested = "requested" case accepted = "accepted" @@ -1735,10 +1968,11 @@ public enum ConnStatus: String, Decodable { get { switch self { case .new: return true + case .prepared: return false case .joined: return false case .requested: return true case .accepted: return true - case .sndReady: return false + case .sndReady: return nil case .ready: return nil case .deleted: return nil } @@ -1746,7 +1980,7 @@ public enum ConnStatus: String, Decodable { } } -public struct Group: Decodable { +public struct Group: Decodable, Hashable { public var groupInfo: GroupInfo public var members: [GroupMember] @@ -1756,28 +1990,44 @@ public struct Group: Decodable { } } -public struct GroupInfo: Identifiable, Decodable, NamedChat { +public struct GroupInfo: Identifiable, Decodable, NamedChat, Hashable { public var groupId: Int64 var localDisplayName: GroupName public var groupProfile: GroupProfile + public var businessChat: BusinessChatInfo? public var fullGroupPreferences: FullGroupPreferences public var membership: GroupMember - public var hostConnCustomUserProfileId: Int64? public var chatSettings: ChatSettings var createdAt: Date var updatedAt: Date var chatTs: Date? + public var uiThemes: ThemeModeOverrides? public var id: ChatId { get { "#\(groupId)" } } public var apiId: Int64 { get { groupId } } public var ready: Bool { get { true } } - public var sendMsgEnabled: Bool { get { membership.memberActive } } - public var displayName: String { get { groupProfile.displayName } } + public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { + return if membership.memberActive { + membership.memberRole == .observer ? ("you are observer", "Please contact group admin.") : nil + } else { + switch membership.memberStatus { + case .memRejected: ("request to join rejected", nil) + case .memGroupDeleted: ("group is deleted", nil) + case .memRemoved: ("removed from group", nil) + case .memLeft: ("you left", nil) + default: ("can't send messages", nil) + } + } + } + public var sendMsgEnabled: Bool { userCantSendReason == nil } + public var displayName: String { localAlias == "" ? groupProfile.displayName : localAlias } public var fullName: String { get { groupProfile.fullName } } public var image: String? { get { groupProfile.image } } - public var localAlias: String { "" } + public var chatTags: [Int64] + public var chatItemTTL: Int64? + public var localAlias: String - public var canEdit: Bool { + public var isOwner: Bool { return membership.memberRole == .owner && membership.memberCurrent } @@ -1795,19 +2045,20 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat { groupProfile: GroupProfile.sampleData, fullGroupPreferences: FullGroupPreferences.sampleData, membership: GroupMember.sampleData, - hostConnCustomUserProfileId: nil, chatSettings: ChatSettings.defaults, createdAt: .now, - updatedAt: .now + updatedAt: .now, + chatTags: [], + localAlias: "" ) } -public struct GroupRef: Decodable { +public struct GroupRef: Decodable, Hashable { public var groupId: Int64 var localDisplayName: GroupName } -public struct GroupProfile: Codable, NamedChat { +public struct GroupProfile: Codable, NamedChat, Hashable { public init(displayName: String, fullName: String, description: String? = nil, image: String? = nil, groupPreferences: GroupPreferences? = nil) { self.displayName = displayName self.fullName = fullName @@ -1829,7 +2080,18 @@ public struct GroupProfile: Codable, NamedChat { ) } -public struct GroupMember: Identifiable, Decodable { +public struct BusinessChatInfo: Decodable, Hashable { + public var chatType: BusinessChatType + public var businessId: String + public var customerId: String +} + +public enum BusinessChatType: String, Codable, Hashable { + case business + case customer +} + +public struct GroupMember: Identifiable, Decodable, Hashable { public var groupMemberId: Int64 public var groupId: Int64 public var memberId: String @@ -1844,8 +2106,17 @@ public struct GroupMember: Identifiable, Decodable { public var memberContactId: Int64? public var memberContactProfileId: Int64 public var activeConn: Connection? + public var memberChatVRange: VersionRange public var id: String { "#\(groupId) @\(groupMemberId)" } + public var ready: Bool { get { activeConn?.connStatus == .ready } } + public var sndReady: Bool { get { ready || activeConn?.connStatus == .sndReady } } + public var sendMsgEnabled: Bool { get { + sndReady + && memberCurrent + && !(activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false) + && !(activeConn?.connDisabled ?? true) + } } public var displayName: String { get { let p = memberProfile @@ -1886,14 +2157,26 @@ public struct GroupMember: Identifiable, Decodable { ? String.localizedStringWithFormat(NSLocalizedString("Past member %@", comment: "past/unknown group member"), name) : name } + + public var localAliasAndFullName: String { + get { + let p = memberProfile + let fullName = p.displayName + (p.fullName == "" || p.fullName == p.displayName ? "" : " / \(p.fullName)") + let name = p.localAlias == "" ? fullName : "\(p.localAlias) (\(fullName))" + + return pastMember(name) + } + } public var memberActive: Bool { switch memberStatus { + case .memRejected: return false case .memRemoved: return false case .memLeft: return false case .memGroupDeleted: return false case .memUnknown: return false case .memInvited: return false + case .memPendingApproval: return true case .memIntroduced: return false case .memIntroInvited: return false case .memAccepted: return false @@ -1906,11 +2189,13 @@ public struct GroupMember: Identifiable, Decodable { public var memberCurrent: Bool { switch memberStatus { + case .memRejected: return false case .memRemoved: return false case .memLeft: return false case .memGroupDeleted: return false case .memUnknown: return false case .memInvited: return false + case .memPendingApproval: return false case .memIntroduced: return true case .memIntroInvited: return true case .memAccepted: return true @@ -1930,7 +2215,7 @@ public struct GroupMember: Identifiable, Decodable { public func canChangeRoleTo(groupInfo: GroupInfo) -> [GroupMemberRole]? { if !canBeRemoved(groupInfo: groupInfo) { return nil } let userRole = groupInfo.membership.memberRole - return GroupMemberRole.allCases.filter { $0 <= userRole && $0 != .author } + return GroupMemberRole.supportedRoles.filter { $0 <= userRole } } public func canBlockForAll(groupInfo: GroupInfo) -> Bool { @@ -1938,7 +2223,19 @@ public struct GroupMember: Identifiable, Decodable { return memberStatus != .memRemoved && memberStatus != .memLeft && memberRole < .admin && userRole >= .admin && userRole >= memberRole && groupInfo.membership.memberActive } + + public var canReceiveReports: Bool { + memberRole >= .moderator && versionRange.maxVersion >= REPORTS_VERSION + } + public var versionRange: VersionRange { + if let activeConn { + activeConn.peerChatVRange + } else { + memberChatVRange + } + } + public var memberIncognito: Bool { memberProfile.profileId != memberContactProfileId } @@ -1957,38 +2254,43 @@ public struct GroupMember: Identifiable, Decodable { memberProfile: LocalProfile.sampleData, memberContactId: 1, memberContactProfileId: 1, - activeConn: Connection.sampleData + activeConn: Connection.sampleData, + memberChatVRange: VersionRange(2, 12) ) } -public struct GroupMemberSettings: Codable { +public struct GroupMemberSettings: Codable, Hashable { public var showMessages: Bool } -public struct GroupMemberRef: Decodable { +public struct GroupMemberRef: Decodable, Hashable { var groupMemberId: Int64 var profile: Profile } -public struct GroupMemberIds: Decodable { +public struct GroupMemberIds: Decodable, Hashable { var groupMemberId: Int64 var groupId: Int64 } -public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Decodable { - case observer = "observer" - case author = "author" - case member = "member" - case admin = "admin" - case owner = "owner" +public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Codable, Hashable { + case observer + case author + case member + case moderator + case admin + case owner public var id: Self { self } + public static var supportedRoles: [GroupMemberRole] = [.observer, .member, .admin, .owner] + public var text: String { switch self { case .observer: return NSLocalizedString("observer", comment: "member role") case .author: return NSLocalizedString("author", comment: "member role") case .member: return NSLocalizedString("member", comment: "member role") + case .moderator: return NSLocalizedString("moderator", comment: "member role") case .admin: return NSLocalizedString("admin", comment: "member role") case .owner: return NSLocalizedString("owner", comment: "member role") } @@ -1996,11 +2298,12 @@ public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Dec private var comparisonValue: Int { switch self { - case .observer: return 0 - case .author: return 1 - case .member: return 2 - case .admin: return 3 - case .owner: return 4 + case .observer: 0 + case .author: 1 + case .member: 2 + case .moderator: 3 + case .admin: 4 + case .owner: 5 } } @@ -2009,7 +2312,7 @@ public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Dec } } -public enum GroupMemberCategory: String, Decodable { +public enum GroupMemberCategory: String, Decodable, Hashable { case userMember = "user" case inviteeMember = "invitee" case hostMember = "host" @@ -2017,12 +2320,14 @@ public enum GroupMemberCategory: String, Decodable { case postMember = "post" } -public enum GroupMemberStatus: String, Decodable { +public enum GroupMemberStatus: String, Decodable, Hashable { + case memRejected = "rejected" case memRemoved = "removed" case memLeft = "left" case memGroupDeleted = "deleted" case memUnknown = "unknown" case memInvited = "invited" + case memPendingApproval = "pending_approval" case memIntroduced = "introduced" case memIntroInvited = "intro-inv" case memAccepted = "accepted" @@ -2033,11 +2338,13 @@ public enum GroupMemberStatus: String, Decodable { public var text: LocalizedStringKey { switch self { + case .memRejected: return "rejected" case .memRemoved: return "removed" case .memLeft: return "left" case .memGroupDeleted: return "group deleted" case .memUnknown: return "unknown status" case .memInvited: return "invited" + case .memPendingApproval: return "pending approval" case .memIntroduced: return "connecting (introduced)" case .memIntroInvited: return "connecting (introduction invitation)" case .memAccepted: return "connecting (accepted)" @@ -2050,11 +2357,13 @@ public enum GroupMemberStatus: String, Decodable { public var shortText: LocalizedStringKey { switch self { + case .memRejected: return "rejected" case .memRemoved: return "removed" case .memLeft: return "left" case .memGroupDeleted: return "group deleted" case .memUnknown: return "unknown" case .memInvited: return "invited" + case .memPendingApproval: return "pending" case .memIntroduced: return "connecting" case .memIntroInvited: return "connecting" case .memAccepted: return "connecting" @@ -2066,7 +2375,7 @@ public enum GroupMemberStatus: String, Decodable { } } -public struct NoteFolder: Identifiable, Decodable, NamedChat { +public struct NoteFolder: Identifiable, Decodable, NamedChat, Hashable { public var noteFolderId: Int64 public var favorite: Bool public var unread: Bool @@ -2077,6 +2386,7 @@ public struct NoteFolder: Identifiable, Decodable, NamedChat { public var id: ChatId { get { "*\(noteFolderId)" } } public var apiId: Int64 { get { noteFolderId } } public var ready: Bool { get { true } } + public var userCantSendReason: (composeLabel: LocalizedStringKey, alertMessage: LocalizedStringKey?)? { nil } public var sendMsgEnabled: Bool { get { true } } public var displayName: String { get { ChatInfo.privateNotesChatName } } public var fullName: String { get { "" } } @@ -2099,83 +2409,173 @@ public struct NoteFolder: Identifiable, Decodable, NamedChat { ) } -public enum InvitedBy: Decodable { +public enum InvitedBy: Decodable, Hashable { case contact(byContactId: Int64) case user case unknown } -public struct MemberSubError: Decodable { +public struct MemberSubError: Decodable, Hashable { var member: GroupMemberIds var memberError: ChatError } -public enum ConnectionEntity: Decodable { - case rcvDirectMsgConnection(contact: Contact?) - case rcvGroupMsgConnection(groupInfo: GroupInfo, groupMember: GroupMember) - case sndFileConnection(sndFileTransfer: SndFileTransfer) - case rcvFileConnection(rcvFileTransfer: RcvFileTransfer) - case userContactConnection(userContact: UserContact) +public enum ConnectionEntity: Decodable, Hashable { + case rcvDirectMsgConnection(entityConnection: Connection, contact: Contact?) + case rcvGroupMsgConnection(entityConnection: Connection, groupInfo: GroupInfo, groupMember: GroupMember) + case sndFileConnection(entityConnection: Connection, sndFileTransfer: SndFileTransfer) + case rcvFileConnection(entityConnection: Connection, rcvFileTransfer: RcvFileTransfer) + case userContactConnection(entityConnection: Connection, userContact: UserContact) public var id: String? { switch self { - case let .rcvDirectMsgConnection(contact): - return contact?.id ?? nil - case let .rcvGroupMsgConnection(_, groupMember): - return groupMember.id - case let .userContactConnection(userContact): - return userContact.id + case let .rcvDirectMsgConnection(conn, contact): + contact?.id ?? conn.id + case let .rcvGroupMsgConnection(_, _, groupMember): + groupMember.id + case let .userContactConnection(_, userContact): + userContact.id default: - return nil + nil } } + + // public var localDisplayName: String? { + // switch self { + // case let .rcvDirectMsgConnection(conn, contact): + // if let name = contact?.localDisplayName { "@\(name)" } else { conn.id } + // case let .rcvGroupMsgConnection(_, g, m): + // "#\(g.localDisplayName) @\(m.localDisplayName)" + // case let .userContactConnection(_, userContact): + // userContact.id + // default: + // nil + // } + // } - public var ntfsEnabled: Bool { + public var conn: Connection { switch self { - case let .rcvDirectMsgConnection(contact): return contact?.chatSettings.enableNtfs == .all - case let .rcvGroupMsgConnection(groupInfo, _): return groupInfo.chatSettings.enableNtfs == .all - case .sndFileConnection: return false - case .rcvFileConnection: return false - case let .userContactConnection(userContact): return userContact.groupId == nil + case let .rcvDirectMsgConnection(entityConnection, _): entityConnection + case let .rcvGroupMsgConnection(entityConnection, _, _): entityConnection + case let .sndFileConnection(entityConnection, _): entityConnection + case let .rcvFileConnection(entityConnection, _): entityConnection + case let .userContactConnection(entityConnection, _): entityConnection } } } -public struct NtfMsgInfo: Decodable { +public struct NtfConn: Decodable, Hashable { + public var user: User + public var agentConnId: String + public var agentDbQueueId: Int64 + public var connEntity: ConnectionEntity + public var expectedMsg_: NtfMsgInfo? +} + +public struct NtfMsgInfo: Decodable, Hashable { public var msgId: String public var msgTs: Date } -public struct AChatItem: Decodable { - public var chatInfo: ChatInfo - public var chatItem: ChatItem +public enum RcvNtfMsgInfo: Decodable { + case info(ntfMsgInfo: NtfMsgInfo?) + case error(ntfMsgError: AgentErrorType) + + @inline(__always) + public var noMsg: Bool { + if case let .info(msg) = self { msg == nil } else { true } + } - public var chatId: String { - if case let .groupRcv(groupMember) = chatItem.chatDir { - return groupMember.id - } - return chatInfo.id + @inline(__always) + public var isError: Bool { + if case .error = self { true } else { false } } } -public struct ACIReaction: Decodable { +let iso8601DateFormatter = { + let f = ISO8601DateFormatter() + f.formatOptions = [.withInternetDateTime] + return f +}() + +// used in apiGetConnNtfMessages +public struct ConnMsgReq { + public var msgConnId: String + public var msgDbQueueId: Int64 + public var msgTs: Date // SystemTime encodes as a number, should be taken from NtfMsgInfo + + public init(msgConnId: String, msgDbQueueId: Int64, msgTs: Date) { + self.msgConnId = msgConnId + self.msgDbQueueId = msgDbQueueId + self.msgTs = msgTs + } + + public var cmdString: String { + "\(msgConnId):\(msgDbQueueId):\(iso8601DateFormatter.string(from: msgTs))" + } +} + +public struct NtfMsgAckInfo: Decodable, Hashable { + public var msgId: String + public var msgTs_: Date? +} + +public struct ChatItemDeletion: Decodable, Hashable { + public var deletedChatItem: AChatItem + public var toChatItem: AChatItem? = nil +} + +public struct AChatItem: Decodable, Hashable { + public var chatInfo: ChatInfo + public var chatItem: ChatItem +} + +public struct CIMentionMember: Decodable, Hashable { + public var groupMemberId: Int64 + public var displayName: String + public var localAlias: String? + public var memberRole: GroupMemberRole +} + +public struct CIMention: Decodable, Hashable { + public var memberId: String + public var memberRef: CIMentionMember? + + public init(groupMember m: GroupMember) { + self.memberId = m.memberId + self.memberRef = CIMentionMember( + groupMemberId: m.groupMemberId, + displayName: m.memberProfile.displayName, + localAlias: m.memberProfile.localAlias, + memberRole: m.memberRole + ) + } +} + +public struct ACIReaction: Decodable, Hashable { public var chatInfo: ChatInfo public var chatReaction: CIReaction } -public struct CIReaction: Decodable { +public struct MemberReaction: Decodable, Hashable { + public var groupMember: GroupMember + public var reactionTs: Date +} + +public struct CIReaction: Decodable, Hashable { public var chatDir: CIDirection public var chatItem: ChatItem public var sentAt: Date public var reaction: MsgReaction } -public struct ChatItem: Identifiable, Decodable { - public init(chatDir: CIDirection, meta: CIMeta, content: CIContent, formattedText: [FormattedText]? = nil, quotedItem: CIQuote? = nil, reactions: [CIReactionCount] = [], file: CIFile? = nil) { +public struct ChatItem: Identifiable, Decodable, Hashable { + public init(chatDir: CIDirection, meta: CIMeta, content: CIContent, formattedText: [FormattedText]? = nil, mentions: [String: CIMention]? = nil, quotedItem: CIQuote? = nil, reactions: [CIReactionCount] = [], file: CIFile? = nil) { self.chatDir = chatDir self.meta = meta self.content = content self.formattedText = formattedText + self.mentions = mentions self.quotedItem = quotedItem self.reactions = reactions self.file = file @@ -2185,6 +2585,7 @@ public struct ChatItem: Identifiable, Decodable { public var meta: CIMeta public var content: CIContent public var formattedText: [FormattedText]? + public var mentions: [String: CIMention]? public var quotedItem: CIQuote? public var reactions: [CIReactionCount] public var file: CIFile? @@ -2193,7 +2594,7 @@ public struct ChatItem: Identifiable, Decodable { public var isLiveDummy: Bool = false private enum CodingKeys: String, CodingKey { - case chatDir, meta, content, formattedText, quotedItem, reactions, file + case chatDir, meta, content, formattedText, mentions, quotedItem, reactions, file } public var id: Int64 { meta.itemId } @@ -2361,21 +2762,53 @@ public struct ChatItem: Identifiable, Decodable { } } - public func memberToModerate(_ chatInfo: ChatInfo) -> (GroupInfo, GroupMember)? { + public func memberToModerate(_ chatInfo: ChatInfo) -> (GroupInfo, GroupMember?)? { switch (chatInfo, chatDir) { case let (.group(groupInfo), .groupRcv(groupMember)): let m = groupInfo.membership return m.memberRole >= .admin && m.memberRole >= groupMember.memberRole && meta.itemDeleted == nil ? (groupInfo, groupMember) : nil + case let (.group(groupInfo), .groupSnd): + let m = groupInfo.membership + return m.memberRole >= .admin ? (groupInfo, nil) : nil default: return nil } } - public static func getSample (_ id: Int64, _ dir: CIDirection, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, quotedItem: CIQuote? = nil, file: CIFile? = nil, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, editable: Bool = true) -> ChatItem { + public var showLocalDelete: Bool { + switch content { + case .sndDirectE2EEInfo: return false + case .rcvDirectE2EEInfo: return false + case .sndGroupE2EEInfo: return false + case .rcvGroupE2EEInfo: return false + default: return true + } + } + + public var isReport: Bool { + switch content { + case let .sndMsgContent(msgContent), let .rcvMsgContent(msgContent): + switch msgContent { + case .report: true + default: false + } + default: false + } + } + + public var isActiveReport: Bool { + isReport && !isDeletedContent && meta.itemDeleted == nil + } + + public var canBeDeletedForSelf: Bool { + (content.msgContent != nil && !meta.isLive) || meta.itemDeleted != nil || isDeletedContent || mergeCategory != nil || showLocalDelete + } + + public static func getSample (_ id: Int64, _ dir: CIDirection, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, quotedItem: CIQuote? = nil, file: CIFile? = nil, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> ChatItem { ChatItem( chatDir: dir, - meta: CIMeta.getSample(id, ts, text, status, itemDeleted: itemDeleted, itemEdited: itemEdited, itemLive: itemLive, editable: editable), + meta: CIMeta.getSample(id, ts, text, status, itemDeleted: itemDeleted, itemEdited: itemEdited, itemLive: itemLive, deletable: deletable, editable: editable), content: .sndMsgContent(msgContent: .text(text)), quotedItem: quotedItem, file: file @@ -2452,6 +2885,35 @@ public struct ChatItem: Identifiable, Decodable { file: nil ) } + + public static func getReportSample(text: String, reason: ReportReason, item: ChatItem, sender: GroupMember? = nil) -> ChatItem { + let chatDir = if let sender = sender { + CIDirection.groupRcv(groupMember: sender) + } else { + CIDirection.groupSnd + } + + return ChatItem( + chatDir: chatDir, + meta: CIMeta( + itemId: -2, + itemTs: .now, + itemText: "", + itemStatus: .rcvRead, + createdAt: .now, + updatedAt: .now, + itemDeleted: nil, + itemEdited: false, + itemLive: false, + userMention: false, + deletable: false, + editable: false + ), + content: .sndMsgContent(msgContent: .report(text: text, reason: reason)), + quotedItem: CIQuote.getSample(item.id, item.meta.createdAt, item.text, chatDir: item.chatDir), + file: nil + ) + } public static func deletedItemDummy() -> ChatItem { ChatItem( @@ -2466,6 +2928,8 @@ public struct ChatItem: Identifiable, Decodable { itemDeleted: nil, itemEdited: false, itemLive: false, + userMention: false, + deletable: false, editable: false ), content: .rcvDeleted(deleteMode: .cidmBroadcast), @@ -2487,6 +2951,8 @@ public struct ChatItem: Identifiable, Decodable { itemDeleted: nil, itemEdited: false, itemLive: true, + userMention: false, + deletable: false, editable: false ), content: .sndMsgContent(msgContent: .text("")), @@ -2497,7 +2963,7 @@ public struct ChatItem: Identifiable, Decodable { return item } - public static func invalidJSON(chatDir: CIDirection?, meta: CIMeta?, json: String) -> ChatItem { + public static func invalidJSON(chatDir: CIDirection?, meta: CIMeta?, json: Data?) -> ChatItem { ChatItem( chatDir: chatDir ?? .directSnd, meta: meta ?? .invalidJSON, @@ -2508,7 +2974,7 @@ public struct ChatItem: Identifiable, Decodable { } } -public enum CIMergeCategory { +public enum CIMergeCategory: Hashable { case memberConnected case rcvGroupEvent case sndGroupEvent @@ -2517,7 +2983,7 @@ public enum CIMergeCategory { case chatFeature } -public enum CIDirection: Decodable { +public enum CIDirection: Decodable, Hashable { case directSnd case directRcv case groupSnd @@ -2537,22 +3003,33 @@ public enum CIDirection: Decodable { } } } + + public func sameDirection(_ dir: CIDirection) -> Bool { + switch (self, dir) { + case let (.groupRcv(m1), .groupRcv(m2)): m1.groupMemberId == m2.groupMemberId + default: sent == dir.sent + } + } } -public struct CIMeta: Decodable { +public struct CIMeta: Decodable, Hashable { public var itemId: Int64 public var itemTs: Date var itemText: String public var itemStatus: CIStatus + public var sentViaProxy: Bool? public var createdAt: Date public var updatedAt: Date + public var itemForwarded: CIForwardedFrom? public var itemDeleted: CIDeleted? public var itemEdited: Bool public var itemTimed: CITimed? public var itemLive: Bool? + public var userMention: Bool + public var deletable: Bool public var editable: Bool - public var timestampText: Text { get { formatTimestampText(itemTs) } } + public var timestampText: Text { Text(formatTimestampMeta(itemTs)) } public var recent: Bool { updatedAt + 10 > .now } public var isLive: Bool { itemLive == true } public var disappearing: Bool { !isRcvNew && itemTimed?.deleteAt != nil } @@ -2562,11 +3039,7 @@ public struct CIMeta: Decodable { return false } - public func statusIcon(_ metaColor: Color = .secondary) -> (String, Color)? { - itemStatus.statusIcon(metaColor) - } - - public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, editable: Bool = true) -> CIMeta { + public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> CIMeta { CIMeta( itemId: id, itemTs: ts, @@ -2577,6 +3050,8 @@ public struct CIMeta: Decodable { itemDeleted: itemDeleted, itemEdited: itemEdited, itemLive: itemLive, + userMention: false, + deletable: deletable, editable: editable ) } @@ -2592,21 +3067,34 @@ public struct CIMeta: Decodable { itemDeleted: nil, itemEdited: false, itemLive: false, + userMention: false, + deletable: false, editable: false ) } } -public struct CITimed: Decodable { +public struct CITimed: Decodable, Hashable { public var ttl: Int public var deleteAt: Date? } let msgTimeFormat = Date.FormatStyle.dateTime.hour().minute() let msgDateFormat = Date.FormatStyle.dateTime.day(.twoDigits).month(.twoDigits) +let msgDateYearFormat = Date.FormatStyle.dateTime.day(.twoDigits).month(.twoDigits).year(.twoDigits) public func formatTimestampText(_ date: Date) -> Text { - return Text(date, format: recent(date) ? msgTimeFormat : msgDateFormat) + Text(verbatim: date.formatted( + recent(date) + ? msgTimeFormat + : Calendar.current.isDate(date, equalTo: .now, toGranularity: .year) + ? msgDateFormat + : msgDateYearFormat + )) +} + +public func formatTimestampMeta(_ date: Date) -> String { + date.formatted(date: .omitted, time: .shortened) } private func recent(_ date: Date) -> Bool { @@ -2624,43 +3112,61 @@ private func recent(_ date: Date) -> Bool { return isSameDay || (now < currentDay12 && date >= previousDay18 && date < currentDay00) } -public enum CIStatus: Decodable { +public enum CIStatus: Decodable, Hashable { case sndNew case sndSent(sndProgress: SndCIStatusProgress) case sndRcvd(msgRcptStatus: MsgReceiptStatus, sndProgress: SndCIStatusProgress) case sndErrorAuth - case sndError(agentError: String) + case sndError(agentError: SndError) + case sndWarning(agentError: SndError) case rcvNew case rcvRead case invalid(text: String) - var id: String { + public var id: String { switch self { case .sndNew: return "sndNew" case .sndSent: return "sndSent" case .sndRcvd: return "sndRcvd" case .sndErrorAuth: return "sndErrorAuth" case .sndError: return "sndError" + case .sndWarning: return "sndWarning" case .rcvNew: return "rcvNew" case .rcvRead: return "rcvRead" case .invalid: return "invalid" } } - - public func statusIcon(_ metaColor: Color = .secondary) -> (String, Color)? { + + public var sent: Bool { switch self { - case .sndNew: return nil - case .sndSent: return ("checkmark", metaColor) - case let .sndRcvd(msgRcptStatus, _): + case .sndNew: true + case .sndSent: true + case .sndRcvd: true + case .sndErrorAuth: true + case .sndError: true + case .sndWarning: true + case .rcvNew: false + case .rcvRead: false + case .invalid: false + } + } + + public func statusIcon(_ metaColor: Color, _ paleMetaColor: Color, _ primaryColor: Color = .accentColor) -> (Image, Color)? { + switch self { + case .sndNew: nil + case let .sndSent(sndProgress): + (Image("checkmark.wide"), sndProgress == .partial ? paleMetaColor : metaColor) + case let .sndRcvd(msgRcptStatus, sndProgress): switch msgRcptStatus { - case .ok: return ("checkmark", metaColor) - case .badMsgHash: return ("checkmark", .red) + case .ok: (Image("checkmark.2"), sndProgress == .partial ? paleMetaColor : metaColor) + case .badMsgHash: (Image("checkmark.2"), .red) } - case .sndErrorAuth: return ("multiply", .red) - case .sndError: return ("exclamationmark.triangle.fill", .yellow) - case .rcvNew: return ("circlebadge.fill", Color.accentColor) - case .rcvRead: return nil - case .invalid: return ("questionmark", metaColor) + case .sndErrorAuth: (Image(systemName: "multiply"), .red) + case .sndError: (Image(systemName: "multiply"), .red) + case .sndWarning: (Image(systemName: "exclamationmark.triangle.fill"), .orange) + case .rcvNew: (Image(systemName: "circlebadge.fill"), primaryColor) + case .rcvRead: nil + case .invalid: (Image(systemName: "questionmark"), metaColor) } } @@ -2675,7 +3181,11 @@ public enum CIStatus: Decodable { ) case let .sndError(agentError): return ( NSLocalizedString("Message delivery error", comment: "item status text"), - String.localizedStringWithFormat(NSLocalizedString("Unexpected error: %@", comment: "item status description"), agentError) + agentError.errorInfo + ) + case let .sndWarning(agentError): return ( + NSLocalizedString("Message delivery warning", comment: "item status text"), + agentError.errorInfo ) case .rcvNew: return nil case .rcvRead: return nil @@ -2685,19 +3195,126 @@ public enum CIStatus: Decodable { ) } } + + public var isSndRcvd: Bool { + switch self { + case .sndRcvd: return true + default: return false + } + } } -public enum MsgReceiptStatus: String, Decodable { +public enum SndError: Decodable, Hashable { + case auth + case quota + case expired + case relay(srvError: SrvError) + case proxy(proxyServer: String, srvError: SrvError) + case proxyRelay(proxyServer: String, srvError: SrvError) + case other(sndError: String) + + public var errorInfo: String { + switch self { + case .auth: NSLocalizedString("Wrong key or unknown connection - most likely this connection is deleted.", comment: "snd error text") + case .quota: NSLocalizedString("Capacity exceeded - recipient did not receive previously sent messages.", comment: "snd error text") + case .expired: NSLocalizedString("Network issues - message expired after many attempts to send it.", comment: "snd error text") + case let .relay(srvError): String.localizedStringWithFormat(NSLocalizedString("Destination server error: %@", comment: "snd error text"), srvError.errorInfo) + case let .proxy(proxyServer, srvError): String.localizedStringWithFormat(NSLocalizedString("Forwarding server: %@\nError: %@", comment: "snd error text"), proxyServer, srvError.errorInfo) + case let .proxyRelay(proxyServer, srvError): String.localizedStringWithFormat(NSLocalizedString("Forwarding server: %@\nDestination server error: %@", comment: "snd error text"), proxyServer, srvError.errorInfo) + case let .other(sndError): String.localizedStringWithFormat(NSLocalizedString("Error: %@", comment: "snd error text"), sndError) + } + } +} + +public enum SrvError: Decodable, Hashable { + case host + case version + case other(srvError: String) + + var id: String { + switch self { + case .host: return "host" + case .version: return "version" + case let .other(srvError): return "other \(srvError)" + } + } + + public var errorInfo: String { + switch self { + case .host: NSLocalizedString("Server address is incompatible with network settings.", comment: "srv error text.") + case .version: NSLocalizedString("Server version is incompatible with network settings.", comment: "srv error text") + case let .other(srvError): srvError + } + } +} + +public enum MsgReceiptStatus: String, Decodable, Hashable { case ok case badMsgHash } -public enum SndCIStatusProgress: String, Decodable { +public enum SndCIStatusProgress: String, Decodable, Hashable { case partial case complete } -public enum CIDeleted: Decodable { +public enum GroupSndStatus: Decodable, Hashable { + case new + case forwarded + case inactive + case sent + case rcvd(msgRcptStatus: MsgReceiptStatus) + case error(agentError: SndError) + case warning(agentError: SndError) + case invalid(text: String) + + public func statusIcon(_ metaColor: Color, _ primaryColor: Color = .accentColor) -> (Image, Color) { + switch self { + case .new: (Image(systemName: "ellipsis"), metaColor) + case .forwarded: (Image(systemName: "chevron.forward.2"), metaColor) + case .inactive: (Image(systemName: "person.badge.minus"), metaColor) + case .sent: (Image("checkmark.wide"), metaColor) + case let .rcvd(msgRcptStatus): + switch msgRcptStatus { + case .ok: (Image("checkmark.2"), metaColor) + case .badMsgHash: (Image("checkmark.2"), .red) + } + case .error: (Image(systemName: "multiply"), .red) + case .warning: (Image(systemName: "exclamationmark.triangle.fill"), .orange) + case .invalid: (Image(systemName: "questionmark"), metaColor) + } + } + + public var statusInfo: (String, String)? { + switch self { + case .new: return nil + case .forwarded: return ( + NSLocalizedString("Message forwarded", comment: "item status text"), + NSLocalizedString("No direct connection yet, message is forwarded by admin.", comment: "item status description") + ) + case .inactive: return ( + NSLocalizedString("Member inactive", comment: "item status text"), + NSLocalizedString("Message may be delivered later if member becomes active.", comment: "item status description") + ) + case .sent: return nil + case .rcvd: return nil + case let .error(agentError): return ( + NSLocalizedString("Message delivery error", comment: "item status text"), + agentError.errorInfo + ) + case let .warning(agentError): return ( + NSLocalizedString("Message delivery warning", comment: "item status text"), + agentError.errorInfo + ) + case let .invalid(text): return ( + NSLocalizedString("Invalid status", comment: "item status text"), + text + ) + } + } +} + +public enum CIDeleted: Decodable, Hashable { case deleted(deletedTs: Date?) case blocked(deletedTs: Date?) case blockedByAdmin(deletedTs: Date?) @@ -2713,16 +3330,56 @@ public enum CIDeleted: Decodable { } } -public enum CIDeleteMode: String, Decodable { +public enum MsgDirection: String, Decodable, Hashable { + case rcv = "rcv" + case snd = "snd" +} + +public enum CIForwardedFrom: Decodable, Hashable { + case unknown + case contact(chatName: String, msgDir: MsgDirection, contactId: Int64?, chatItemId: Int64?) + case group(chatName: String, msgDir: MsgDirection, groupId: Int64?, chatItemId: Int64?) + + var chatName: String { + switch self { + case .unknown: "" + case let .contact(chatName, _, _, _): chatName + case let .group(chatName, _, _, _): chatName + } + } + + public var chatTypeApiIdMsgId: (ChatType, Int64, ChatItem.ID?)? { + switch self { + case .unknown: nil + case let .contact(_, _, contactId, msgId): + if let contactId { + (ChatType.direct, contactId, msgId) + } else { nil } + case let .group(_, _, groupId, msgId): + if let groupId { + (ChatType.group, groupId, msgId) + } else { nil } + } + } + + public func text(_ chatType: ChatType) -> LocalizedStringKey { + chatType == .local + ? (chatName == "" ? "saved" : "saved from \(chatName)") + : "forwarded" + } +} + +public enum CIDeleteMode: String, Decodable, Hashable { case cidmBroadcast = "broadcast" case cidmInternal = "internal" + case cidmInternalMark = "internalMark" } protocol ItemContent { var text: String { get } } -public enum CIContent: Decodable, ItemContent { +public enum CIContent: Decodable, ItemContent, Hashable { case sndMsgContent(msgContent: MsgContent) case rcvMsgContent(msgContent: MsgContent) case sndDeleted(deleteMode: CIDeleteMode) // legacy - since v4.3.0 itemDeleted field is used @@ -2742,8 +3399,8 @@ public enum CIContent: Decodable, ItemContent { case sndChatFeature(feature: ChatFeature, enabled: FeatureEnabled, param: Int?) case rcvChatPreference(feature: ChatFeature, allowed: FeatureAllowed, param: Int?) case sndChatPreference(feature: ChatFeature, allowed: FeatureAllowed, param: Int?) - case rcvGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?) - case sndGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?) + case rcvGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?, memberRole_: GroupMemberRole?) + case sndGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?, memberRole_: GroupMemberRole?) case rcvChatFeatureRejected(feature: ChatFeature) case rcvGroupFeatureRejected(groupFeature: GroupFeature) case sndModerated @@ -2753,7 +3410,7 @@ public enum CIContent: Decodable, ItemContent { case rcvDirectE2EEInfo(e2eeInfo: E2EEInfo) case sndGroupE2EEInfo(e2eeInfo: E2EEInfo) case rcvGroupE2EEInfo(e2eeInfo: E2EEInfo) - case invalidJSON(json: String) + case invalidJSON(json: Data?) public var text: String { get { @@ -2777,8 +3434,8 @@ public enum CIContent: Decodable, ItemContent { case let .sndChatFeature(feature, enabled, param): return CIContent.featureText(feature, enabled.text, param) case let .rcvChatPreference(feature, allowed, param): return CIContent.preferenceText(feature, allowed, param) case let .sndChatPreference(feature, allowed, param): return CIContent.preferenceText(feature, allowed, param) - case let .rcvGroupFeature(feature, preference, param): return CIContent.featureText(feature, preference.enable.text, param) - case let .sndGroupFeature(feature, preference, param): return CIContent.featureText(feature, preference.enable.text, param) + case let .rcvGroupFeature(feature, preference, param, role): return CIContent.featureText(feature, preference.enable.text, param, role) + case let .sndGroupFeature(feature, preference, param, role): return CIContent.featureText(feature, preference.enable.text, param, role) case let .rcvChatFeatureRejected(feature): return String.localizedStringWithFormat("%@: received, prohibited", feature.text) case let .rcvGroupFeatureRejected(groupFeature): return String.localizedStringWithFormat("%@: received, prohibited", groupFeature.text) case .sndModerated: return NSLocalizedString("moderated", comment: "moderated chat item") @@ -2803,10 +3460,25 @@ public enum CIContent: Decodable, ItemContent { NSLocalizedString("This chat is protected by end-to-end encryption.", comment: "E2EE info chat item") } - static func featureText(_ feature: Feature, _ enabled: String, _ param: Int?) -> String { - feature.hasParam - ? "\(feature.text): \(timeText(param))" - : "\(feature.text): \(enabled)" + static func featureText(_ feature: Feature, _ enabled: String, _ param: Int?, _ role: GroupMemberRole? = nil) -> String { + ( + feature.hasParam + ? "\(feature.text): \(timeText(param))" + : "\(feature.text): \(enabled)" + ) + + ( + feature.hasRole && role != nil + ? " (\(roleText(role)))" + : "" + ) + } + + private static func roleText(_ role: GroupMemberRole?) -> String { + switch role { + case .owner: NSLocalizedString("owners", comment: "feature role") + case .admin: NSLocalizedString("admins", comment: "feature role") + default: NSLocalizedString("all members", comment: "feature role") + } } public static func preferenceText(_ feature: Feature, _ allowed: FeatureAllowed, _ param: Int?) -> String { @@ -2841,9 +3513,16 @@ public enum CIContent: Decodable, ItemContent { default: return false } } + + public var isSndCall: Bool { + switch self { + case .sndCall: return true + default: return false + } + } } -public enum MsgDecryptError: String, Decodable { +public enum MsgDecryptError: String, Decodable, Hashable { case ratchetHeader case tooManySkipped case ratchetEarlier @@ -2861,21 +3540,19 @@ public enum MsgDecryptError: String, Decodable { } } -public struct CIQuote: Decodable, ItemContent { +public struct CIQuote: Decodable, ItemContent, Hashable { public var chatDir: CIDirection? public var itemId: Int64? var sharedMsgId: String? = nil public var sentAt: Date public var content: MsgContent public var formattedText: [FormattedText]? - public var text: String { switch (content.text, content) { case let ("", .voice(_, duration)): return durationText(duration) default: return content.text } } - public func getSender(_ membership: GroupMember?) -> String? { switch (chatDir) { case .directSnd: return "you" @@ -2899,13 +3576,13 @@ public struct CIQuote: Decodable, ItemContent { } } -public struct CIReactionCount: Decodable { +public struct CIReactionCount: Decodable, Hashable { public var reaction: MsgReaction public var userReacted: Bool public var totalReacted: Int } -public enum MsgReaction: Hashable { +public enum MsgReaction: Hashable, Identifiable { case emoji(emoji: MREmojiChar) case unknown(type: String) @@ -2926,15 +3603,24 @@ public enum MsgReaction: Hashable { case type case emoji } + + public var id: String { + switch self { + case let .emoji(emoji): emoji.rawValue + case let .unknown(unknown): unknown + } + } } -public enum MREmojiChar: String, Codable, CaseIterable { +public enum MREmojiChar: String, Codable, CaseIterable, Hashable { case thumbsup = "👍" case thumbsdown = "👎" case smile = "😀" + case laugh = "😂" case sad = "😢" case heart = "❤" case launch = "🚀" + case check = "✅" } extension MsgReaction: Decodable { @@ -2944,8 +3630,12 @@ extension MsgReaction: Decodable { let type = try container.decode(String.self, forKey: CodingKeys.type) switch type { case "emoji": - let emoji = try container.decode(MREmojiChar.self, forKey: CodingKeys.emoji) - self = .emoji(emoji: emoji) + do { + let emoji = try container.decode(MREmojiChar.self, forKey: CodingKeys.emoji) + self = .emoji(emoji: emoji) + } catch { + self = .unknown(type: "emoji") + } default: self = .unknown(type: type) } @@ -2969,7 +3659,7 @@ extension MsgReaction: Encodable { } } -public struct CIFile: Decodable { +public struct CIFile: Decodable, Hashable { public var fileId: Int64 public var fileName: String public var fileSize: Int64 @@ -2995,12 +3685,15 @@ public struct CIFile: Decodable { case .sndComplete: return true case .sndCancelled: return true case .sndError: return true + case .sndWarning: return true case .rcvInvitation: return false case .rcvAccepted: return false case .rcvTransfer: return false + case .rcvAborted: return false case .rcvCancelled: return false case .rcvComplete: return true case .rcvError: return false + case .rcvWarning: return false case .invalid: return false } } @@ -3019,19 +3712,44 @@ public struct CIFile: Decodable { } case .sndCancelled: return nil case .sndError: return nil + case .sndWarning: return sndCancelAction case .rcvInvitation: return nil case .rcvAccepted: return rcvCancelAction case .rcvTransfer: return rcvCancelAction + case .rcvAborted: return nil case .rcvCancelled: return nil case .rcvComplete: return nil + case .rcvWarning: return rcvCancelAction case .rcvError: return nil case .invalid: return nil } } } + + public var showStatusIconInSmallView: Bool { + get { + switch fileStatus { + case .sndStored: fileProtocol != .local + case .sndTransfer: true + case .sndComplete: false + case .sndCancelled: true + case .sndError: true + case .sndWarning: true + case .rcvInvitation: false + case .rcvAccepted: true + case .rcvTransfer: true + case .rcvAborted: true + case .rcvCancelled: true + case .rcvComplete: false + case .rcvError: true + case .rcvWarning: true + case .invalid: true + } + } + } } -public struct CryptoFile: Codable { +public struct CryptoFile: Codable, Hashable { public var filePath: String // the name of the file, not a full path public var cryptoArgs: CryptoFileArgs? @@ -3078,22 +3796,28 @@ public struct CryptoFile: Codable { static var decryptedUrls = Dictionary() } -public struct CryptoFileArgs: Codable { +public struct CryptoFileArgs: Codable, Hashable { public var fileKey: String public var fileNonce: String } -public struct CancelAction { +public struct CancelAction: Hashable { public var uiAction: String public var alert: AlertInfo } -public struct AlertInfo { +public struct AlertInfo: Hashable { public var title: LocalizedStringKey public var message: LocalizedStringKey public var confirm: LocalizedStringKey } +extension LocalizedStringKey: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine("\(self)") + } +} + private var sndCancelAction = CancelAction( uiAction: NSLocalizedString("Stop file", comment: "cancel file action"), alert: AlertInfo( @@ -3121,51 +3845,93 @@ private var rcvCancelAction = CancelAction( ) ) -public enum FileProtocol: String, Decodable { +public enum FileProtocol: String, Decodable, Hashable { case smp = "smp" case xftp = "xftp" case local = "local" } -public enum CIFileStatus: Decodable, Equatable { +public enum CIFileStatus: Decodable, Equatable, Hashable { case sndStored case sndTransfer(sndProgress: Int64, sndTotal: Int64) case sndComplete case sndCancelled - case sndError + case sndError(sndFileError: FileError) + case sndWarning(sndFileError: FileError) case rcvInvitation case rcvAccepted case rcvTransfer(rcvProgress: Int64, rcvTotal: Int64) + case rcvAborted case rcvComplete case rcvCancelled - case rcvError + case rcvError(rcvFileError: FileError) + case rcvWarning(rcvFileError: FileError) case invalid(text: String) - var id: String { + public var id: String { switch self { case .sndStored: return "sndStored" case let .sndTransfer(sndProgress, sndTotal): return "sndTransfer \(sndProgress) \(sndTotal)" case .sndComplete: return "sndComplete" case .sndCancelled: return "sndCancelled" - case .sndError: return "sndError" + case let .sndError(sndFileError): return "sndError \(sndFileError)" + case let .sndWarning(sndFileError): return "sndWarning \(sndFileError)" case .rcvInvitation: return "rcvInvitation" case .rcvAccepted: return "rcvAccepted" case let .rcvTransfer(rcvProgress, rcvTotal): return "rcvTransfer \(rcvProgress) \(rcvTotal)" + case .rcvAborted: return "rcvAborted" case .rcvComplete: return "rcvComplete" case .rcvCancelled: return "rcvCancelled" - case .rcvError: return "rcvError" + case let .rcvError(rcvFileError): return "rcvError \(rcvFileError)" + case let .rcvWarning(rcvFileError): return "rcvWarning \(rcvFileError)" case .invalid: return "invalid" } } } -public enum MsgContent: Equatable { +public enum FileError: Decodable, Equatable, Hashable { + case auth + case blocked(server: String, blockInfo: BlockingInfo) + case noFile + case relay(srvError: SrvError) + case other(fileError: String) + + var id: String { + switch self { + case .auth: return "auth" + case let .blocked(srv, info): return "blocked \(srv) \(info)" + case .noFile: return "noFile" + case let .relay(srvError): return "relay \(srvError)" + case let .other(fileError): return "other \(fileError)" + } + } + + public var errorInfo: String { + switch self { + case .auth: NSLocalizedString("Wrong key or unknown file chunk address - most likely file is deleted.", comment: "file error text") + case let .blocked(_, info): String.localizedStringWithFormat(NSLocalizedString("File is blocked by server operator:\n%@.", comment: "file error text"), info.reason.text) + case .noFile: NSLocalizedString("File not found - most likely file was deleted or cancelled.", comment: "file error text") + case let .relay(srvError): String.localizedStringWithFormat(NSLocalizedString("File server error: %@", comment: "file error text"), srvError.errorInfo) + case let .other(fileError): String.localizedStringWithFormat(NSLocalizedString("Error: %@", comment: "file error text"), fileError) + } + } + + public var moreInfoButton: (label: LocalizedStringKey, link: URL)? { + switch self { + case .blocked: ("How it works", contentModerationPostLink) + default: nil + } + } +} + +public enum MsgContent: Equatable, Hashable { case text(String) case link(text: String, preview: LinkPreview) case image(text: String, image: String) case video(text: String, image: String, duration: Int) case voice(text: String, duration: Int) case file(String) + case report(text: String, reason: ReportReason) // TODO include original JSON, possibly using https://github.com/zoul/generic-json-swift case unknown(type: String, text: String) @@ -3177,6 +3943,7 @@ public enum MsgContent: Equatable { case let .video(text, _, _): return text case let .voice(text, _): return text case let .file(text): return text + case let .report(text, _): return text case let .unknown(_, text): return text } } @@ -3209,7 +3976,24 @@ public enum MsgContent: Equatable { } } - var cmdString: String { + public var isImageOrVideo: Bool { + switch self { + case .image: true + case .video: true + default: false + } + } + + public var isMediaOrFileAttachment: Bool { + switch self { + case .image: true + case .video: true + case .file: true + default: false + } + } + + public var cmdString: String { "json \(encodeJSON(self))" } @@ -3219,6 +4003,7 @@ public enum MsgContent: Equatable { case preview case image case duration + case reason } public static func == (lhs: MsgContent, rhs: MsgContent) -> Bool { @@ -3229,6 +4014,7 @@ public enum MsgContent: Equatable { case let (.video(lt, li, ld), .video(rt, ri, rd)): return lt == rt && li == ri && ld == rd case let (.voice(lt, ld), .voice(rt, rd)): return lt == rt && ld == rd case let (.file(lf), .file(rf)): return lf == rf + case let (.report(lt, lr), .report(rt, rr)): return lt == rt && lr == rr case let (.unknown(lType, lt), .unknown(rType, rt)): return lType == rType && lt == rt default: return false } @@ -3264,6 +4050,10 @@ extension MsgContent: Decodable { case "file": let text = try container.decode(String.self, forKey: CodingKeys.text) self = .file(text) + case "report": + let text = try container.decode(String.self, forKey: CodingKeys.text) + let reason = try container.decode(ReportReason.self, forKey: CodingKeys.reason) + self = .report(text: text, reason: reason) default: let text = try? container.decode(String.self, forKey: CodingKeys.text) self = .unknown(type: type, text: text ?? "unknown message format") @@ -3301,6 +4091,10 @@ extension MsgContent: Encodable { case let .file(text): try container.encode("file", forKey: .type) try container.encode(text, forKey: .text) + case let .report(text, reason): + try container.encode("report", forKey: .type) + try container.encode(text, forKey: .text) + try container.encode(reason, forKey: .reason) // TODO use original JSON and type case let .unknown(_, text): try container.encode("text", forKey: .type) @@ -3309,16 +4103,22 @@ extension MsgContent: Encodable { } } -public struct FormattedText: Decodable { +public struct FormattedText: Decodable, Hashable { public var text: String public var format: Format? + public static func plain(_ text: String) -> [FormattedText] { + text.isEmpty + ? [] + : [FormattedText(text: text, format: nil)] + } + public var isSecret: Bool { if case .secret = format { true } else { false } } } -public enum Format: Decodable, Equatable { +public enum Format: Decodable, Equatable, Hashable { case bold case italic case strikeThrough @@ -3327,6 +4127,7 @@ public enum Format: Decodable, Equatable { case colored(color: FormatColor) case uri case simplexLink(linkType: SimplexLinkType, simplexUri: String, smpHosts: [String]) + case mention(memberName: String) case email case phone @@ -3340,21 +4141,23 @@ public enum Format: Decodable, Equatable { } } -public enum SimplexLinkType: String, Decodable { +public enum SimplexLinkType: String, Decodable, Hashable { case contact case invitation case group + case channel public var description: String { switch self { case .contact: return NSLocalizedString("SimpleX contact address", comment: "simplex link type") case .invitation: return NSLocalizedString("SimpleX one-time invitation", comment: "simplex link type") case .group: return NSLocalizedString("SimpleX group link", comment: "simplex link type") + case .channel: return NSLocalizedString("SimpleX channel link", comment: "simplex link type") } } } -public enum FormatColor: String, Decodable { +public enum FormatColor: String, Decodable, Hashable { case red = "red" case green = "green" case blue = "blue" @@ -3364,24 +4167,81 @@ public enum FormatColor: String, Decodable { case black = "black" case white = "white" - public var uiColor: Color { - get { - switch (self) { - case .red: return .red - case .green: return .green - case .blue: return .blue - case .yellow: return .yellow - case .cyan: return .cyan - case .magenta: return .purple - case .black: return .primary - case .white: return .primary - } + public var uiColor: Color? { + switch (self) { + case .red: .red + case .green: .green + case .blue: .blue + case .yellow: .yellow + case .cyan: .cyan + case .magenta: .purple + case .black: nil + case .white: nil + } + } +} + +public enum ReportReason: Hashable { + case spam + case illegal + case community + case profile + case other + case unknown(type: String) + + public static var supportedReasons: [ReportReason] = [.spam, .illegal, .community, .profile, .other] + + public var text: String { + switch self { + case .spam: return NSLocalizedString("Spam", comment: "report reason") + case .illegal: return NSLocalizedString("Inappropriate content", comment: "report reason") + case .community: return NSLocalizedString("Community guidelines violation", comment: "report reason") + case .profile: return NSLocalizedString("Inappropriate profile", comment: "report reason") + case .other: return NSLocalizedString("Another reason", comment: "report reason") + case let .unknown(type): return type + } + } + + public var attrString: NSAttributedString { + let descr = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) + return NSAttributedString(string: text.isEmpty ? self.text : "\(self.text): ", attributes: [ + .font: UIFont(descriptor: descr.withSymbolicTraits(.traitItalic) ?? descr, size: 0), + .foregroundColor: UIColor(Color.red) + ]) + } +} + +extension ReportReason: Encodable { + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + switch self { + case .spam: try container.encode("spam") + case .illegal: try container.encode("illegal") + case .community: try container.encode("community") + case .profile: try container.encode("profile") + case .other: try container.encode("other") + case let .unknown(type): try container.encode(type) + } + } +} + +extension ReportReason: Decodable { + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + let type = try container.decode(String.self) + switch type { + case "spam": self = .spam + case "illegal": self = .illegal + case "community": self = .community + case "profile": self = .profile + case "other": self = .other + default: self = .unknown(type: type) } } } // Struct to use with simplex API -public struct LinkPreview: Codable, Equatable { +public struct LinkPreview: Codable, Equatable, Hashable { public init(uri: URL, title: String, description: String = "", image: String) { self.uri = uri self.title = title @@ -3396,31 +4256,83 @@ public struct LinkPreview: Codable, Equatable { public var image: String } -public enum NtfTknStatus: String, Decodable { +public enum NtfTknStatus: String, Decodable, Hashable { case new = "NEW" case registered = "REGISTERED" case invalid = "INVALID" + case invalidBad = "INVALID,BAD" + case invalidTopic = "INVALID,TOPIC" + case invalidExpired = "INVALID,EXPIRED" + case invalidUnregistered = "INVALID,UNREGISTERED" case confirmed = "CONFIRMED" case active = "ACTIVE" case expired = "EXPIRED" + + public var workingToken: Bool { + switch self { + case .new: true + case .registered: true + case .invalid: false + case .invalidBad: false + case .invalidTopic: false + case .invalidExpired: false + case .invalidUnregistered: false + case .confirmed: true + case .active: true + case .expired: false + } + } + + public var text: String { + switch self { + case .new: NSLocalizedString("New", comment: "token status text") + case .registered: NSLocalizedString("Registered", comment: "token status text") + case .invalid: NSLocalizedString("Invalid", comment: "token status text") + case .invalidBad: NSLocalizedString("Invalid (bad token)", comment: "token status text") + case .invalidTopic: NSLocalizedString("Invalid (wrong topic)", comment: "token status text") + case .invalidExpired: NSLocalizedString("Invalid (expired)", comment: "token status text") + case .invalidUnregistered: NSLocalizedString("Invalid (unregistered)", comment: "token status text") + case .confirmed: NSLocalizedString("Confirmed", comment: "token status text") + case .active: NSLocalizedString("Active", comment: "token status text") + case .expired: NSLocalizedString("Expired", comment: "token status text") + } + } + + public func info(register: Bool) -> String { + switch self { + case .new: return NSLocalizedString("Please wait for token to be registered.", comment: "token info") + case .registered: fallthrough + case .confirmed: return NSLocalizedString("Please wait for token activation to complete.", comment: "token info") + case .active: return NSLocalizedString("You should receive notifications.", comment: "token info") + case .invalid: fallthrough + case .invalidBad: fallthrough + case .invalidTopic: fallthrough + case .invalidExpired: fallthrough + case .invalidUnregistered: fallthrough + case .expired: + return register + ? NSLocalizedString("Register notification token?", comment: "token info") + : NSLocalizedString("Please try to disable and re-enable notfications.", comment: "token info") + } + } } -public struct SndFileTransfer: Decodable { +public struct SndFileTransfer: Decodable, Hashable { } -public struct RcvFileTransfer: Decodable { +public struct RcvFileTransfer: Decodable, Hashable { public let fileId: Int64 } -public struct FileTransferMeta: Decodable { +public struct FileTransferMeta: Decodable, Hashable { public let fileId: Int64 public let fileName: String public let filePath: String public let fileSize: Int64 } -public enum CICallStatus: String, Decodable { +public enum CICallStatus: String, Decodable, Hashable { case pending case missed case rejected @@ -3452,7 +4364,7 @@ public func durationText(_ sec: Int) -> String { : String(format: "%02d:%02d:%02d", m / 60, m % 60, s) } -public enum MsgErrorType: Decodable { +public enum MsgErrorType: Decodable, Hashable { case msgSkipped(fromMsgId: Int64, toMsgId: Int64) case msgBadId(msgId: Int64) case msgBadHash @@ -3469,7 +4381,7 @@ public enum MsgErrorType: Decodable { } } -public struct CIGroupInvitation: Decodable { +public struct CIGroupInvitation: Decodable, Hashable { public var groupId: Int64 public var groupMemberId: Int64 public var localDisplayName: GroupName @@ -3485,18 +4397,18 @@ public struct CIGroupInvitation: Decodable { } } -public enum CIGroupInvitationStatus: String, Decodable { +public enum CIGroupInvitationStatus: String, Decodable, Hashable { case pending case accepted case rejected case expired } -public struct E2EEInfo: Decodable { +public struct E2EEInfo: Decodable, Hashable { public var pqEnabled: Bool } -public enum RcvDirectEvent: Decodable { +public enum RcvDirectEvent: Decodable, Hashable { case contactDeleted case profileUpdated(fromProfile: Profile, toProfile: Profile) @@ -3525,7 +4437,7 @@ public enum RcvDirectEvent: Decodable { } } -public enum RcvGroupEvent: Decodable { +public enum RcvGroupEvent: Decodable, Hashable { case memberAdded(groupMemberId: Int64, profile: Profile) case memberConnected case memberLeft @@ -3581,7 +4493,7 @@ public enum RcvGroupEvent: Decodable { } } -public enum SndGroupEvent: Decodable { +public enum SndGroupEvent: Decodable, Hashable { case memberRole(groupMemberId: Int64, profile: Profile, role: GroupMemberRole) case userRole(role: GroupMemberRole) case memberBlocked(groupMemberId: Int64, profile: Profile, blocked: Bool) @@ -3609,7 +4521,7 @@ public enum SndGroupEvent: Decodable { } } -public enum RcvConnEvent: Decodable { +public enum RcvConnEvent: Decodable, Hashable { case switchQueue(phase: SwitchPhase) case ratchetSync(syncStatus: RatchetSyncState) case verificationCodeReset @@ -3646,7 +4558,7 @@ func ratchetSyncStatusToText(_ ratchetSyncStatus: RatchetSyncState) -> String { } } -public enum SndConnEvent: Decodable { +public enum SndConnEvent: Decodable, Hashable { case switchQueue(phase: SwitchPhase, member: GroupMemberRef?) case ratchetSync(syncStatus: RatchetSyncState, member: GroupMemberRef?) case pqEnabled(enabled: Bool) @@ -3683,56 +4595,64 @@ public enum SndConnEvent: Decodable { } } -public enum SwitchPhase: String, Decodable { +public enum SwitchPhase: String, Decodable, Hashable { case started case confirmed case secured case completed } -public enum ChatItemTTL: Hashable, Identifiable, Comparable { +public enum ChatItemTTL: Identifiable, Comparable, Hashable { case day case week case month + case year case seconds(_ seconds: Int64) case none - public static var values: [ChatItemTTL] { [.none, .month, .week, .day] } + public static var values: [ChatItemTTL] { [.none, .year, .month, .week, .day] } public var id: Self { self } - public init(_ seconds: Int64?) { + public init(_ seconds: Int64) { switch seconds { + case 0: self = .none case 86400: self = .day case 7 * 86400: self = .week case 30 * 86400: self = .month - case let .some(n): self = .seconds(n) - case .none: self = .none + case 365 * 86400: self = .year + default: self = .seconds(seconds) } } - public var deleteAfterText: LocalizedStringKey { + public var deleteAfterText: String { switch self { - case .day: return "1 day" - case .week: return "1 week" - case .month: return "1 month" - case let .seconds(seconds): return "\(seconds) second(s)" - case .none: return "never" + case .day: return NSLocalizedString("1 day", comment: "delete after time") + case .week: return NSLocalizedString("1 week", comment: "delete after time") + case .month: return NSLocalizedString("1 month", comment: "delete after time") + case .year: return NSLocalizedString("1 year", comment: "delete after time") + case let .seconds(seconds): return String.localizedStringWithFormat(NSLocalizedString("%d seconds(s)", comment: "delete after time"), seconds) + case .none: return NSLocalizedString("never", comment: "delete after time") } } - public var seconds: Int64? { + public var seconds: Int64 { switch self { case .day: return 86400 case .week: return 7 * 86400 case .month: return 30 * 86400 + case .year: return 365 * 86400 case let .seconds(seconds): return seconds - case .none: return nil + case .none: return 0 } } private var comparisonValue: Int64 { - self.seconds ?? Int64.max + if self.seconds == 0 { + return Int64.max + } else { + return self.seconds + } } public static func < (lhs: Self, rhs: Self) -> Bool { @@ -3740,12 +4660,64 @@ public enum ChatItemTTL: Hashable, Identifiable, Comparable { } } -public struct ChatItemInfo: Decodable { - public var itemVersions: [ChatItemVersion] - public var memberDeliveryStatuses: [MemberDeliveryStatus]? +public enum ChatTTL: Identifiable, Hashable { + case userDefault(ChatItemTTL) + case chat(ChatItemTTL) + + public var id: Self { self } + + public var text: String { + switch self { + case let .chat(ttl): return ttl.deleteAfterText + case let .userDefault(ttl): return String.localizedStringWithFormat( + NSLocalizedString("default (%@)", comment: "delete after time"), + ttl.deleteAfterText) + } + } + + public var neverExpires: Bool { + switch self { + case let .chat(ttl): return ttl.seconds == 0 + case let .userDefault(ttl): return ttl.seconds == 0 + } + } + + public var value: Int64? { + switch self { + case let .chat(ttl): return ttl.seconds + case .userDefault: return nil + } + } + + public var usingDefault: Bool { + switch self { + case .userDefault: return true + case .chat: return false + } + } } -public struct ChatItemVersion: Decodable { +public struct ChatTag: Decodable, Hashable { + public var chatTagId: Int64 + public var chatTagText: String + public var chatTagEmoji: String? + + public var id: Int64 { chatTagId } + + public init(chatTagId: Int64, chatTagText: String, chatTagEmoji: String?) { + self.chatTagId = chatTagId + self.chatTagText = chatTagText + self.chatTagEmoji = chatTagEmoji + } +} + +public struct ChatItemInfo: Decodable, Hashable { + public var itemVersions: [ChatItemVersion] + public var memberDeliveryStatuses: [MemberDeliveryStatus]? + public var forwardedFromChatItem: AChatItem? +} + +public struct ChatItemVersion: Decodable, Hashable { public var chatItemVersionId: Int64 public var msgContent: MsgContent public var formattedText: [FormattedText]? @@ -3753,7 +4725,8 @@ public struct ChatItemVersion: Decodable { public var createdAt: Date } -public struct MemberDeliveryStatus: Decodable { +public struct MemberDeliveryStatus: Decodable, Hashable { public var groupMemberId: Int64 - public var memberDeliveryStatus: CIStatus + public var memberDeliveryStatus: GroupSndStatus + public var sentViaProxy: Bool? } diff --git a/apps/ios/SimpleXChat/ChatUtils.swift b/apps/ios/SimpleXChat/ChatUtils.swift new file mode 100644 index 0000000000..6cbc76ec98 --- /dev/null +++ b/apps/ios/SimpleXChat/ChatUtils.swift @@ -0,0 +1,119 @@ +// +// ChatUtils.swift +// SimpleXChat +// +// Created by Levitating Pineapple on 15/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation + +public protocol ChatLike { + var chatInfo: ChatInfo { get} + var chatItems: [ChatItem] { get } + var chatStats: ChatStats { get } +} + +extension ChatLike { + public func groupFeatureEnabled(_ feature: GroupFeature) -> Bool { + if case let .group(groupInfo) = self.chatInfo { + let p = groupInfo.fullGroupPreferences + return switch feature { + case .timedMessages: p.timedMessages.on + case .directMessages: p.directMessages.on(for: groupInfo.membership) + case .fullDelete: p.fullDelete.on + case .reactions: p.reactions.on + case .voice: p.voice.on(for: groupInfo.membership) + case .files: p.files.on(for: groupInfo.membership) + case .simplexLinks: p.simplexLinks.on(for: groupInfo.membership) + case .history: p.history.on + case .reports: p.reports.on + } + } else { + return true + } + } + + public func prohibitedByPref( + hasSimplexLink: Bool, + isMediaOrFileAttachment: Bool, + isVoice: Bool + ) -> Bool { + // preference checks should match checks in compose view + let simplexLinkProhibited = hasSimplexLink && !groupFeatureEnabled(.simplexLinks) + let fileProhibited = isMediaOrFileAttachment && !groupFeatureEnabled(.files) + let voiceProhibited = isVoice && !chatInfo.featureEnabled(.voice) + return switch chatInfo { + case .direct: voiceProhibited + case .group: simplexLinkProhibited || fileProhibited || voiceProhibited + case .local: false + case .contactRequest: false + case .contactConnection: false + case .invalidJSON: false + } + } +} + +public func filterChatsToForwardTo(chats: [C]) -> [C] { + var filteredChats = chats.filter { c in + c.chatInfo.chatType != .local && canForwardToChat(c.chatInfo) + } + if let privateNotes = chats.first(where: { $0.chatInfo.chatType == .local }) { + filteredChats.insert(privateNotes, at: 0) + } + return filteredChats +} + +public func foundChat(_ chat: ChatLike, _ searchStr: String) -> Bool { + let cInfo = chat.chatInfo + return switch cInfo { + case let .direct(contact): + viewNameContains(cInfo, searchStr) || + contact.profile.displayName.localizedLowercase.contains(searchStr) || + contact.fullName.localizedLowercase.contains(searchStr) + default: + viewNameContains(cInfo, searchStr) + } + + func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool { + cInfo.chatViewName.localizedLowercase.contains(s) + } +} + +private func canForwardToChat(_ cInfo: ChatInfo) -> Bool { + switch cInfo { + case let .direct(contact): contact.sendMsgEnabled && !contact.nextSendGrpInv + case let .group(groupInfo): groupInfo.sendMsgEnabled + case let .local(noteFolder): noteFolder.sendMsgEnabled + case .contactRequest: false + case .contactConnection: false + case .invalidJSON: false + } +} + +public func chatIconName(_ cInfo: ChatInfo) -> String { + switch cInfo { + case .direct: "person.crop.circle.fill" + case let .group(groupInfo): + switch groupInfo.businessChat?.chatType { + case .none: "person.2.circle.fill" + case .business: "briefcase.circle.fill" + case .customer: "person.crop.circle.fill" + } + case .local: "folder.circle.fill" + case .contactRequest: "person.crop.circle.fill" + default: "circle.fill" + } +} + +public func hasSimplexLink(_ text: String?) -> Bool { + if let text, let parsedMsg = parseSimpleXMarkdown(text) { + parsedMsgHasSimplexLink(parsedMsg) + } else { + false + } +} + +public func parsedMsgHasSimplexLink(_ parsedMsg: [FormattedText]) -> Bool { + parsedMsg.contains(where: { ft in ft.format?.isSimplexLink ?? false }) +} diff --git a/apps/ios/SimpleXChat/CryptoFile.swift b/apps/ios/SimpleXChat/CryptoFile.swift index 0e539ba97c..dfe833f832 100644 --- a/apps/ios/SimpleXChat/CryptoFile.swift +++ b/apps/ios/SimpleXChat/CryptoFile.swift @@ -18,10 +18,10 @@ public func writeCryptoFile(path: String, data: Data) throws -> CryptoFileArgs { memcpy(ptr, (data as NSData).bytes, data.count) var cPath = path.cString(using: .utf8)! let cjson = chat_write_file(getChatCtrl(), &cPath, ptr, Int32(data.count))! - let d = fromCString(cjson).data(using: .utf8)! + let d = dataFromCString(cjson)! // TODO [unsafe] switch try jsonDecoder.decode(WriteFileResult.self, from: d) { case let .result(cfArgs): return cfArgs - case let .error(err): throw RuntimeError(err) + case let .error(err): throw RuntimeError(err) // TODO [unsafe] } } @@ -51,10 +51,10 @@ public func encryptCryptoFile(fromPath: String, toPath: String) throws -> Crypto var cFromPath = fromPath.cString(using: .utf8)! var cToPath = toPath.cString(using: .utf8)! let cjson = chat_encrypt_file(getChatCtrl(), &cFromPath, &cToPath)! - let d = fromCString(cjson).data(using: .utf8)! + let d = dataFromCString(cjson)! // TODO [unsafe] switch try jsonDecoder.decode(WriteFileResult.self, from: d) { case let .result(cfArgs): return cfArgs - case let .error(err): throw RuntimeError(err) + case let .error(err): throw RuntimeError(err) // TODO [unsafe] } } diff --git a/apps/ios/SimpleXChat/ErrorAlert.swift b/apps/ios/SimpleXChat/ErrorAlert.swift new file mode 100644 index 0000000000..a433d2313b --- /dev/null +++ b/apps/ios/SimpleXChat/ErrorAlert.swift @@ -0,0 +1,154 @@ +// +// ErrorAlert.swift +// SimpleXChat +// +// Created by Levitating Pineapple on 20/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +public struct ErrorAlert: Error { + public let title: LocalizedStringKey + public let message: LocalizedStringKey? + public let actions: Optional<() -> AnyView> + + public init( + title: LocalizedStringKey, + message: LocalizedStringKey? = nil + ) { + self.title = title + self.message = message + self.actions = nil + } + + public init( + title: LocalizedStringKey, + message: LocalizedStringKey? = nil, + @ViewBuilder actions: @escaping () -> A + ) { + self.title = title + self.message = message + self.actions = { AnyView(actions()) } + } + + public init(_ title: LocalizedStringKey) { + self = ErrorAlert(title: title) + } + + public init(_ error: any Error) { + self = if let e = error as? ChatError { + ErrorAlert(e) + } else { + ErrorAlert("\(error.localizedDescription)") + } + } + + public init(_ chatError: ChatError) { + self = if let networkErrorAlert = getNetworkErrorAlert(chatError) { + networkErrorAlert + } else { + ErrorAlert("\(chatErrorString(chatError))") + } + } +} + +extension LocalizedStringKey: @unchecked Sendable { } + +extension View { + /// Bridges ``ErrorAlert`` to the generic alert API. + /// - Parameters: + /// - errorAlert: Binding to the Error, which is rendered in the alert + /// - actions: View Builder containing action buttons. + /// System defaults to `Ok` dismiss error action, when no actions are provided. + /// System implicitly adds `Cancel` action, if a destructive action is present + /// + /// - Returns: View, which displays ErrorAlert?, when set. + @ViewBuilder public func alert( + _ errorAlert: Binding, + @ViewBuilder actions: (ErrorAlert) -> A = { _ in EmptyView() } + ) -> some View { + alert( + errorAlert.wrappedValue?.title ?? "", + isPresented: Binding( + get: { errorAlert.wrappedValue != nil }, + set: { if !$0 { errorAlert.wrappedValue = nil } } + ), + actions: { + if let actions_ = errorAlert.wrappedValue?.actions { + actions_() + } else { + if let alert = errorAlert.wrappedValue { actions(alert) } + } + }, + message: { + if let message = errorAlert.wrappedValue?.message { + Text(message) + } + } + ) + } +} + +public func getNetworkErrorAlert(_ e: ChatError) -> ErrorAlert? { + switch e { + case let .errorAgent(.BROKER(addr, .TIMEOUT)): + ErrorAlert(title: "Connection timeout", message: "Please check your network connection with \(serverHostname(addr)) and try again.") + case let .errorAgent(.BROKER(addr, .NETWORK)): + ErrorAlert(title: "Connection error", message: "Please check your network connection with \(serverHostname(addr)) and try again.") + case let .errorAgent(.BROKER(addr, .HOST)): + ErrorAlert(title: "Connection error", message: "Server address is incompatible with network settings: \(serverHostname(addr)).") + case let .errorAgent(.BROKER(addr, .TRANSPORT(.version))): + ErrorAlert(title: "Connection error", message: "Server version is incompatible with your app: \(serverHostname(addr)).") + case let .errorAgent(.SMP(serverAddress, .PROXY(proxyErr))): + smpProxyErrorAlert(proxyErr, serverAddress) + case let .errorAgent(.PROXY(proxyServer, relayServer, .protocolError(.PROXY(proxyErr)))): + proxyDestinationErrorAlert(proxyErr, proxyServer, relayServer) + default: nil + } +} + +private func smpProxyErrorAlert(_ proxyErr: ProxyError, _ srvAddr: String) -> ErrorAlert? { + switch proxyErr { + case .BROKER(brokerErr: .TIMEOUT): + return ErrorAlert(title: "Private routing error", message: "Error connecting to forwarding server \(serverHostname(srvAddr)). Please try later.") + case .BROKER(brokerErr: .NETWORK): + return ErrorAlert(title: "Private routing error", message: "Error connecting to forwarding server \(serverHostname(srvAddr)). Please try later.") + case .BROKER(brokerErr: .HOST): + return ErrorAlert(title: "Private routing error", message: "Forwarding server address is incompatible with network settings: \(serverHostname(srvAddr)).") + case .BROKER(brokerErr: .TRANSPORT(.version)): + return ErrorAlert(title: "Private routing error", message: "Forwarding server version is incompatible with network settings: \(serverHostname(srvAddr)).") + default: + return nil + } +} + +private func proxyDestinationErrorAlert(_ proxyErr: ProxyError, _ proxyServer: String, _ relayServer: String) -> ErrorAlert? { + switch proxyErr { + case .BROKER(brokerErr: .TIMEOUT): + return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.") + case .BROKER(brokerErr: .NETWORK): + return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.") + case .NO_SESSION: + return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.") + case .BROKER(brokerErr: .HOST): + return ErrorAlert(title: "Private routing error", message: "Destination server address of \(serverHostname(relayServer)) is incompatible with forwarding server \(serverHostname(proxyServer)) settings.") + case .BROKER(brokerErr: .TRANSPORT(.version)): + return ErrorAlert(title: "Private routing error", message: "Destination server version of \(serverHostname(relayServer)) is incompatible with forwarding server \(serverHostname(proxyServer)).") + default: + return nil + } +} + +public func serverHostname(_ srv: String) -> String { + parseServerAddress(srv)?.hostnames.first ?? srv +} + +public func mtrErrorDescription(_ err: MTRError) -> LocalizedStringKey { + switch err { + case let .noDown(dbMigrations): + "database version is newer than the app, but no down migration for: \(dbMigrations.joined(separator: ", "))" + case let .different(appMigration, dbMigration): + "different migration in the app/database: \(appMigration) / \(dbMigration)" + } +} diff --git a/apps/ios/SimpleXChat/FileUtils.swift b/apps/ios/SimpleXChat/FileUtils.swift index 125600f3f3..2341eb4a4f 100644 --- a/apps/ios/SimpleXChat/FileUtils.swift +++ b/apps/ios/SimpleXChat/FileUtils.swift @@ -8,6 +8,7 @@ import Foundation import OSLog +import UIKit let logger = Logger() @@ -40,7 +41,7 @@ public func getDocumentsDirectory() -> URL { FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! } -func getGroupContainerDirectory() -> URL { +public func getGroupContainerDirectory() -> URL { FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: APP_GROUP_NAME)! } @@ -85,6 +86,8 @@ public func deleteAppDatabaseAndFiles() { try? fm.removeItem(at: getTempFilesDirectory()) try? fm.removeItem(at: getMigrationTempFilesDirectory()) try? fm.createDirectory(at: getTempFilesDirectory(), withIntermediateDirectories: true) + try? fm.removeItem(at: getWallpaperDirectory()) + try? fm.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true) deleteAppFiles() _ = kcDatabasePassword.remove() storeDBPassphraseGroupDefault.set(true) @@ -196,6 +199,14 @@ public func getAppFilePath(_ fileName: String) -> URL { getAppFilesDirectory().appendingPathComponent(fileName) } +public func getWallpaperDirectory() -> URL { + getAppDirectory().appendingPathComponent("assets", isDirectory: true).appendingPathComponent("wallpapers", isDirectory: true) +} + +public func getWallpaperFilePath(_ filename: String) -> URL { + getWallpaperDirectory().appendingPathComponent(filename) +} + public func saveFile(_ data: Data, _ fileName: String, encrypted: Bool) -> CryptoFile? { let filePath = getAppFilePath(fileName) do { diff --git a/apps/ios/Shared/Model/ImageUtils.swift b/apps/ios/SimpleXChat/ImageUtils.swift similarity index 56% rename from apps/ios/Shared/Model/ImageUtils.swift rename to apps/ios/SimpleXChat/ImageUtils.swift index 6437597b19..be43158bc1 100644 --- a/apps/ios/Shared/Model/ImageUtils.swift +++ b/apps/ios/SimpleXChat/ImageUtils.swift @@ -7,18 +7,19 @@ // import Foundation -import SimpleXChat import SwiftUI import AVKit +import SwiftyGif +import LinkPresentation -func getLoadedFileSource(_ file: CIFile?) -> CryptoFile? { +public func getLoadedFileSource(_ file: CIFile?) -> CryptoFile? { if let file = file, file.loaded { return file.fileSource } return nil } -func getLoadedImage(_ file: CIFile?) -> UIImage? { +public func getLoadedImage(_ file: CIFile?) -> UIImage? { if let fileSource = getLoadedFileSource(file) { let filePath = getAppFilePath(fileSource.filePath) do { @@ -37,7 +38,7 @@ func getLoadedImage(_ file: CIFile?) -> UIImage? { return nil } -func getFileData(_ path: URL, _ cfArgs: CryptoFileArgs?) throws -> Data { +public func getFileData(_ path: URL, _ cfArgs: CryptoFileArgs?) throws -> Data { if let cfArgs = cfArgs { return try readCryptoFile(path: path.path, cryptoArgs: cfArgs) } else { @@ -45,7 +46,7 @@ func getFileData(_ path: URL, _ cfArgs: CryptoFileArgs?) throws -> Data { } } -func getLoadedVideo(_ file: CIFile?) -> URL? { +public func getLoadedVideo(_ file: CIFile?) -> URL? { if let fileSource = getLoadedFileSource(file) { let filePath = getAppFilePath(fileSource.filePath) if FileManager.default.fileExists(atPath: filePath.path) { @@ -55,13 +56,13 @@ func getLoadedVideo(_ file: CIFile?) -> URL? { return nil } -func saveAnimImage(_ image: UIImage) -> CryptoFile? { +public func saveAnimImage(_ image: UIImage) -> CryptoFile? { let fileName = generateNewFileName("IMG", "gif") guard let imageData = image.imageData else { return nil } return saveFile(imageData, fileName, encrypted: privacyEncryptLocalFilesGroupDefault.get()) } -func saveImage(_ uiImage: UIImage) -> CryptoFile? { +public func saveImage(_ uiImage: UIImage) -> CryptoFile? { let hasAlpha = imageHasAlpha(uiImage) let ext = hasAlpha ? "png" : "jpg" if let imageDataResized = resizeImageToDataSize(uiImage, maxDataSize: MAX_IMAGE_SIZE, hasAlpha: hasAlpha) { @@ -71,7 +72,7 @@ func saveImage(_ uiImage: UIImage) -> CryptoFile? { return nil } -func cropToSquare(_ image: UIImage) -> UIImage { +public func cropToSquare(_ image: UIImage) -> UIImage { let size = image.size let side = min(size.width, size.height) let newSize = CGSize(width: side, height: side) @@ -84,7 +85,7 @@ func cropToSquare(_ image: UIImage) -> UIImage { return resizeImage(image, newBounds: CGRect(origin: .zero, size: newSize), drawIn: CGRect(origin: origin, size: size), hasAlpha: imageHasAlpha(image)) } -func resizeImageToDataSize(_ image: UIImage, maxDataSize: Int64, hasAlpha: Bool) -> Data? { +public func resizeImageToDataSize(_ image: UIImage, maxDataSize: Int64, hasAlpha: Bool) -> Data? { var img = image var data = hasAlpha ? img.pngData() : img.jpegData(compressionQuality: 0.85) var dataSize = data?.count ?? 0 @@ -99,7 +100,7 @@ func resizeImageToDataSize(_ image: UIImage, maxDataSize: Int64, hasAlpha: Bool) return data } -func resizeImageToStrSize(_ image: UIImage, maxDataSize: Int64) -> String? { +public func resizeImageToStrSizeSync(_ image: UIImage, maxDataSize: Int64) -> String? { var img = image let hasAlpha = imageHasAlpha(image) var str = compressImageStr(img, hasAlpha: hasAlpha) @@ -115,7 +116,15 @@ func resizeImageToStrSize(_ image: UIImage, maxDataSize: Int64) -> String? { return str } -func compressImageStr(_ image: UIImage, _ compressionQuality: CGFloat = 0.85, hasAlpha: Bool) -> String? { +public func resizeImageToStrSize(_ image: UIImage, maxDataSize: Int64) async -> String? { + resizeImageToStrSizeSync(image, maxDataSize: maxDataSize) +} + +public func compressImageStr(_ image: UIImage, _ compressionQuality: CGFloat = 0.85, hasAlpha: Bool) -> String? { +// // Heavy workload to verify if UI gets blocked by the call +// for i in 0..<100 { +// print(image.jpegData(compressionQuality: Double(i) / 100)?.count ?? 0, terminator: ", ") +// } let ext = hasAlpha ? "png" : "jpg" if let data = hasAlpha ? image.pngData() : image.jpegData(compressionQuality: compressionQuality) { return "data:image/\(ext);base64,\(data.base64EncodedString())" @@ -129,7 +138,7 @@ private func reduceSize(_ image: UIImage, ratio: CGFloat, hasAlpha: Bool) -> UII return resizeImage(image, newBounds: bounds, drawIn: bounds, hasAlpha: hasAlpha) } -private func resizeImage(_ image: UIImage, newBounds: CGRect, drawIn: CGRect, hasAlpha: Bool) -> UIImage { +public func resizeImage(_ image: UIImage, newBounds: CGRect, drawIn: CGRect, hasAlpha: Bool) -> UIImage { let format = UIGraphicsImageRendererFormat() format.scale = 1.0 format.opaque = !hasAlpha @@ -138,7 +147,7 @@ private func resizeImage(_ image: UIImage, newBounds: CGRect, drawIn: CGRect, ha } } -func imageHasAlpha(_ img: UIImage) -> Bool { +public func imageHasAlpha(_ img: UIImage) -> Bool { if let cgImage = img.cgImage { let colorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue) @@ -158,7 +167,35 @@ func imageHasAlpha(_ img: UIImage) -> Bool { return false } -func saveFileFromURL(_ url: URL) -> CryptoFile? { +/// Reduces image size, while consuming less RAM +/// +/// Used by ShareExtension to downsize large images +/// before passing them to regular image processing pipeline +/// to avoid exceeding 120MB memory +/// +/// - Parameters: +/// - url: Location of the image data +/// - size: Maximum dimension (width or height) +/// - Returns: Downsampled image or `nil`, if the image can't be located +public func downsampleImage(at url: URL, to size: Int64) -> UIImage? { + autoreleasepool { + if let source = CGImageSourceCreateWithURL(url as CFURL, nil) { + CGImageSourceCreateThumbnailAtIndex( + source, + 0, + [ + kCGImageSourceCreateThumbnailFromImageAlways: true, + kCGImageSourceShouldCacheImmediately: true, + kCGImageSourceCreateThumbnailWithTransform: true, + kCGImageSourceThumbnailMaxPixelSize: String(size) as CFString + ] as CFDictionary + ) + .map { UIImage(cgImage: $0) } + } else { nil } + } +} + +public func saveFileFromURL(_ url: URL) -> CryptoFile? { let encrypted = privacyEncryptLocalFilesGroupDefault.get() let savedFile: CryptoFile? if url.startAccessingSecurityScopedResource() { @@ -184,7 +221,7 @@ func saveFileFromURL(_ url: URL) -> CryptoFile? { return savedFile } -func moveTempFileFromURL(_ url: URL) -> CryptoFile? { +public func moveTempFileFromURL(_ url: URL) -> CryptoFile? { do { let encrypted = privacyEncryptLocalFilesGroupDefault.get() let fileName = uniqueCombine(url.lastPathComponent) @@ -197,7 +234,6 @@ func moveTempFileFromURL(_ url: URL) -> CryptoFile? { try FileManager.default.moveItem(at: url, to: getAppFilePath(fileName)) savedFile = CryptoFile.plain(fileName) } - ChatModel.shared.filesToDelete.remove(url) return savedFile } catch { logger.error("ImageUtils.moveTempFileFromURL error: \(error.localizedDescription)") @@ -205,7 +241,53 @@ func moveTempFileFromURL(_ url: URL) -> CryptoFile? { } } -func generateNewFileName(_ prefix: String, _ ext: String, fullPath: Bool = false) -> String { +public func saveWallpaperFile(url: URL) -> String? { + let destFile = URL(fileURLWithPath: generateNewFileName(getWallpaperDirectory().path + "/" + "wallpaper", "jpg", fullPath: true)) + do { + try FileManager.default.copyItem(atPath: url.path, toPath: destFile.path) + return destFile.lastPathComponent + } catch { + logger.error("FileUtils.saveWallpaperFile error: \(error.localizedDescription)") + return nil + } +} + +public func saveWallpaperFile(image: UIImage) -> String? { + let hasAlpha = imageHasAlpha(image) + let destFile = URL(fileURLWithPath: generateNewFileName(getWallpaperDirectory().path + "/" + "wallpaper", hasAlpha ? "png" : "jpg", fullPath: true)) + let dataResized = resizeImageToDataSize(image, maxDataSize: 5_000_000, hasAlpha: hasAlpha) + do { + try dataResized!.write(to: destFile) + return destFile.lastPathComponent + } catch { + logger.error("FileUtils.saveWallpaperFile error: \(error.localizedDescription)") + return nil + } +} + +public func removeWallpaperFile(fileName: String? = nil) { + do { + try FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: getWallpaperDirectory().path), includingPropertiesForKeys: nil, options: []).forEach { url in + if url.lastPathComponent == fileName { + try FileManager.default.removeItem(at: url) + } + } + } catch { + logger.error("FileUtils.removeWallpaperFile error: \(error)") + } + if let fileName { + WallpaperType.cachedImages.removeValue(forKey: fileName) + } +} + +public func removeWallpaperFilesFromTheme(_ theme: ThemeModeOverrides?) { + if let theme { + removeWallpaperFile(fileName: theme.light?.wallpaper?.imageFile) + removeWallpaperFile(fileName: theme.dark?.wallpaper?.imageFile) + } +} + +public func generateNewFileName(_ prefix: String, _ ext: String, fullPath: Bool = false) -> String { uniqueCombine("\(prefix)_\(getTimestamp()).\(ext)", fullPath: fullPath) } @@ -237,7 +319,7 @@ private func getTimestamp() -> String { return df.string(from: Date()) } -func dropImagePrefix(_ s: String) -> String { +public func dropImagePrefix(_ s: String) -> String { dropPrefix(dropPrefix(s, "data:image/png;base64,"), "data:image/jpg;base64,") } @@ -245,8 +327,23 @@ private func dropPrefix(_ s: String, _ prefix: String) -> String { s.hasPrefix(prefix) ? String(s.dropFirst(prefix.count)) : s } +public func makeVideoQualityLower(_ input: URL, outputUrl: URL) async -> Bool { + let asset: AVURLAsset = AVURLAsset(url: input, options: nil) + if let s = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset640x480) { + s.outputURL = outputUrl + s.outputFileType = .mp4 + s.metadataItemFilter = AVMetadataItemFilter.forSharing() + await s.export() + if let err = s.error { + logger.error("Failed to export video with error: \(err)") + } + return s.status == .completed + } + return false +} + extension AVAsset { - func generatePreview() -> (UIImage, Int)? { + public func generatePreview() -> (UIImage, Int)? { let generator = AVAssetImageGenerator(asset: self) generator.appliesPreferredTrackTransform = true var actualTime = CMTimeMake(value: 0, timescale: 0) @@ -258,7 +355,7 @@ extension AVAsset { } extension UIImage { - func replaceColor(_ from: UIColor, _ to: UIColor) -> UIImage { + public func replaceColor(_ from: UIColor, _ to: UIColor) -> UIImage { if let cgImage = cgImage { let colorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue) @@ -304,3 +401,92 @@ extension UIImage { return self } } + +public func imageFromBase64(_ base64Encoded: String?) -> UIImage? { + if let base64Encoded { + if let img = imageCache.object(forKey: base64Encoded as NSString) { + return img + } else if let data = Data(base64Encoded: dropImagePrefix(base64Encoded)), + let img = UIImage(data: data) { + imageCacheQueue.async { + imageCache.setObject(img, forKey: base64Encoded as NSString) + } + return img + } else { + return nil + } + } else { + return nil + } +} + +private let imageCacheQueue = DispatchQueue.global(qos: .background) + +private var imageCache: NSCache = { + var cache = NSCache() + cache.countLimit = 1000 + return cache +}() + +public func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) { + logger.debug("getLinkMetadata: fetching URL preview") + LPMetadataProvider().startFetchingMetadata(for: url){ metadata, error in + if let e = error { + logger.error("Error retrieving link metadata: \(e.localizedDescription)") + } + if let metadata = metadata, + let imageProvider = metadata.imageProvider, + imageProvider.canLoadObject(ofClass: UIImage.self) { + imageProvider.loadObject(ofClass: UIImage.self){ object, error in + var linkPreview: LinkPreview? = nil + if let error = error { + logger.error("Couldn't load image preview from link metadata with error: \(error.localizedDescription)") + } else { + if let image = object as? UIImage, + let resized = resizeImageToStrSizeSync(image, maxDataSize: 14000), + let title = metadata.title, + let uri = metadata.originalURL { + linkPreview = LinkPreview(uri: uri, title: title, image: resized) + } + } + cb(linkPreview) + } + } else { + logger.error("Could not load link preview image") + cb(nil) + } + } +} + +public func getLinkPreview(for url: URL) async -> LinkPreview? { + await withCheckedContinuation { cont in + getLinkPreview(url: url) { cont.resume(returning: $0) } + } +} + +private let squareToCircleRatio = 0.935 + +private let radiusFactor = (1 - squareToCircleRatio) / 50 + +@ViewBuilder public func clipProfileImage(_ img: Image, size: CGFloat, radius: Double, blurred: Bool = false) -> some View { + if radius >= 50 { + blurredFrame(img, size, blurred).clipShape(Circle()) + } else if radius <= 0 { + let sz = size * squareToCircleRatio + blurredFrame(img, sz, blurred).padding((size - sz) / 2) + } else { + let sz = size * (squareToCircleRatio + radius * radiusFactor) + blurredFrame(img, sz, blurred) + .clipShape(RoundedRectangle(cornerRadius: sz * radius / 100, style: .continuous)) + .padding((size - sz) / 2) + } +} + +@ViewBuilder private func blurredFrame(_ img: Image, _ size: CGFloat, _ blurred: Bool) -> some View { + let v = img.resizable().frame(width: size, height: size) + if blurred { + v.blur(radius: size / 4) + } else { + v + } +} diff --git a/apps/ios/SimpleXChat/Notifications.swift b/apps/ios/SimpleXChat/Notifications.swift index bc959cb34b..5579449caa 100644 --- a/apps/ios/SimpleXChat/Notifications.swift +++ b/apps/ios/SimpleXChat/Notifications.swift @@ -15,13 +15,14 @@ public let ntfCategoryContactConnected = "NTF_CAT_CONTACT_CONNECTED" public let ntfCategoryMessageReceived = "NTF_CAT_MESSAGE_RECEIVED" public let ntfCategoryCallInvitation = "NTF_CAT_CALL_INVITATION" public let ntfCategoryConnectionEvent = "NTF_CAT_CONNECTION_EVENT" +public let ntfCategoryManyEvents = "NTF_CAT_MANY_EVENTS" public let ntfCategoryCheckMessage = "NTF_CAT_CHECK_MESSAGE" public let appNotificationId = "chat.simplex.app.notification" let contactHidden = NSLocalizedString("Contact hidden:", comment: "notification") -public func createContactRequestNtf(_ user: any UserLike, _ contactRequest: UserContactRequest) -> UNMutableNotificationContent { +public func createContactRequestNtf(_ user: any UserLike, _ contactRequest: UserContactRequest, _ badgeCount: Int) -> UNMutableNotificationContent { let hideContent = ntfPreviewModeGroupDefault.get() == .hidden return createNotification( categoryIdentifier: ntfCategoryContactRequest, @@ -34,11 +35,12 @@ public func createContactRequestNtf(_ user: any UserLike, _ contactRequest: User hideContent ? NSLocalizedString("this contact", comment: "notification title") : contactRequest.chatViewName ), targetContentIdentifier: nil, - userInfo: ["chatId": contactRequest.id, "contactRequestId": contactRequest.apiId, "userId": user.userId] + userInfo: ["chatId": contactRequest.id, "contactRequestId": contactRequest.apiId, "userId": user.userId], + badgeCount: badgeCount ) } -public func createContactConnectedNtf(_ user: any UserLike, _ contact: Contact) -> UNMutableNotificationContent { +public func createContactConnectedNtf(_ user: any UserLike, _ contact: Contact, _ badgeCount: Int) -> UNMutableNotificationContent { let hideContent = ntfPreviewModeGroupDefault.get() == .hidden return createNotification( categoryIdentifier: ntfCategoryContactConnected, @@ -47,16 +49,17 @@ public func createContactConnectedNtf(_ user: any UserLike, _ contact: Contact) hideContent ? NSLocalizedString("A new contact", comment: "notification title") : contact.displayName ), body: String.localizedStringWithFormat( - NSLocalizedString("You can now send messages to %@", comment: "notification body"), + NSLocalizedString("You can now chat with %@", comment: "notification body"), hideContent ? NSLocalizedString("this contact", comment: "notification title") : contact.chatViewName ), targetContentIdentifier: contact.id, - userInfo: ["userId": user.userId] + userInfo: ["userId": user.userId], // userInfo: ["chatId": contact.id, "contactId": contact.apiId] + badgeCount: badgeCount ) } -public func createMessageReceivedNtf(_ user: any UserLike, _ cInfo: ChatInfo, _ cItem: ChatItem) -> UNMutableNotificationContent { +public func createMessageReceivedNtf(_ user: any UserLike, _ cInfo: ChatInfo, _ cItem: ChatItem, _ badgeCount: Int) -> UNMutableNotificationContent { let previewMode = ntfPreviewModeGroupDefault.get() var title: String if case let .group(groupInfo) = cInfo, case let .groupRcv(groupMember) = cItem.chatDir { @@ -69,12 +72,13 @@ public func createMessageReceivedNtf(_ user: any UserLike, _ cInfo: ChatInfo, _ title: title, body: previewMode == .message ? hideSecrets(cItem) : NSLocalizedString("new message", comment: "notification"), targetContentIdentifier: cInfo.id, - userInfo: ["userId": user.userId] + userInfo: ["userId": user.userId], // userInfo: ["chatId": cInfo.id, "chatItemId": cItem.id] + badgeCount: badgeCount ) } -public func createCallInvitationNtf(_ invitation: RcvCallInvitation) -> UNMutableNotificationContent { +public func createCallInvitationNtf(_ invitation: RcvCallInvitation, _ badgeCount: Int) -> UNMutableNotificationContent { let text = invitation.callType.media == .video ? NSLocalizedString("Incoming video call", comment: "notification") : NSLocalizedString("Incoming audio call", comment: "notification") @@ -84,17 +88,18 @@ public func createCallInvitationNtf(_ invitation: RcvCallInvitation) -> UNMutabl title: hideContent ? contactHidden : "\(invitation.contact.chatViewName):", body: text, targetContentIdentifier: nil, - userInfo: ["chatId": invitation.contact.id, "userId": invitation.user.userId] + userInfo: ["chatId": invitation.contact.id, "userId": invitation.user.userId], + badgeCount: badgeCount ) } -public func createConnectionEventNtf(_ user: User, _ connEntity: ConnectionEntity) -> UNMutableNotificationContent { +public func createConnectionEventNtf(_ user: User, _ connEntity: ConnectionEntity, _ badgeCount: Int) -> UNMutableNotificationContent { let hideContent = ntfPreviewModeGroupDefault.get() == .hidden var title: String var body: String? = nil var targetContentIdentifier: String? = nil switch connEntity { - case let .rcvDirectMsgConnection(contact): + case let .rcvDirectMsgConnection(_, contact): if let contact = contact { title = hideContent ? contactHidden : "\(contact.chatViewName):" targetContentIdentifier = contact.id @@ -102,7 +107,7 @@ public func createConnectionEventNtf(_ user: User, _ connEntity: ConnectionEntit title = NSLocalizedString("New contact:", comment: "notification") } body = NSLocalizedString("message received", comment: "notification") - case let .rcvGroupMsgConnection(groupInfo, groupMember): + case let .rcvGroupMsgConnection(_, groupInfo, groupMember): title = groupMsgNtfTitle(groupInfo, groupMember, hideContent: hideContent) body = NSLocalizedString("message received", comment: "notification") targetContentIdentifier = groupInfo.id @@ -118,11 +123,12 @@ public func createConnectionEventNtf(_ user: User, _ connEntity: ConnectionEntit title: title, body: body, targetContentIdentifier: targetContentIdentifier, - userInfo: ["userId": user.userId] + userInfo: ["userId": user.userId], + badgeCount: badgeCount ) } -public func createErrorNtf(_ dbStatus: DBMigrationResult) -> UNMutableNotificationContent { +public func createErrorNtf(_ dbStatus: DBMigrationResult, _ badgeCount: Int) -> UNMutableNotificationContent { var title: String switch dbStatus { case .errorNotADatabase: @@ -142,14 +148,16 @@ public func createErrorNtf(_ dbStatus: DBMigrationResult) -> UNMutableNotificati } return createNotification( categoryIdentifier: ntfCategoryConnectionEvent, - title: title + title: title, + badgeCount: badgeCount ) } -public func createAppStoppedNtf() -> UNMutableNotificationContent { +public func createAppStoppedNtf(_ badgeCount: Int) -> UNMutableNotificationContent { return createNotification( categoryIdentifier: ntfCategoryConnectionEvent, - title: NSLocalizedString("Encrypted message: app is stopped", comment: "notification") + title: NSLocalizedString("Encrypted message: app is stopped", comment: "notification"), + badgeCount: badgeCount ) } @@ -159,8 +167,15 @@ private func groupMsgNtfTitle(_ groupInfo: GroupInfo, _ groupMember: GroupMember : "#\(groupInfo.displayName) \(groupMember.chatViewName):" } -public func createNotification(categoryIdentifier: String, title: String, subtitle: String? = nil, body: String? = nil, - targetContentIdentifier: String? = nil, userInfo: [AnyHashable : Any] = [:]) -> UNMutableNotificationContent { +public func createNotification( + categoryIdentifier: String, + title: String, + subtitle: String? = nil, + body: String? = nil, + targetContentIdentifier: String? = nil, + userInfo: [AnyHashable : Any] = [:], + badgeCount: Int +) -> UNMutableNotificationContent { let content = UNMutableNotificationContent() content.categoryIdentifier = categoryIdentifier content.title = title @@ -170,6 +185,7 @@ public func createNotification(categoryIdentifier: String, title: String, subtit content.userInfo = userInfo // TODO move logic of adding sound here, so it applies to background notifications too content.sound = .default + content.badge = badgeCount as NSNumber // content.interruptionLevel = .active // content.relevanceScore = 0.5 // 0-1 return content @@ -187,6 +203,11 @@ func hideSecrets(_ cItem: ChatItem) -> String { } return res } else { - return cItem.text + let mc = cItem.content.msgContent + if case let .report(text, reason) = mc { + return String.localizedStringWithFormat(NSLocalizedString("Report: %@", comment: "report in notification"), text.isEmpty ? reason.text : text) + } else { + return cItem.text + } } } diff --git a/apps/ios/SimpleXChat/SharedFileSubscriber.swift b/apps/ios/SimpleXChat/SharedFileSubscriber.swift index f496e6999e..bf5997f40b 100644 --- a/apps/ios/SimpleXChat/SharedFileSubscriber.swift +++ b/apps/ios/SimpleXChat/SharedFileSubscriber.swift @@ -12,6 +12,8 @@ public typealias AppSubscriber = SharedFileSubscriber> +public typealias SESubscriber = SharedFileSubscriber> + public class SharedFileSubscriber: NSObject, NSFilePresenter { var fileURL: URL public var presentedItemURL: URL? @@ -57,6 +59,8 @@ let appMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent( let nseMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent("chat.simplex.app.SimpleX-NSE.messages", isDirectory: false) +let seMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent("chat.simplex.app.SimpleX-SE.messages", isDirectory: false) + public struct ProcessMessage: Codable { var createdAt: Date = Date.now var message: Message @@ -70,6 +74,10 @@ public enum NSEProcessMessage: Codable { case state(state: NSEState) } +public enum SEProcessMessage: Codable { + case state(state: SEState) +} + public func sendAppProcessMessage(_ message: AppProcessMessage) { SharedFileSubscriber.notify(url: appMessagesSharedFile, message: ProcessMessage(message: message)) } @@ -78,6 +86,10 @@ public func sendNSEProcessMessage(_ message: NSEProcessMessage) { SharedFileSubscriber.notify(url: nseMessagesSharedFile, message: ProcessMessage(message: message)) } +public func sendSEProcessMessage(_ message: SEProcessMessage) { + SharedFileSubscriber.notify(url: seMessagesSharedFile, message: ProcessMessage(message: message)) +} + public func appMessageSubscriber(onMessage: @escaping (AppProcessMessage) -> Void) -> AppSubscriber { SharedFileSubscriber(fileURL: appMessagesSharedFile) { (msg: ProcessMessage) in onMessage(msg.message) @@ -90,6 +102,12 @@ public func nseMessageSubscriber(onMessage: @escaping (NSEProcessMessage) -> Voi } } +public func seMessageSubscriber(onMessage: @escaping (SEProcessMessage) -> Void) -> SESubscriber { + SharedFileSubscriber(fileURL: seMessagesSharedFile) { (msg: ProcessMessage) in + onMessage(msg.message) + } +} + public func sendAppState(_ state: AppState) { sendAppProcessMessage(.state(state: state)) } @@ -97,3 +115,7 @@ public func sendAppState(_ state: AppState) { public func sendNSEState(_ state: NSEState) { sendNSEProcessMessage(.state(state: state)) } + +public func sendSEState(_ state: SEState) { + sendSEProcessMessage(.state(state: state)) +} diff --git a/apps/ios/SimpleXChat/SimpleX.h b/apps/ios/SimpleXChat/SimpleX.h index 153365424e..92dfafca21 100644 --- a/apps/ios/SimpleXChat/SimpleX.h +++ b/apps/ios/SimpleXChat/SimpleX.h @@ -10,6 +10,7 @@ #define SimpleX_h #include "hs_init.h" +#include "objc.h" extern void hs_init(int argc, char **argv[]); diff --git a/apps/ios/SimpleXChat/Theme/ChatWallpaperTypes.swift b/apps/ios/SimpleXChat/Theme/ChatWallpaperTypes.swift new file mode 100644 index 0000000000..662f8b43d1 --- /dev/null +++ b/apps/ios/SimpleXChat/Theme/ChatWallpaperTypes.swift @@ -0,0 +1,402 @@ +// +// ChatWallpaper.swift +// SimpleX (iOS) +// +// Created by Avently on 06.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI + +public enum PresetWallpaper: CaseIterable { + case cats + case flowers + case hearts + case kids + case school + case travel + + var res: UIImage { + UIImage(named: "wallpaper_\(filename)")! + } + + public var filename: String { + switch self { + case .cats: "cats" + case .flowers: "flowers" + case .hearts: "hearts" + case .kids: "kids" + case .school: "school" + case .travel: "travel" + } + } + + public var scale: Float { + switch self { + case .cats: 0.63 + case .flowers: 0.53 + case .hearts: 0.59 + case .kids: 0.53 + case .school: 0.53 + case .travel: 0.68 + } + } + + public var background: [DefaultTheme: Color] { + switch self { + case .cats: wallpaperBackgrounds(light: "#ffF8F6EA") + case .flowers: wallpaperBackgrounds(light: "#ffE2FFE4") + case .hearts: wallpaperBackgrounds(light: "#ffFDECEC") + case .kids: wallpaperBackgrounds(light: "#ffdbfdfb") + case .school: wallpaperBackgrounds(light: "#ffE7F5FF") + case .travel: wallpaperBackgrounds(light: "#fff9eeff") + } + } + + public var tint: [DefaultTheme: Color] { + switch self { + case .cats: [ + DefaultTheme.LIGHT: "#ffefdca6".colorFromReadableHex(), + DefaultTheme.DARK: "#ff4b3b0e".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff51400f".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff4b3b0e".colorFromReadableHex() + ] + case .flowers: [ + DefaultTheme.LIGHT: "#ff9CEA59".colorFromReadableHex(), + DefaultTheme.DARK: "#ff31560D".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff36600f".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff31560D".colorFromReadableHex() + ] + case .hearts: [ + DefaultTheme.LIGHT: "#fffde0e0".colorFromReadableHex(), + DefaultTheme.DARK: "#ff3c0f0f".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff411010".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff3C0F0F".colorFromReadableHex() + ] + case .kids: [ + DefaultTheme.LIGHT: "#ffadeffc".colorFromReadableHex(), + DefaultTheme.DARK: "#ff16404B".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff184753".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff16404B".colorFromReadableHex() + ] + case .school: [ + DefaultTheme.LIGHT: "#ffCEEBFF".colorFromReadableHex(), + DefaultTheme.DARK: "#ff0F293B".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff112f43".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff0F293B".colorFromReadableHex() + ] + case .travel: [ + DefaultTheme.LIGHT: "#ffeedbfe".colorFromReadableHex(), + DefaultTheme.DARK: "#ff311E48".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff35204e".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff311E48".colorFromReadableHex() + ] + } + } + + public var colors: [DefaultTheme: ThemeColors] { + switch self { + case .cats: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#fffffaed", + sentQuote: "#fffaf0d6", + receivedMessage: "#ffF8F7F4", + receivedQuote: "#ffefede9" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff2f2919", + sentQuote: "#ff473a1d", + receivedMessage: "#ff272624", + receivedQuote: "#ff373633" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff41371b", + sentQuote: "#ff654f1c", + receivedMessage: "#ff272624", + receivedQuote: "#ff373633" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff41371b", + sentQuote: "#ff654f1c", + receivedMessage: "#ff1f1e1b", + receivedQuote: "#ff2f2d27" + ) + ] + case .flowers: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#fff1ffe5", + sentQuote: "#ffdcf9c4", + receivedMessage: "#ffF4F8F2", + receivedQuote: "#ffe7ece7" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff163521", + sentQuote: "#ff1B5330", + receivedMessage: "#ff242523", + receivedQuote: "#ff353733" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff184739", + sentQuote: "#ff1F6F4B", + receivedMessage: "#ff242523", + receivedQuote: "#ff353733" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff184739", + sentQuote: "#ff1F6F4B", + receivedMessage: "#ff1c1f1a", + receivedQuote: "#ff282b25" + ) + ] + case .hearts: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#fffff4f4", + sentQuote: "#ffffdfdf", + receivedMessage: "#fff8f6f6", + receivedQuote: "#ffefebeb" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff301515", + sentQuote: "#ff4C1818", + receivedMessage: "#ff242121", + receivedQuote: "#ff3b3535" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff491A28", + sentQuote: "#ff761F29", + receivedMessage: "#ff242121", + receivedQuote: "#ff3b3535" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff491A28", + sentQuote: "#ff761F29", + receivedMessage: "#ff1f1b1b", + receivedQuote: "#ff2e2626" + ) + ] + case .kids: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#ffeafeff", + sentQuote: "#ffcbf4f7", + receivedMessage: "#fff3fafa", + receivedQuote: "#ffe4efef" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff16302F", + sentQuote: "#ff1a4a49", + receivedMessage: "#ff252626", + receivedQuote: "#ff373A39" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff1a4745", + sentQuote: "#ff1d6b69", + receivedMessage: "#ff252626", + receivedQuote: "#ff373a39" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff1a4745", + sentQuote: "#ff1d6b69", + receivedMessage: "#ff1e1f1f", + receivedQuote: "#ff262b29" + ) + ] + case .school: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#ffeef9ff", + sentQuote: "#ffD6EDFA", + receivedMessage: "#ffF3F5F9", + receivedQuote: "#ffe4e8ee" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff172833", + sentQuote: "#ff1C3E4F", + receivedMessage: "#ff26282c", + receivedQuote: "#ff393c40" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff1A3C5D", + sentQuote: "#ff235b80", + receivedMessage: "#ff26282c", + receivedQuote: "#ff393c40" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff1A3C5D", + sentQuote: "#ff235b80", + receivedMessage: "#ff1d1e22", + receivedQuote: "#ff292b2f" + ) + ] + case .travel: [ + DefaultTheme.LIGHT: ThemeColors.from( + sentMessage: "#fffcf6ff", + sentQuote: "#fff2e0fc", + receivedMessage: "#ffF6F4F7", + receivedQuote: "#ffede9ee" + ), + DefaultTheme.DARK: ThemeColors.from( + sentMessage: "#ff33263B", + sentQuote: "#ff53385E", + receivedMessage: "#ff272528", + receivedQuote: "#ff3B373E" + ), + DefaultTheme.SIMPLEX: ThemeColors.from( + sentMessage: "#ff3C255D", + sentQuote: "#ff623485", + receivedMessage: "#ff26273B", + receivedQuote: "#ff3A394F" + ), + DefaultTheme.BLACK: ThemeColors.from( + sentMessage: "#ff3C255D", + sentQuote: "#ff623485", + receivedMessage: "#ff231f23", + receivedQuote: "#ff2c2931" + ) + ] + } + } + + public static func from(_ filename: String) -> PresetWallpaper? { + switch filename { + case PresetWallpaper.cats.filename: PresetWallpaper.cats + case PresetWallpaper.flowers.filename: PresetWallpaper.flowers + case PresetWallpaper.hearts.filename: PresetWallpaper.hearts + case PresetWallpaper.kids.filename: PresetWallpaper.kids + case PresetWallpaper.school.filename: PresetWallpaper.school + case PresetWallpaper.travel.filename: PresetWallpaper.travel + default: nil + } + } +} + +func wallpaperBackgrounds(light: String) -> [DefaultTheme : Color] { + [ + DefaultTheme.LIGHT: light.colorFromReadableHex(), + DefaultTheme.DARK: "#ff121212".colorFromReadableHex(), + DefaultTheme.SIMPLEX: "#ff111528".colorFromReadableHex(), + DefaultTheme.BLACK: "#ff070707".colorFromReadableHex() + ] +} + +public enum WallpaperScaleType: String, Codable, CaseIterable { + case fill + case fit + case `repeat` + + public var text: String { + switch self { + case .fill: "Fill" + case .fit: "Fit" + case .repeat: "Repeat" + } + } + + public func computeScaleFactor(_ srcSize: CGSize, _ dstSize: CGSize) -> (CGFloat, CGFloat) { + switch self { + case .fill: + let widthScale = dstSize.width / srcSize.width + let heightScale = dstSize.height / srcSize.height + return (max(widthScale, heightScale), max(widthScale, heightScale)) + case .fit: fallthrough + case .repeat: + let widthScale = dstSize.width / srcSize.width + let heightScale = dstSize.height / srcSize.height + return (min(widthScale, heightScale), min(widthScale, heightScale)) + } + } +} + +public enum WallpaperType: Equatable { + public var image: SwiftUI.Image? { + if let uiImage { + return SwiftUI.Image(uiImage: uiImage) + } + return nil + } + + public var uiImage: UIImage? { + let filename: String + switch self { + case let .preset(f, _): filename = f + case let .image(f, _, _): filename = f + default: return nil + } + if filename == "" { return nil } + if let image = WallpaperType.cachedImages[filename] { + return image + } else { + let res: UIImage? + if case let .preset(filename, _) = self { + res = (PresetWallpaper.from(filename) ?? PresetWallpaper.cats).res + } else { + // In case of unintentional image deletion don't crash the app + res = UIImage(contentsOfFile: getWallpaperFilePath(filename).path) + } + if let res { + WallpaperType.cachedImages[filename] = res + } + return res + } + } + + public func sameType(_ other: WallpaperType?) -> Bool { + if case let .preset(filename, _) = self, case let .preset(otherFilename, _) = other { filename == otherFilename } + else if case .image = self, case .image = other { true } + else if case .empty = self, case .empty = other { true } + else { false } + } + + public var isPreset: Bool { switch self { case .preset: true; default: false } } + + public var isImage: Bool { switch self { case .image: true; default: false } } + + public var isEmpty: Bool { switch self { case .empty: true; default: false } } + + public var scale: Float { + switch self { + case let .preset(_, scale): scale ?? 1 + case let .image(_, scale, _): scale ?? 1 + case .empty: 1 + } + } + + public func samePreset(other: PresetWallpaper?) -> Bool { if case let .preset(filename, _) = self, filename == other?.filename { true } else { false } } + + case preset(_ filename: String, _ scale: Float?) + + case image(_ filename: String, _ scale: Float?, _ scaleType: WallpaperScaleType?) + + case empty + + public func defaultBackgroundColor(_ theme: DefaultTheme, _ themeBackground: Color) -> Color { + if case let .preset(filename, _) = self { + (PresetWallpaper.from(filename) ?? PresetWallpaper.cats).background[theme]! + } else { + themeBackground + } + } + + public func defaultTintColor(_ theme: DefaultTheme) -> Color { + if case let .preset(filename, _) = self { + (PresetWallpaper.from(filename) ?? PresetWallpaper.cats).tint[theme]! + } else if case let .image(_, _, scaleType) = self, scaleType == WallpaperScaleType.repeat { + Color.clear + } else { + Color.clear + } + } + + public static var cachedImages: [String: UIImage] = [:] + + public static func from(_ wallpaper: ThemeWallpaper?) -> WallpaperType? { + if wallpaper == nil { + return nil + } else if let preset = wallpaper?.preset { + return WallpaperType.preset(preset, wallpaper?.scale) + } else if let imageFile = wallpaper?.imageFile { + return WallpaperType.image(imageFile, wallpaper?.scale, wallpaper?.scaleType) + } else { + return WallpaperType.empty + } + } +} diff --git a/apps/ios/SimpleXChat/Theme/Color.swift b/apps/ios/SimpleXChat/Theme/Color.swift new file mode 100644 index 0000000000..f307eaa5aa --- /dev/null +++ b/apps/ios/SimpleXChat/Theme/Color.swift @@ -0,0 +1,131 @@ +// +// Color.swift +// SimpleX (iOS) +// +// Created by Avently on 05.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI + +//let Purple200 = Color(0xFFBB86FC) +//let Purple500 = Color(0xFF6200EE) +//let Purple700 = Color(0xFF3700B3) +//let Teal200 = Color(0xFF03DAC5) +//let Gray = Color(0x22222222) +//let Indigo = Color(0xFF9966FF) +let SimplexBlue = Color(0, 136, 255, a: 255) +//let SimplexGreen = Color(77, 218, 103, a: 255) +//let SecretColor = Color(0x40808080) +let LightGray = Color(241, 242, 246, a: 255) +let DarkGray = Color(43, 44, 46, a: 255) +let HighOrLowlight = Color(139, 135, 134, a: 255) +//let MessagePreviewDark = Color(179, 175, 174, a: 255) +//let MessagePreviewLight = Color(49, 45, 44, a: 255) +//let ToolbarLight = Color(220, 220, 220, a: 12) +//let ToolbarDark = Color(80, 80, 80, a: 12) +//let SettingsSecondaryLight = Color(200, 196, 195, a: 90) +//let GroupDark = Color(80, 80, 80, a: 60) +//let IncomingCallLight = Color(239, 237, 236, a: 255) +//let WarningOrange = Color(255, 127, 0, a: 255) +//let WarningYellow = Color(255, 192, 0, a: 255) +//let FileLight = Color(183, 190, 199, a: 255) +//let FileDark = Color(101, 101, 106, a: 255) + +extension Color { + public init(_ argb: Int64) { + let a = Double((argb & 0xFF000000) >> 24) / 255.0 + let r = Double((argb & 0xFF0000) >> 16) / 255.0 + let g = Double((argb & 0xFF00) >> 8) / 255.0 + let b = Double((argb & 0xFF)) / 255.0 + self.init(.sRGB, red: r, green: g, blue: b, opacity: a) + } + + public init(_ r: Int, _ g: Int, _ b: Int, a: Int) { + self.init(.sRGB, red: Double(r) / 255.0, green: Double(g) / 255.0, blue: Double(b) / 255.0, opacity: Double(a) / 255.0) + } + + public func toReadableHex() -> String { + let uiColor: UIColor = .init(self) + var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) + uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) + // Can be negative values and more than 1. Extended color range, making it normal + r = min(1, max(0, r)) + g = min(1, max(0, g)) + b = min(1, max(0, b)) + a = min(1, max(0, a)) + return String(format: "#%02x%02x%02x%02x", + Int((a * 255).rounded()), + Int((r * 255).rounded()), + Int((g * 255).rounded()), + Int((b * 255).rounded()) + ) + } + + public func toHTMLHex() -> String { + let uiColor: UIColor = .init(self) + var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) + uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) + // Can be negative values and more than 1. Extended color range, making it normal + r = min(1, max(0, r)) + g = min(1, max(0, g)) + b = min(1, max(0, b)) + a = min(1, max(0, a)) + return String(format: "#%02x%02x%02x%02x", + Int((r * 255).rounded()), + Int((g * 255).rounded()), + Int((b * 255).rounded()), + Int((a * 255).rounded()) + ) + } + + public func darker(_ factor: CGFloat = 0.1) -> Color { + var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) + UIColor(self).getRed(&r, green: &g, blue: &b, alpha: &a) + return Color(.sRGB, red: max(r * (1 - factor), 0), green: max(g * (1 - factor), 0), blue: max(b * (1 - factor), 0), opacity: a) + } + + public func lighter(_ factor: CGFloat = 0.1) -> Color { + var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) + UIColor(self).getRed(&r, green: &g, blue: &b, alpha: &a) + return Color(.sRGB, red: min(r * (1 + factor), 1), green: min(g * (1 + factor), 1), blue: min(b * (1 + factor), 1), opacity: a) + } + + public func asGroupedBackground(_ mode: DefaultThemeMode) -> Color { + let uiColor: UIColor = .init(self) + var (r, g, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0) + uiColor.getRed(&r, green: &g, blue: &b, alpha: &a) + return mode == DefaultThemeMode.light + ? Color(.sRGB, red: max(0, r - 0.052), green: max(0, g - 0.051), blue: max(0, b - 0.032), opacity: a) + : Color(.sRGB, red: min(1, r + 0.11), green: min(1, g + 0.11), blue: min(1, b + 0.115), opacity: a) + } +} + +extension String { + func colorFromReadableHex() -> Color { + // https://stackoverflow.com/a/56874327 + let hex = self.trimmingCharacters(in: ["#", " "]) + var int: UInt64 = 0 + Scanner(string: hex).scanHexInt64(&int) + let a, r, g, b: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (a, r, g, b) = (1, 1, 1, 0) + } + + return Color( + .sRGB, + red: Double(r) / 255, + green: Double(g) / 255, + blue: Double(b) / 255, + opacity: Double(a) / 255 + ) + } +} diff --git a/apps/ios/SimpleXChat/Theme/ThemeTypes.swift b/apps/ios/SimpleXChat/Theme/ThemeTypes.swift new file mode 100644 index 0000000000..4074382543 --- /dev/null +++ b/apps/ios/SimpleXChat/Theme/ThemeTypes.swift @@ -0,0 +1,736 @@ +// +// Theme.swift +// SimpleX (iOS) +// +// Created by Avently on 03.06.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation +import SwiftUI + +public enum DefaultTheme: String, Codable, Equatable { + case LIGHT + case DARK + case SIMPLEX + case BLACK + + public static let SYSTEM_THEME_NAME: String = "SYSTEM" + + public var themeName: String { self.rawValue } + + public var mode: DefaultThemeMode { + self == .LIGHT + ? DefaultThemeMode.light + : DefaultThemeMode.dark + } + + public func hasChangedAnyColor(_ overrides: ThemeOverrides?) -> Bool { + if let overrides { + overrides.colors != ThemeColors() || (overrides.wallpaper != nil && (overrides.wallpaper?.background != nil || overrides.wallpaper?.tint != nil)) + } else { + false + } + } +} + +public enum DefaultThemeMode: String, Codable { + case light + case dark +} + +public class Colors: ObservableObject, NSCopying, Equatable { + @Published public var primary: Color + @Published public var primaryVariant: Color + @Published public var secondary: Color + @Published public var secondaryVariant: Color + @Published public var background: Color + @Published public var surface: Color + @Published public var error: Color + @Published public var onBackground: Color + @Published public var onSurface: Color + @Published public var isLight: Bool + + public init(primary: Color, primaryVariant: Color, secondary: Color, secondaryVariant: Color, background: Color, surface: Color, error: Color, onBackground: Color, onSurface: Color, isLight: Bool) { + self.primary = primary + self.primaryVariant = primaryVariant + self.secondary = secondary + self.secondaryVariant = secondaryVariant + self.background = background + self.surface = surface + self.error = error + self.onBackground = onBackground + self.onSurface = onSurface + self.isLight = isLight + } + + public static func == (lhs: Colors, rhs: Colors) -> Bool { + lhs.primary == rhs.primary && + lhs.primaryVariant == rhs.primaryVariant && + lhs.secondary == rhs.secondary && + lhs.secondaryVariant == rhs.secondaryVariant && + lhs.background == rhs.background && + lhs.surface == rhs.surface && + lhs.error == rhs.error && + lhs.onBackground == rhs.onBackground && + lhs.onSurface == rhs.onSurface && + lhs.isLight == rhs.isLight + } + + public func copy(with zone: NSZone? = nil) -> Any { + Colors(primary: self.primary, primaryVariant: self.primaryVariant, secondary: self.secondary, secondaryVariant: self.secondaryVariant, background: self.background, surface: self.surface, error: self.error, onBackground: self.onBackground, onSurface: self.onSurface, isLight: self.isLight) + } + + public func clone() -> Colors { copy() as! Colors } +} + +public class AppColors: ObservableObject, NSCopying, Equatable { + @Published public var title: Color + @Published public var primaryVariant2: Color + @Published public var sentMessage: Color + @Published public var sentQuote: Color + @Published public var receivedMessage: Color + @Published public var receivedQuote: Color + + public init(title: Color, primaryVariant2: Color, sentMessage: Color, sentQuote: Color, receivedMessage: Color, receivedQuote: Color) { + self.title = title + self.primaryVariant2 = primaryVariant2 + self.sentMessage = sentMessage + self.sentQuote = sentQuote + self.receivedMessage = receivedMessage + self.receivedQuote = receivedQuote + } + + public static func == (lhs: AppColors, rhs: AppColors) -> Bool { + lhs.title == rhs.title && + lhs.primaryVariant2 == rhs.primaryVariant2 && + lhs.sentMessage == rhs.sentMessage && + lhs.sentQuote == rhs.sentQuote && + lhs.receivedQuote == rhs.receivedMessage && + lhs.receivedQuote == rhs.receivedQuote + } + + public func copy(with zone: NSZone? = nil) -> Any { + AppColors(title: self.title, primaryVariant2: self.primaryVariant2, sentMessage: self.sentMessage, sentQuote: self.sentQuote, receivedMessage: self.receivedMessage, receivedQuote: self.receivedQuote) + } + + public func clone() -> AppColors { copy() as! AppColors } + + public func copy( + title: Color?, + primaryVariant2: Color?, + sentMessage: Color?, + sentQuote: Color?, + receivedMessage: Color?, + receivedQuote: Color? + ) -> AppColors { + AppColors( + title: title ?? self.title, + primaryVariant2: primaryVariant2 ?? self.primaryVariant2, + sentMessage: sentMessage ?? self.sentMessage, + sentQuote: sentQuote ?? self.sentQuote, + receivedMessage: receivedMessage ?? self.receivedMessage, + receivedQuote: receivedQuote ?? self.receivedQuote + ) + } +} + +public class AppWallpaper: ObservableObject, NSCopying, Equatable { + public static func == (lhs: AppWallpaper, rhs: AppWallpaper) -> Bool { + lhs.background == rhs.background && + lhs.tint == rhs.tint && + lhs.type == rhs.type + } + + @Published public var background: Color? = nil + @Published public var tint: Color? = nil + @Published public var type: WallpaperType = WallpaperType.empty + + public init(background: Color?, tint: Color?, type: WallpaperType) { + self.background = background + self.tint = tint + self.type = type + } + + public func copy(with zone: NSZone? = nil) -> Any { + AppWallpaper(background: self.background, tint: self.tint, type: self.type) + } + + public func clone() -> AppWallpaper { copy() as! AppWallpaper } + + public func copyWithoutDefault(_ background: Color?, _ tint: Color?, _ type: WallpaperType) -> AppWallpaper { + AppWallpaper( + background: background, + tint: tint, + type: type + ) + } +} + +public enum ThemeColor { + case primary + case primaryVariant + case secondary + case secondaryVariant + case background + case surface + case title + case sentMessage + case sentQuote + case receivedMessage + case receivedQuote + case primaryVariant2 + case wallpaperBackground + case wallpaperTint + + public func fromColors(_ colors: Colors, _ appColors: AppColors, _ appWallpaper: AppWallpaper) -> Color? { + switch (self) { + case .primary: colors.primary + case .primaryVariant: colors.primaryVariant + case .secondary: colors.secondary + case .secondaryVariant: colors.secondaryVariant + case .background: colors.background + case .surface: colors.surface + case .title: appColors.title + case .primaryVariant2: appColors.primaryVariant2 + case .sentMessage: appColors.sentMessage + case .sentQuote: appColors.sentQuote + case .receivedMessage: appColors.receivedMessage + case .receivedQuote: appColors.receivedQuote + case .wallpaperBackground: appWallpaper.background + case .wallpaperTint: appWallpaper.tint + } + } + + public var text: LocalizedStringKey { + switch (self) { + case .primary: "Accent" + case .primaryVariant: "Additional accent" + case .secondary: "Secondary" + case .secondaryVariant: "Additional secondary" + case .background: "Background" + case .surface: "Menus" + case .title: "Title" + case .primaryVariant2: "Additional accent 2" + case .sentMessage: "Sent message" + case .sentQuote: "Sent reply" + case .receivedMessage: "Received message" + case .receivedQuote: "Received reply" + case .wallpaperBackground: "Wallpaper background" + case .wallpaperTint: "Wallpaper accent" + } + } +} + +public struct ThemeColors: Codable, Equatable, Hashable { + public var primary: String? = nil + public var primaryVariant: String? = nil + public var secondary: String? = nil + public var secondaryVariant: String? = nil + public var background: String? = nil + public var surface: String? = nil + public var title: String? = nil + public var primaryVariant2: String? = nil + public var sentMessage: String? = nil + public var sentQuote: String? = nil + public var receivedMessage: String? = nil + public var receivedQuote: String? = nil + + public init(primary: String? = nil, primaryVariant: String? = nil, secondary: String? = nil, secondaryVariant: String? = nil, background: String? = nil, surface: String? = nil, title: String? = nil, primaryVariant2: String? = nil, sentMessage: String? = nil, sentQuote: String? = nil, receivedMessage: String? = nil, receivedQuote: String? = nil) { + self.primary = primary + self.primaryVariant = primaryVariant + self.secondary = secondary + self.secondaryVariant = secondaryVariant + self.background = background + self.surface = surface + self.title = title + self.primaryVariant2 = primaryVariant2 + self.sentMessage = sentMessage + self.sentQuote = sentQuote + self.receivedMessage = receivedMessage + self.receivedQuote = receivedQuote + } + + public enum CodingKeys: String, CodingKey, CaseIterable { + case primary = "accent" + case primaryVariant = "accentVariant" + case secondary + case secondaryVariant + case background + case surface = "menus" + case title + case primaryVariant2 = "accentVariant2" + case sentMessage + case sentQuote = "sentReply" + case receivedMessage + case receivedQuote = "receivedReply" + } + + public static func from(sentMessage: String, sentQuote: String, receivedMessage: String, receivedQuote: String) -> ThemeColors { + var c = ThemeColors() + c.sentMessage = sentMessage + c.sentQuote = sentQuote + c.receivedMessage = receivedMessage + c.receivedQuote = receivedQuote + return c + } + + public static func from(_ colors: Colors, _ appColors: AppColors) -> ThemeColors { + ThemeColors( + primary: colors.primary.toReadableHex(), + primaryVariant: colors.primaryVariant.toReadableHex(), + secondary: colors.secondary.toReadableHex(), + secondaryVariant: colors.secondaryVariant.toReadableHex(), + background: colors.background.toReadableHex(), + surface: colors.surface.toReadableHex(), + title: appColors.title.toReadableHex(), + primaryVariant2: appColors.primaryVariant2.toReadableHex(), + sentMessage: appColors.sentMessage.toReadableHex(), + sentQuote: appColors.sentQuote.toReadableHex(), + receivedMessage: appColors.receivedMessage.toReadableHex(), + receivedQuote: appColors.receivedQuote.toReadableHex() + ) + } +} + +public struct ThemeWallpaper: Codable, Equatable, Hashable { + public var preset: String? + public var scale: Float? + public var scaleType: WallpaperScaleType? + public var background: String? + public var tint: String? + public var image: String? + public var imageFile: String? + + public init(preset: String? = nil, scale: Float? = nil, scaleType: WallpaperScaleType? = nil, background: String? = nil, tint: String? = nil, image: String? = nil, imageFile: String? = nil) { + self.preset = preset + self.scale = scale + self.scaleType = scaleType + self.background = background + self.tint = tint + self.image = image + self.imageFile = imageFile + } + + public enum CodingKeys: String, CodingKey, CaseIterable { + case preset + case scale + case scaleType + case background + case tint + case image + case imageFile + } + + public func toAppWallpaper() -> AppWallpaper { + AppWallpaper ( + background: background?.colorFromReadableHex(), + tint: tint?.colorFromReadableHex(), + type: WallpaperType.from(self) ?? WallpaperType.empty + ) + } + + public func withFilledWallpaperPath() -> ThemeWallpaper { + let aw = toAppWallpaper() + let type = aw.type + let preset: String? = if case let WallpaperType.preset(filename, _) = type { filename } else { nil } + let scale: Float? = if scale == nil { nil } else { + if case let WallpaperType.preset(_, scale) = type { + scale + } else if case let WallpaperType.image(_, scale, _) = type { + scale + } else { + nil + } + } + let scaleType: WallpaperScaleType? = if scaleType == nil { nil } else if case let WallpaperType.image(_, _, scaleType) = type { scaleType } else { nil } + let imageFile: String? = if case let WallpaperType.image(filename, _, _) = type { filename } else { nil } + return ThemeWallpaper ( + preset: preset, + scale: scale, + scaleType: scaleType, + background: aw.background?.toReadableHex(), + tint: aw.tint?.toReadableHex(), + image: nil, + imageFile: imageFile + ) + } + + public static func from(_ type: WallpaperType, _ background: String?, _ tint: String?) -> ThemeWallpaper { + let preset: String? = if case let WallpaperType.preset(filename, _) = type { filename } else { nil } + let scale: Float? = if case let WallpaperType.preset(_, scale) = type { scale } else if case let WallpaperType.image(_, scale, _) = type { scale } else { nil } + let scaleType: WallpaperScaleType? = if case let WallpaperType.image(_, _, scaleType) = type { scaleType } else { nil } + let imageFile: String? = if case let WallpaperType.image(filename, _, _) = type { filename } else { nil } + return ThemeWallpaper( + preset: preset, + scale: scale, + scaleType: scaleType, + background: background, + tint: tint, + image: nil, + imageFile: imageFile + ) + } +} + +/// If you add new properties, make sure they serialized to YAML correctly, see: +/// encodeThemeOverrides() +public struct ThemeOverrides: Codable, Equatable, Hashable { + public var themeId: String = UUID().uuidString + public var base: DefaultTheme + public var colors: ThemeColors = ThemeColors() + public var wallpaper: ThemeWallpaper? = nil + + public init(themeId: String = UUID().uuidString, base: DefaultTheme, colors: ThemeColors = ThemeColors(), wallpaper: ThemeWallpaper? = nil) { + self.themeId = themeId + self.base = base + self.colors = colors + self.wallpaper = wallpaper + } + + public func isSame(_ type: WallpaperType?, _ themeName: String) -> Bool { + if base.themeName != themeName { + return false + } + return if let preset = wallpaper?.preset, let type, case let WallpaperType.preset(filename, _) = type, preset == filename { + true + } else if wallpaper?.imageFile != nil, let type, case WallpaperType.image = type { + true + } else if wallpaper?.preset == nil && wallpaper?.imageFile == nil && type == nil { + true + } else if wallpaper?.preset == nil && wallpaper?.imageFile == nil, let type, case WallpaperType.empty = type { + true + } else { + false + } + } + + public func withUpdatedColor(_ name: ThemeColor, _ color: String?) -> ThemeOverrides { + var c = colors + var w = wallpaper + switch name { + case ThemeColor.primary: c.primary = color + case ThemeColor.primaryVariant: c.primaryVariant = color + case ThemeColor.secondary: c.secondary = color + case ThemeColor.secondaryVariant: c.secondaryVariant = color + case ThemeColor.background: c.background = color + case ThemeColor.surface: c.surface = color + case ThemeColor.title: c.title = color + case ThemeColor.primaryVariant2: c.primaryVariant2 = color + case ThemeColor.sentMessage: c.sentMessage = color + case ThemeColor.sentQuote: c.sentQuote = color + case ThemeColor.receivedMessage: c.receivedMessage = color + case ThemeColor.receivedQuote: c.receivedQuote = color + case ThemeColor.wallpaperBackground: w?.background = color + case ThemeColor.wallpaperTint: w?.tint = color + } + return ThemeOverrides(themeId: themeId, base: base, colors: c, wallpaper: w) + } + + public func toColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perUserTheme: ThemeColors?, _ presetWallpaperTheme: ThemeColors?) -> Colors { + let baseColors = switch base { + case DefaultTheme.LIGHT: LightColorPalette + case DefaultTheme.DARK: DarkColorPalette + case DefaultTheme.SIMPLEX: SimplexColorPalette + case DefaultTheme.BLACK: BlackColorPalette + } + let c = baseColors.clone() + c.primary = perChatTheme?.primary?.colorFromReadableHex() ?? perUserTheme?.primary?.colorFromReadableHex() ?? colors.primary?.colorFromReadableHex() ?? presetWallpaperTheme?.primary?.colorFromReadableHex() ?? baseColors.primary + c.primaryVariant = perChatTheme?.primaryVariant?.colorFromReadableHex() ?? perUserTheme?.primaryVariant?.colorFromReadableHex() ?? colors.primaryVariant?.colorFromReadableHex() ?? presetWallpaperTheme?.primaryVariant?.colorFromReadableHex() ?? baseColors.primaryVariant + c.secondary = perChatTheme?.secondary?.colorFromReadableHex() ?? perUserTheme?.secondary?.colorFromReadableHex() ?? colors.secondary?.colorFromReadableHex() ?? presetWallpaperTheme?.secondary?.colorFromReadableHex() ?? baseColors.secondary + c.secondaryVariant = perChatTheme?.secondaryVariant?.colorFromReadableHex() ?? perUserTheme?.secondaryVariant?.colorFromReadableHex() ?? colors.secondaryVariant?.colorFromReadableHex() ?? presetWallpaperTheme?.secondaryVariant?.colorFromReadableHex() ?? baseColors.secondaryVariant + c.background = perChatTheme?.background?.colorFromReadableHex() ?? perUserTheme?.background?.colorFromReadableHex() ?? colors.background?.colorFromReadableHex() ?? presetWallpaperTheme?.background?.colorFromReadableHex() ?? baseColors.background + c.surface = perChatTheme?.surface?.colorFromReadableHex() ?? perUserTheme?.surface?.colorFromReadableHex() ?? colors.surface?.colorFromReadableHex() ?? presetWallpaperTheme?.surface?.colorFromReadableHex() ?? baseColors.surface + return c + } + + public func toAppColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perChatWallpaperType: WallpaperType?, _ perUserTheme: ThemeColors?, _ perUserWallpaperType: WallpaperType?, _ presetWallpaperTheme: ThemeColors?) -> AppColors { + let baseColors = switch base { + case DefaultTheme.LIGHT: LightColorPaletteApp + case DefaultTheme.DARK: DarkColorPaletteApp + case DefaultTheme.SIMPLEX: SimplexColorPaletteApp + case DefaultTheme.BLACK: BlackColorPaletteApp + } + + let sentMessageFallback = colors.sentMessage?.colorFromReadableHex() ?? presetWallpaperTheme?.sentMessage?.colorFromReadableHex() ?? baseColors.sentMessage + let sentQuoteFallback = colors.sentQuote?.colorFromReadableHex() ?? presetWallpaperTheme?.sentQuote?.colorFromReadableHex() ?? baseColors.sentQuote + let receivedMessageFallback = colors.receivedMessage?.colorFromReadableHex() ?? presetWallpaperTheme?.receivedMessage?.colorFromReadableHex() ?? baseColors.receivedMessage + let receivedQuoteFallback = colors.receivedQuote?.colorFromReadableHex() ?? presetWallpaperTheme?.receivedQuote?.colorFromReadableHex() ?? baseColors.receivedQuote + + let c = baseColors.clone() + c.title = perChatTheme?.title?.colorFromReadableHex() ?? perUserTheme?.title?.colorFromReadableHex() ?? colors.title?.colorFromReadableHex() ?? presetWallpaperTheme?.title?.colorFromReadableHex() ?? baseColors.title + c.primaryVariant2 = perChatTheme?.primaryVariant2?.colorFromReadableHex() ?? perUserTheme?.primaryVariant2?.colorFromReadableHex() ?? colors.primaryVariant2?.colorFromReadableHex() ?? presetWallpaperTheme?.primaryVariant2?.colorFromReadableHex() ?? baseColors.primaryVariant2 + c.sentMessage = if let c = perChatTheme?.sentMessage { c.colorFromReadableHex() } else if let perUserTheme, (perChatWallpaperType == nil || perUserWallpaperType == nil || perChatWallpaperType!.sameType(perUserWallpaperType)) { perUserTheme.sentMessage?.colorFromReadableHex() ?? sentMessageFallback } else { sentMessageFallback } + c.sentQuote = if let c = perChatTheme?.sentQuote { c.colorFromReadableHex() } else if let perUserTheme, (perChatWallpaperType == nil || perUserWallpaperType == nil || perChatWallpaperType!.sameType(perUserWallpaperType)) { perUserTheme.sentQuote?.colorFromReadableHex() ?? sentQuoteFallback } else { sentQuoteFallback } + c.receivedMessage = if let c = perChatTheme?.receivedMessage { c.colorFromReadableHex() } else if let perUserTheme, (perChatWallpaperType == nil || perUserWallpaperType == nil || perChatWallpaperType!.sameType(perUserWallpaperType)) { perUserTheme.receivedMessage?.colorFromReadableHex() ?? receivedMessageFallback } + else { receivedMessageFallback } + c.receivedQuote = if let c = perChatTheme?.receivedQuote { c.colorFromReadableHex() } else if let perUserTheme, (perChatWallpaperType == nil || perUserWallpaperType == nil || perChatWallpaperType!.sameType(perUserWallpaperType)) { perUserTheme.receivedQuote?.colorFromReadableHex() ?? receivedQuoteFallback } else { receivedQuoteFallback } + return c + } + + public func toAppWallpaper(_ themeOverridesForType: WallpaperType?, _ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverride?, _ themeBackgroundColor: Color) -> AppWallpaper { + let mainType: WallpaperType + if let t = themeOverridesForType { mainType = t } + // type can be nil if override is empty `"wallpaper": "{}"`, in this case no wallpaper is needed, empty. + // It's not nil to override upper level wallpaper + else if let w = perChatTheme?.wallpaper { mainType = w.toAppWallpaper().type } + else if let w = perUserTheme?.wallpaper { mainType = w.toAppWallpaper().type } + else if let w = wallpaper { mainType = w.toAppWallpaper().type } + else { return AppWallpaper(background: nil, tint: nil, type: WallpaperType.empty) } + + let first: ThemeWallpaper? = if mainType.sameType(perChatTheme?.wallpaper?.toAppWallpaper().type) { perChatTheme?.wallpaper } else { nil } + let second: ThemeWallpaper? = if mainType.sameType(perUserTheme?.wallpaper?.toAppWallpaper().type) { perUserTheme?.wallpaper } else { nil } + let third: ThemeWallpaper? = if mainType.sameType(self.wallpaper?.toAppWallpaper().type) { self.wallpaper } else { nil } + + let wallpaper: WallpaperType + switch mainType { + case let WallpaperType.preset(preset, scale): + let scale = if themeOverridesForType == nil { scale ?? first?.scale ?? second?.scale ?? third?.scale } else { second?.scale ?? third?.scale ?? scale } + wallpaper = WallpaperType.preset(preset, scale) + case let WallpaperType.image(filename, scale, scaleType): + let scale = if themeOverridesForType == nil { scale ?? first?.scale ?? second?.scale ?? third?.scale } else { second?.scale ?? third?.scale ?? scale } + let scaleType = if themeOverridesForType == nil { scaleType ?? first?.scaleType ?? second?.scaleType ?? third?.scaleType } else { second?.scaleType ?? third?.scaleType ?? scaleType } + let imageFile = if themeOverridesForType == nil { filename } else { first?.imageFile ?? second?.imageFile ?? third?.imageFile ?? filename } + wallpaper = WallpaperType.image(imageFile, scale, scaleType) + case WallpaperType.empty: + wallpaper = WallpaperType.empty + } + let background = (first?.background ?? second?.background ?? third?.background)?.colorFromReadableHex() ?? mainType.defaultBackgroundColor(base, themeBackgroundColor) + let tint = (first?.tint ?? second?.tint ?? third?.tint)?.colorFromReadableHex() ?? mainType.defaultTintColor(base) + + return AppWallpaper(background: background, tint: tint, type: wallpaper) + } + + public func withFilledColors(_ base: DefaultTheme, _ perChatTheme: ThemeColors?, _ perChatWallpaperType: WallpaperType?, _ perUserTheme: ThemeColors?, _ perUserWallpaperType: WallpaperType?, _ presetWallpaperTheme: ThemeColors?) -> ThemeColors { + let c = toColors(base, perChatTheme, perUserTheme, presetWallpaperTheme) + let ac = toAppColors(base, perChatTheme, perChatWallpaperType, perUserTheme, perUserWallpaperType, presetWallpaperTheme) + return ThemeColors( + primary: c.primary.toReadableHex(), + primaryVariant: c.primaryVariant.toReadableHex(), + secondary: c.secondary.toReadableHex(), + secondaryVariant: c.secondaryVariant.toReadableHex(), + background: c.background.toReadableHex(), + surface: c.surface.toReadableHex(), + title: ac.title.toReadableHex(), + primaryVariant2: ac.primaryVariant2.toReadableHex(), + sentMessage: ac.sentMessage.toReadableHex(), + sentQuote: ac.sentQuote.toReadableHex(), + receivedMessage: ac.receivedMessage.toReadableHex(), + receivedQuote: ac.receivedQuote.toReadableHex() + ) + } +} + +extension [ThemeOverrides] { + public func getTheme(_ themeId: String?) -> ThemeOverrides? { + self.first { $0.themeId == themeId } + } + + public func getTheme(_ themeId: String?, _ type: WallpaperType?, _ base: DefaultTheme) -> ThemeOverrides? { + self.first { $0.themeId == themeId || $0.isSame(type, base.themeName) } + } + + public func replace(_ theme: ThemeOverrides) -> [ThemeOverrides] { + let index = self.firstIndex { $0.themeId == theme.themeId || + // prevent situation when two themes has the same type but different theme id (maybe something was changed in prefs by hand) + $0.isSame(WallpaperType.from(theme.wallpaper), theme.base.themeName) + } + var a = self.map { $0 } + if let index { + a[index] = theme + } else { + a.append(theme) + } + return a + } + + public func sameTheme(_ type: WallpaperType?, _ themeName: String) -> ThemeOverrides? { first { $0.isSame(type, themeName) } } + + public func skipDuplicates() -> [ThemeOverrides] { + var res: [ThemeOverrides] = [] + self.forEach { theme in + let themeType = WallpaperType.from(theme.wallpaper) + if !res.contains(where: { $0.themeId == theme.themeId || $0.isSame(themeType, theme.base.themeName) }) { + res.append(theme) + } + } + return res + } + +} + +public struct ThemeModeOverrides: Codable, Hashable { + public var light: ThemeModeOverride? = nil + public var dark: ThemeModeOverride? = nil + + public init(light: ThemeModeOverride? = nil, dark: ThemeModeOverride? = nil) { + self.light = light + self.dark = dark + } + + public func preferredMode(_ darkTheme: Bool) -> ThemeModeOverride? { + darkTheme ? dark : light + } +} + +public struct ThemeModeOverride: Codable, Equatable, Hashable { + public var mode: DefaultThemeMode// = CurrentColors.base.mode + public var colors: ThemeColors = ThemeColors() + public var wallpaper: ThemeWallpaper? = nil + + public init(mode: DefaultThemeMode, colors: ThemeColors = ThemeColors(), wallpaper: ThemeWallpaper? = nil) { + self.mode = mode + self.colors = colors + self.wallpaper = wallpaper + } + + public var type: WallpaperType? { WallpaperType.from(wallpaper) } + + public func withUpdatedColor(_ name: ThemeColor, _ color: String?) -> ThemeModeOverride { + var c = colors + var w = wallpaper + switch (name) { + case ThemeColor.primary: c.primary = color + case ThemeColor.primaryVariant: c.primaryVariant = color + case ThemeColor.secondary: c.secondary = color + case ThemeColor.secondaryVariant: c.secondaryVariant = color + case ThemeColor.background: c.background = color + case ThemeColor.surface: c.surface = color + case ThemeColor.title: c.title = color + case ThemeColor.primaryVariant2: c.primaryVariant2 = color + case ThemeColor.sentMessage: c.sentMessage = color + case ThemeColor.sentQuote: c.sentQuote = color + case ThemeColor.receivedMessage: c.receivedMessage = color + case ThemeColor.receivedQuote: c.receivedQuote = color + case ThemeColor.wallpaperBackground: w?.background = color + case ThemeColor.wallpaperTint: w?.tint = color + } + return ThemeModeOverride(mode: mode, colors: c, wallpaper: w) + } + + public static func withFilledAppDefaults(_ mode: DefaultThemeMode, _ base: DefaultTheme) -> ThemeModeOverride { + ThemeModeOverride( + mode: mode, + colors: ThemeOverrides(base: base).withFilledColors(base, nil, nil, nil, nil, nil), + wallpaper: ThemeWallpaper(preset: PresetWallpaper.school.filename) + ) + } +} + +public let DarkColorPalette = Colors( + primary: SimplexBlue, + primaryVariant: SimplexBlue, + secondary: HighOrLowlight, + secondaryVariant: DarkGray, + background: Color.black, + surface: Color(0xFF222222), + error: Color.red, + onBackground: Color.white, + onSurface: Color.white, + isLight: false +) +public let DarkColorPaletteApp = AppColors( + title: .white, + primaryVariant2: Color(0xFF18262E), + sentMessage: Color(0xFF18262E), + sentQuote: Color(0xFF1D3847), + receivedMessage: Color(0xff262627), + receivedQuote: Color(0xff373739) +) + +public let LightColorPalette = Colors ( + primary: SimplexBlue, + primaryVariant: SimplexBlue, + secondary: HighOrLowlight, + secondaryVariant: LightGray, + background: Color.white, + surface: Color.white, + error: Color.red, + onBackground: Color.black, + onSurface: Color.black, + isLight: true +) +public let LightColorPaletteApp = AppColors( + title: .black, + primaryVariant2: Color(0xFFE9F7FF), + sentMessage: Color(0xFFE9F7FF), + sentQuote: Color(0xFFD6F0FF), + receivedMessage: Color(0xfff5f5f6), + receivedQuote: Color(0xffececee) +) + +public let SimplexColorPalette = Colors( + primary: Color(0xFF70F0F9), + primaryVariant: Color(0xFF1298A5), + secondary: HighOrLowlight, + secondaryVariant: Color(0xFF2C464D), + background: Color(0xFF111528), + surface: Color(0xFF121C37), + error: Color.red, + onBackground: Color.white, + onSurface: Color.white, + isLight: false +) +public let SimplexColorPaletteApp = AppColors( + title: .white, + primaryVariant2: Color(0xFF172941), + sentMessage: Color(0xFF172941), + sentQuote: Color(0xFF1C3A57), + receivedMessage: Color(0xff25283a), + receivedQuote: Color(0xff36394a) +) + +public let BlackColorPalette = Colors( + primary: Color(0xff0077e0), + primaryVariant: Color(0xff0077e0), + secondary: HighOrLowlight, + secondaryVariant: DarkGray, + background: Color(0xff070707), + surface: Color(0xff161617), + error: Color.red, + onBackground: Color.white, + onSurface: Color.white, + isLight: false +) +public let BlackColorPaletteApp = AppColors( + title: .white, + primaryVariant2: Color(0xff243747), + sentMessage: Color(0xFF18262E), + sentQuote: Color(0xFF1D3847), + receivedMessage: Color(0xff1b1b1b), + receivedQuote: Color(0xff29292b) +) + +extension Colors { + public func updateColorsFrom(_ other: Colors) { + primary = other.primary + primaryVariant = other.primaryVariant + secondary = other.secondary + secondaryVariant = other.secondaryVariant + background = other.background + surface = other.surface + error = other.error + onBackground = other.onBackground + onSurface = other.onSurface + isLight = other.isLight + } +} + +extension AppColors { + public func updateColorsFrom(_ other: AppColors) { + title = other.title + primaryVariant2 = other.primaryVariant2 + sentMessage = other.sentMessage + sentQuote = other.sentQuote + receivedMessage = other.receivedMessage + receivedQuote = other.receivedQuote + } +} + +extension AppWallpaper { + public func updateWallpaperFrom(_ other: AppWallpaper) { + background = other.background + tint = other.tint + type = other.type + } +} diff --git a/apps/ios/SimpleXChat/dummy.m b/apps/ios/SimpleXChat/dummy.m index 64fbc32dd3..d26e108520 100644 --- a/apps/ios/SimpleXChat/dummy.m +++ b/apps/ios/SimpleXChat/dummy.m @@ -21,4 +21,13 @@ DIR *opendir$INODE64(const char *name) { return opendir(name); } +int readdir$INODE64(DIR *restrict dirp, struct dirent *restrict entry, + struct dirent **restrict result) { + return readdir_r(dirp, entry, result); +} + +DIR *fdopendir$INODE64(const char *name) { + return opendir(name); +} + #endif diff --git a/apps/ios/SimpleXChat/hs_init.c b/apps/ios/SimpleXChat/hs_init.c index adacd57310..e75173d6cf 100644 --- a/apps/ios/SimpleXChat/hs_init.c +++ b/apps/ios/SimpleXChat/hs_init.c @@ -25,6 +25,22 @@ void haskell_init(void) { } void haskell_init_nse(void) { + int argc = 7; + char *argv[] = { + "simplex", + "+RTS", // requires `hs_init_with_rtsopts` + "-A256k", // chunk size for new allocations + "-H512k", // initial heap size + "-F0.5", // heap growth triggering GC + "-Fd0.3", // memory return + "-c", // compacting garbage collector + 0 + }; + char **pargv = argv; + hs_init_with_rtsopts(&argc, &pargv); +} + +void haskell_init_se(void) { int argc = 7; char *argv[] = { "simplex", diff --git a/apps/ios/SimpleXChat/hs_init.h b/apps/ios/SimpleXChat/hs_init.h index a732fd7113..40be4fc263 100644 --- a/apps/ios/SimpleXChat/hs_init.h +++ b/apps/ios/SimpleXChat/hs_init.h @@ -13,4 +13,6 @@ void haskell_init(void); void haskell_init_nse(void); +void haskell_init_se(void); + #endif /* hs_init_h */ diff --git a/apps/ios/SimpleXChat/objc.h b/apps/ios/SimpleXChat/objc.h new file mode 100644 index 0000000000..a75a6dc5e4 --- /dev/null +++ b/apps/ios/SimpleXChat/objc.h @@ -0,0 +1,20 @@ +// +// objc.h +// SimpleX (iOS) +// +// Created by Stanislav Dmitrenko on 09.09.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +#ifndef objc_h +#define objc_h + +#import + +@interface ObjC : NSObject + ++ (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error; + +@end + +#endif /* objc_h */ diff --git a/apps/ios/SimpleXChat/objc.m b/apps/ios/SimpleXChat/objc.m new file mode 100644 index 0000000000..c6952578ab --- /dev/null +++ b/apps/ios/SimpleXChat/objc.m @@ -0,0 +1,25 @@ +// +// objc.m +// SimpleXChat +// +// Created by Stanislav Dmitrenko on 09.09.2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +#import "objc.h" + +@implementation ObjC + +// https://stackoverflow.com/a/36454808 ++ (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error { + @try { + tryBlock(); + return YES; + } + @catch (NSException *exception) { + *error = [[NSError alloc] initWithDomain: exception.name code: 0 userInfo: exception.userInfo]; + return NO; + } +} + +@end diff --git a/apps/ios/bg.lproj/Localizable.strings b/apps/ios/bg.lproj/Localizable.strings index 580c20d65a..e4bc8f2150 100644 --- a/apps/ios/bg.lproj/Localizable.strings +++ b/apps/ios/bg.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (може да се копира)"; @@ -31,30 +16,15 @@ /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- гласови съобщения до 5 минути.\n- персонализирано време за изчезване.\n- история на редактиране."; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 цветно!"; -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - /* No comment provided by engineer. */ "(new)" = "(ново)"; /* No comment provided by engineer. */ "(this device v%@)" = "(това устройство v%@)"; -/* No comment provided by engineer. */ -")" = ")"; - /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Допринеси](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -65,10 +35,7 @@ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Звезда в GitHub](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add contact**: to create a new invitation link, or connect via a link you received." = "**Добави контакт**: за създаване на нов линк или свързване чрез получен линк за връзка."; - -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Добави нов контакт**: за да създадете своя еднократен QR код или линк за вашия контакт."; +"**Create 1-time link**: to create and share a new invitation link." = "**Добави контакт**: за създаване на нов линк."; /* No comment provided by engineer. */ "**Create group**: to create a new group." = "**Създай група**: за създаване на нова група."; @@ -80,20 +47,26 @@ "**e2e encrypted** video call" = "**e2e криптирано** видео разговор"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**По поверително**: проверявайте новите съобщения на всеки 20 минути. Токенът на устройството се споделя със сървъра за чат SimpleX, но не и колко контакти или съобщения имате."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**По поверително**: проверявайте новите съобщения на всеки 20 минути. Токенът на устройството се споделя със сървъра за чат SimpleX, но не и колко контакти или съобщения имате."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Най-поверително**: не използвайте сървъра за известия SimpleX Chat, периодично проверявайте съобщенията във фонов режим (зависи от това колко често използвате приложението)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**Най-поверително**: не използвайте сървъра за известия SimpleX Chat, периодично проверявайте съобщенията във фонов режим (зависи от това колко често използвате приложението)."; + +/* No comment provided by engineer. */ +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Моля, обърнете внимание**: използването на една и съща база данни на две устройства ще наруши декриптирането на съобщенията от вашите връзки като защита на сигурността."; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Моля, обърнете внимание**: НЯМА да можете да възстановите или промените паролата, ако я загубите."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Препоръчително**: токенът на устройството и известията се изпращат до сървъра за уведомяване на SimpleX Chat, но не и съдържанието, размерът на съобщението или от кого е."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Препоръчително**: токенът на устройството и известията се изпращат до сървъра за уведомяване на SimpleX Chat, но не и съдържанието, размерът на съобщението или от кого е."; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Внимание**: Незабавните push известия изискват парола, запазена в Keychain."; +/* No comment provided by engineer. */ +"**Warning**: the archive will be removed." = "**Внимание**: архивът ще бъде изтрит."; + /* No comment provided by engineer. */ "*bold*" = "\\*удебелен*"; @@ -136,6 +109,9 @@ /* No comment provided by engineer. */ "%@ connected" = "%@ свързан"; +/* No comment provided by engineer. */ +"%@ downloaded" = "%@ изтеглено"; + /* notification title */ "%@ is connected!" = "%@ е свързан!"; @@ -146,11 +122,14 @@ "%@ is verified" = "%@ е потвърдено"; /* No comment provided by engineer. */ -"%@ servers" = "%@ сървъри"; +"%@ uploaded" = "%@ качено"; /* notification title */ "%@ wants to connect!" = "%@ иска да се свърже!"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + /* No comment provided by engineer. */ "%@, %@ and %lld members" = "%@, %@ и %lld членове"; @@ -217,9 +196,6 @@ /* No comment provided by engineer. */ "%lld new interface languages" = "%lld нови езици на интерфейса"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld секунда(и)"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld секунди"; @@ -265,7 +241,8 @@ /* No comment provided by engineer. */ "0s" = "0s"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "1 ден"; /* time interval */ @@ -274,12 +251,17 @@ /* No comment provided by engineer. */ "1 minute" = "1 минута"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "1 месец"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "1 седмица"; +/* No comment provided by engineer. */ +"1-time link" = "Еднократен линк"; + /* No comment provided by engineer. */ "5 minutes" = "5 минути"; @@ -314,10 +296,7 @@ "Abort changing address?" = "Откажи смяна на адрес?"; /* No comment provided by engineer. */ -"About SimpleX" = "За SimpleX"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "Повече за SimpleX адреса"; +"About operators" = "За операторите"; /* No comment provided by engineer. */ "About SimpleX Chat" = "За SimpleX Chat"; @@ -326,63 +305,113 @@ "above, then choose:" = "по-горе, след това избери:"; /* No comment provided by engineer. */ -"Accent color" = "Основен цвят"; +"Accent" = "Акцент"; /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "Приеми"; +/* No comment provided by engineer. */ +"Accept conditions" = "Приеми условията"; + /* No comment provided by engineer. */ "Accept connection request?" = "Приемане на заявка за връзка?"; /* notification body */ "Accept contact request from %@?" = "Приемане на заявка за контакт от %@?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "Приеми инкогнито"; /* call status */ "accepted call" = "обаждането прието"; +/* No comment provided by engineer. */ +"Accepted conditions" = "Приети условия"; + +/* No comment provided by engineer. */ +"Acknowledged" = "Потвърден"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Грешки при потвърждението"; + +/* No comment provided by engineer. */ +"Active connections" = "Активни връзки"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Добавете адрес към вашия профил, така че вашите контакти да могат да го споделят с други хора. Актуализацията на профила ще бъде изпратена до вашите контакти."; /* No comment provided by engineer. */ -"Add contact" = "Добави контакт"; - -/* No comment provided by engineer. */ -"Add preset servers" = "Добави предварително зададени сървъри"; +"Add friends" = "Добави приятели"; /* No comment provided by engineer. */ "Add profile" = "Добави профил"; /* No comment provided by engineer. */ -"Add server…" = "Добави сървър…"; +"Add server" = "Добави сървър"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "Добави сървъри чрез сканиране на QR кодове."; +/* No comment provided by engineer. */ +"Add team members" = "Добави членове на екипа"; + /* No comment provided by engineer. */ "Add to another device" = "Добави към друго устройство"; /* No comment provided by engineer. */ "Add welcome message" = "Добави съобщение при посрещане"; +/* No comment provided by engineer. */ +"Add your team members to the conversations." = "Добавете членовете на вашия екип към разговорите."; + +/* No comment provided by engineer. */ +"Added media & file servers" = "Добавени медийни и файлови сървъри"; + +/* No comment provided by engineer. */ +"Added message servers" = "Добавени сървъри за съобщения"; + +/* No comment provided by engineer. */ +"Additional accent" = "Допълнителен акцент"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Допълнителен акцент 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Допълнителен вторичен"; + /* No comment provided by engineer. */ "Address" = "Адрес"; /* No comment provided by engineer. */ "Address change will be aborted. Old receiving address will be used." = "Промяната на адреса ще бъде прекъсната. Ще се използва старият адрес за получаване."; +/* No comment provided by engineer. */ +"Address or 1-time link?" = "Адрес или еднократен линк?"; + +/* No comment provided by engineer. */ +"Address settings" = "Настройки на адреса"; + /* member role */ "admin" = "админ"; +/* feature role */ +"admins" = "администратори"; + +/* No comment provided by engineer. */ +"Admins can block a member for all." = "Администраторите могат да блокират член за всички."; + /* No comment provided by engineer. */ "Admins can create the links to join groups." = "Админите могат да създадат линкове за присъединяване към групи."; /* No comment provided by engineer. */ "Advanced network settings" = "Разширени мрежови настройки"; +/* No comment provided by engineer. */ +"Advanced settings" = "Разширени настройки"; + /* chat item text */ "agreeing encryption for %@…" = "съгласуване на криптиране за %@…"; @@ -398,9 +427,15 @@ /* No comment provided by engineer. */ "All data is erased when it is entered." = "Всички данни се изтриват при въвеждане."; +/* No comment provided by engineer. */ +"All data is kept private on your device." = "Всички данни се съхраняват поверително на вашето устройство."; + /* No comment provided by engineer. */ "All group members will remain connected." = "Всички членове на групата ще останат свързани."; +/* feature role */ +"all members" = "всички членове"; + /* No comment provided by engineer. */ "All messages will be deleted - this cannot be undone!" = "Всички съобщения ще бъдат изтрити - това не може да бъде отменено!"; @@ -410,21 +445,33 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Всички нови съобщения от %@ ще бъдат скрити!"; +/* profile dropdown */ +"All profiles" = "Всички профили"; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Всички ваши контакти ще останат свързани."; /* No comment provided by engineer. */ "All your contacts will remain connected. Profile update will be sent to your contacts." = "Всички ваши контакти ще останат свързани. Актуализацията на профила ще бъде изпратена до вашите контакти."; +/* No comment provided by engineer. */ +"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Всички ваши контакти, разговори и файлове ще бъдат сигурно криптирани и качени на парчета в конфигурираните XFTP релета."; + /* No comment provided by engineer. */ "Allow" = "Позволи"; /* No comment provided by engineer. */ "Allow calls only if your contact allows them." = "Позволи обаждания само ако вашият контакт ги разрешава."; +/* No comment provided by engineer. */ +"Allow calls?" = "Позволи обаждания?"; + /* No comment provided by engineer. */ "Allow disappearing messages only if your contact allows it to you." = "Позволи изчезващи съобщения само ако вашият контакт ги разрешава."; +/* No comment provided by engineer. */ +"Allow downgrade" = "Позволи понижаване"; + /* No comment provided by engineer. */ "Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Позволи необратимо изтриване на съобщение само ако вашият контакт го рарешава. (24 часа)"; @@ -440,12 +487,18 @@ /* No comment provided by engineer. */ "Allow sending disappearing messages." = "Разреши изпращането на изчезващи съобщения."; +/* No comment provided by engineer. */ +"Allow sharing" = "Позволи споделяне"; + /* No comment provided by engineer. */ "Allow to irreversibly delete sent messages. (24 hours)" = "Позволи необратимо изтриване на изпратените съобщения. (24 часа)"; /* No comment provided by engineer. */ "Allow to send files and media." = "Позволи изпращане на файлове и медия."; +/* No comment provided by engineer. */ +"Allow to send SimpleX links." = "Разрешаване на изпращане на SimpleX линкове."; + /* No comment provided by engineer. */ "Allow to send voice messages." = "Позволи изпращане на гласови съобщения."; @@ -482,6 +535,9 @@ /* pref value */ "always" = "винаги"; +/* No comment provided by engineer. */ +"Always use private routing." = "Винаги използвай поверително рутиране."; + /* No comment provided by engineer. */ "Always use relay" = "Винаги използвай реле"; @@ -494,9 +550,15 @@ /* No comment provided by engineer. */ "Answer call" = "Отговор на повикване"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "Протокол и код с отворен код – всеки може да оперира собствени сървъри."; + /* No comment provided by engineer. */ "App build: %@" = "Компилация на приложението: %@"; +/* No comment provided by engineer. */ +"App data migration" = "Миграция на данните от приложението"; + /* No comment provided by engineer. */ "App encrypts new local files (except videos)." = "Приложението криптира нови локални файлове (с изключение на видеоклипове)."; @@ -509,6 +571,9 @@ /* No comment provided by engineer. */ "App passcode is replaced with self-destruct passcode." = "Кода за достъп до приложение се заменя с код за самоунищожение."; +/* No comment provided by engineer. */ +"App session" = "Сесия на приложението"; + /* No comment provided by engineer. */ "App version" = "Версия на приложението"; @@ -518,6 +583,24 @@ /* No comment provided by engineer. */ "Appearance" = "Изглед"; +/* No comment provided by engineer. */ +"Apply" = "Приложи"; + +/* No comment provided by engineer. */ +"Apply to" = "Приложи към"; + +/* No comment provided by engineer. */ +"Archive and upload" = "Архивиране и качване"; + +/* No comment provided by engineer. */ +"Archive contacts to chat later." = "Архивирайте контактите, за да разговаряте по-късно."; + +/* No comment provided by engineer. */ +"Archived contacts" = "Архивирани контакти"; + +/* No comment provided by engineer. */ +"Archiving database" = "Архивиране на база данни"; + /* No comment provided by engineer. */ "Attach" = "Прикачи"; @@ -560,9 +643,15 @@ /* No comment provided by engineer. */ "Auto-accept images" = "Автоматично приемане на изображения"; +/* alert title */ +"Auto-accept settings" = "Автоматично приемане на настройки"; + /* No comment provided by engineer. */ "Back" = "Назад"; +/* No comment provided by engineer. */ +"Background" = "Фон"; + /* No comment provided by engineer. */ "Bad desktop address" = "Грешен адрес на настолното устройство"; @@ -578,12 +667,33 @@ /* No comment provided by engineer. */ "Bad message ID" = "Лошо ID на съобщението"; +/* No comment provided by engineer. */ +"Better calls" = "По-добри обаждания"; + /* No comment provided by engineer. */ "Better groups" = "По-добри групи"; +/* No comment provided by engineer. */ +"Better message dates." = "По-добри дати на съобщението."; + /* No comment provided by engineer. */ "Better messages" = "По-добри съобщения"; +/* No comment provided by engineer. */ +"Better networking" = "Подобрена мрежа"; + +/* No comment provided by engineer. */ +"Better notifications" = "Подобрени известия"; + +/* No comment provided by engineer. */ +"Better security ✅" = "По-добра сигурност ✅"; + +/* No comment provided by engineer. */ +"Better user experience" = "Подобрен интерфейс"; + +/* No comment provided by engineer. */ +"Black" = "Черна"; + /* No comment provided by engineer. */ "Block" = "Блокирай"; @@ -608,12 +718,19 @@ /* rcv group event chat item */ "blocked %@" = "блокиран %@"; -/* marked deleted chat item preview text */ +/* blocked chat item +marked deleted chat item preview text */ "blocked by admin" = "блокиран от админ"; /* No comment provided by engineer. */ "Blocked by admin" = "Блокиран от админ"; +/* No comment provided by engineer. */ +"Blur for better privacy." = "Размазване за по-добра поверителност."; + +/* No comment provided by engineer. */ +"Blur media" = "Размазване на медия"; + /* No comment provided by engineer. */ "bold" = "удебелен"; @@ -635,6 +752,12 @@ /* No comment provided by engineer. */ "Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Български, финландски, тайландски и украински - благодарение на потребителите и [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"; +/* No comment provided by engineer. */ +"Business address" = "Бизнес адрес"; + +/* No comment provided by engineer. */ +"Business chats" = "Бизнес чатове"; + /* No comment provided by engineer. */ "By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Чрез чат профил (по подразбиране) или [чрез връзка](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (БЕТА)."; @@ -653,30 +776,52 @@ /* No comment provided by engineer. */ "Calls" = "Обаждания"; +/* No comment provided by engineer. */ +"Calls prohibited!" = "Обажданията са забранени!"; + /* No comment provided by engineer. */ "Camera not available" = "Камерата е неодстъпна"; +/* No comment provided by engineer. */ +"Can't call contact" = "Обаждането на контакта не е позволено"; + +/* No comment provided by engineer. */ +"Can't call member" = "Обаждането на члена не е позволено"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Не може да покани контакта!"; /* No comment provided by engineer. */ "Can't invite contacts!" = "Не може да поканят контактите!"; -/* No comment provided by engineer. */ +/* alert action +alert button */ "Cancel" = "Отказ"; +/* No comment provided by engineer. */ +"Cancel migration" = "Отмени миграцията"; + /* feature offered item */ "cancelled %@" = "отменен %@"; /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "Няма достъп до Keychain за запазване на паролата за базата данни"; -/* No comment provided by engineer. */ +/* alert title */ "Cannot receive file" = "Файлът не може да бъде получен"; +/* snd error text */ +"Capacity exceeded - recipient did not receive previously sent messages." = "Капацитетът е надвишен - получателят не е получил предишно изпратените съобщения."; + +/* No comment provided by engineer. */ +"Cellular" = "Мобилна мрежа"; + /* No comment provided by engineer. */ "Change" = "Промени"; +/* authentication reason */ +"Change chat profiles" = "Промени чат профилите"; + /* No comment provided by engineer. */ "Change database passphrase?" = "Промяна на паролата на базата данни?"; @@ -702,7 +847,7 @@ "Change self-destruct mode" = "Промени режима на самоунищожение"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "Промени кода за достъп за самоунищожение"; /* chat item text */ @@ -720,20 +865,17 @@ /* chat item text */ "changing address…" = "промяна на адреса…"; -/* No comment provided by engineer. */ -"Chat archive" = "Архив на чата"; - /* No comment provided by engineer. */ "Chat console" = "Конзола"; /* No comment provided by engineer. */ -"Chat database" = "База данни за чата"; +"Chat database" = "База данни"; /* No comment provided by engineer. */ "Chat database deleted" = "Базата данни на чата е изтрита"; /* No comment provided by engineer. */ -"Chat database imported" = "Базата данни на чат е импортирана"; +"Chat database imported" = "Базата данни на е импортирана"; /* No comment provided by engineer. */ "Chat is running" = "Чатът работи"; @@ -744,25 +886,34 @@ /* No comment provided by engineer. */ "Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "Чатът е спрян. Ако вече сте използвали тази база данни на друго устройство, трябва да я прехвърлите обратно, преди да стартирате чата отново."; +/* No comment provided by engineer. */ +"Chat migrated!" = "Чатът е мигриран!"; + /* No comment provided by engineer. */ "Chat preferences" = "Чат настройки"; +/* No comment provided by engineer. */ +"Chat profile" = "Потребителски профил"; + /* No comment provided by engineer. */ "Chats" = "Чатове"; -/* No comment provided by engineer. */ +/* alert title */ "Check server address and try again." = "Проверете адреса на сървъра и опитайте отново."; /* No comment provided by engineer. */ "Chinese and Spanish interface" = "Китайски и Испански интерфейс"; +/* No comment provided by engineer. */ +"Choose _Migrate from another device_ on the new device and scan QR code." = "Изберете _Мигриране от друго устройство_ на новото устройство и сканирайте QR кода."; + /* No comment provided by engineer. */ "Choose file" = "Избери файл"; /* No comment provided by engineer. */ "Choose from library" = "Избери от библиотеката"; -/* No comment provided by engineer. */ +/* swipe action */ "Clear" = "Изчисти"; /* No comment provided by engineer. */ @@ -780,9 +931,6 @@ /* No comment provided by engineer. */ "colored" = "цветен"; -/* No comment provided by engineer. */ -"Colors" = "Цветове"; - /* server test step */ "Compare file" = "Сравни файл"; @@ -801,6 +949,9 @@ /* No comment provided by engineer. */ "Confirm database upgrades" = "Потвърди актуализаациите на базата данни"; +/* No comment provided by engineer. */ +"Confirm network settings" = "Потвърди мрежовите настройки"; + /* No comment provided by engineer. */ "Confirm new passphrase…" = "Потвърди новата парола…"; @@ -810,6 +961,12 @@ /* No comment provided by engineer. */ "Confirm password" = "Потвърди парола"; +/* No comment provided by engineer. */ +"Confirm that you remember database passphrase to migrate it." = "Потвърдете, че помните паролата на базата данни, преди да я мигрирате."; + +/* No comment provided by engineer. */ +"Confirm upload" = "Потвърди качването"; + /* server test step */ "Connect" = "Свързване"; @@ -885,7 +1042,7 @@ /* No comment provided by engineer. */ "Connecting to desktop" = "Свързване с настолно устройство"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "свързване…"; /* No comment provided by engineer. */ @@ -933,9 +1090,6 @@ /* notification */ "Contact is connected" = "Контактът е свързан"; -/* No comment provided by engineer. */ -"Contact is not connected yet!" = "Контактът все още не е свързан!"; - /* No comment provided by engineer. */ "Contact name" = "Име на контакт"; @@ -951,7 +1105,7 @@ /* No comment provided by engineer. */ "Continue" = "Продължи"; -/* chat item action */ +/* No comment provided by engineer. */ "Copy" = "Копирай"; /* No comment provided by engineer. */ @@ -961,19 +1115,19 @@ "Correct name to %@?" = "Поправи име на %@?"; /* No comment provided by engineer. */ -"Create" = "Създай"; +"Create" = "Създаване"; /* No comment provided by engineer. */ -"Create a group using a random profile." = "Създай група с автоматично генериран профилл."; +"Create 1-time link" = "Създаване на еднократна препратка"; /* No comment provided by engineer. */ -"Create an address to let people connect with you." = "Създайте адрес, за да позволите на хората да се свързват с вас."; +"Create a group using a random profile." = "Създаване група с автоматично създаден профил."; /* server test step */ -"Create file" = "Създай файл"; +"Create file" = "Създаване на файл"; /* No comment provided by engineer. */ -"Create group" = "Създай група"; +"Create group" = "Създаване на група"; /* No comment provided by engineer. */ "Create group link" = "Създай групов линк"; @@ -994,7 +1148,7 @@ "Create secret group" = "Създай тайна група"; /* No comment provided by engineer. */ -"Create SimpleX address" = "Създай SimpleX адрес"; +"Create SimpleX address" = "Създаване на адрес в SimpleX"; /* No comment provided by engineer. */ "Create your profile" = "Създай своя профил"; @@ -1006,7 +1160,7 @@ "Created at: %@" = "Създаден на: %@"; /* No comment provided by engineer. */ -"Created on %@" = "Създаден на %@"; +"Creating archive link" = "Създаване на архивен линк"; /* No comment provided by engineer. */ "Creating link…" = "Линкът се създава…"; @@ -1098,7 +1252,8 @@ /* message decrypt error item */ "Decryption error" = "Грешка при декриптиране"; -/* pref value */ +/* delete after time +pref value */ "default (%@)" = "по подразбиране (%@)"; /* No comment provided by engineer. */ @@ -1107,7 +1262,8 @@ /* No comment provided by engineer. */ "default (yes)" = "по подразбиране (да)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "Изтрий"; /* No comment provided by engineer. */ @@ -1128,12 +1284,6 @@ /* No comment provided by engineer. */ "Delete and notify contact" = "Изтрий и уведоми контакт"; -/* No comment provided by engineer. */ -"Delete archive" = "Изтрий архив"; - -/* No comment provided by engineer. */ -"Delete chat archive?" = "Изтриване на архива на чата?"; - /* No comment provided by engineer. */ "Delete chat profile" = "Изтрий чат профила"; @@ -1146,15 +1296,12 @@ /* No comment provided by engineer. */ "Delete contact" = "Изтрий контакт"; -/* No comment provided by engineer. */ -"Delete Contact" = "Изтрий контакт"; - -/* No comment provided by engineer. */ -"Delete contact?\nThis cannot be undone!" = "Изтрий контакт?\nТова не може да бъде отменено!"; - /* No comment provided by engineer. */ "Delete database" = "Изтрий базата данни"; +/* No comment provided by engineer. */ +"Delete database from this device" = "Изтриване на базата данни от това устройство"; + /* server test step */ "Delete file" = "Изтрий файл"; @@ -1191,7 +1338,7 @@ /* No comment provided by engineer. */ "Delete message?" = "Изтрий съобщението?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "Изтрий съобщенията"; /* No comment provided by engineer. */ @@ -1203,9 +1350,6 @@ /* No comment provided by engineer. */ "Delete old database?" = "Изтрий старата база данни?"; -/* No comment provided by engineer. */ -"Delete pending connection" = "Изтрий предстоящата връзка"; - /* No comment provided by engineer. */ "Delete pending connection?" = "Изтрий предстоящата връзка?"; @@ -1231,7 +1375,7 @@ "deleted contact" = "изтрит контакт"; /* rcv group event chat item */ -"deleted group" = "групата изтрита"; +"deleted group" = "групата е изтрита"; /* No comment provided by engineer. */ "Delivery" = "Доставка"; @@ -1282,7 +1426,7 @@ "Direct messages" = "Лични съобщения"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "Личните съобщения между членовете са забранени в тази група."; +"Direct messages between members are prohibited." = "Личните съобщения между членовете са забранени в тази група."; /* No comment provided by engineer. */ "Disable (keep overrides)" = "Деактивиране (запазване на промените)"; @@ -1306,7 +1450,7 @@ "Disappearing messages are prohibited in this chat." = "Изчезващите съобщения са забранени в този чат."; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "Изчезващите съобщения са забранени в тази група."; +"Disappearing messages are prohibited." = "Изчезващите съобщения са забранени в тази група."; /* No comment provided by engineer. */ "Disappears at" = "Изчезва в"; @@ -1347,9 +1491,22 @@ /* No comment provided by engineer. */ "Downgrade and open chat" = "Понижи версията и отвори чата"; +/* alert button +chat item action */ +"Download" = "Изтегли"; + +/* No comment provided by engineer. */ +"Download failed" = "Неуспешно изтегляне"; + /* server test step */ "Download file" = "Свали файл"; +/* No comment provided by engineer. */ +"Downloading archive" = "Архива се изтегля"; + +/* No comment provided by engineer. */ +"Downloading link details" = "Подробности за линка се изтеглят"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Дублирано име!"; @@ -1374,7 +1531,7 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "Активиране (запазване на промените)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "Активиране на автоматично изтриване на съобщения?"; /* No comment provided by engineer. */ @@ -1383,6 +1540,9 @@ /* No comment provided by engineer. */ "Enable for all" = "Активиране за всички"; +/* No comment provided by engineer. */ +"Enable in direct chats (BETA)!" = "Активиране в личните чатове (БЕТА)!"; + /* No comment provided by engineer. */ "Enable instant notifications?" = "Активирай незабавни известия?"; @@ -1410,6 +1570,9 @@ /* enabled status */ "enabled" = "активирано"; +/* No comment provided by engineer. */ +"Enabled for" = "Активирано за"; + /* enabled status */ "enabled for contact" = "активирано за контакт"; @@ -1497,6 +1660,9 @@ /* No comment provided by engineer. */ "Enter Passcode" = "Въведете kодa за достъп"; +/* No comment provided by engineer. */ +"Enter passphrase" = "Въведи парола"; + /* No comment provided by engineer. */ "Enter passphrase…" = "Въведи парола…"; @@ -1530,9 +1696,6 @@ /* No comment provided by engineer. */ "Error accepting contact request" = "Грешка при приемане на заявка за контакт"; -/* No comment provided by engineer. */ -"Error accessing database file" = "Грешка при достъпа до файла с базата данни"; - /* No comment provided by engineer. */ "Error adding member(s)" = "Грешка при добавяне на член(ове)"; @@ -1567,7 +1730,7 @@ "Error decrypting file" = "Грешка при декриптирането на файла"; /* No comment provided by engineer. */ -"Error deleting chat database" = "Грешка при изтриване на чат базата данни"; +"Error deleting chat database" = "Грешка при изтриване на базата данни"; /* No comment provided by engineer. */ "Error deleting chat!" = "Грешка при изтриването на чата!"; @@ -1575,9 +1738,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "Грешка при изтриване на връзката"; -/* No comment provided by engineer. */ -"Error deleting contact" = "Грешка при изтриване на контакт"; - /* No comment provided by engineer. */ "Error deleting database" = "Грешка при изтриване на базата данни"; @@ -1590,6 +1750,9 @@ /* No comment provided by engineer. */ "Error deleting user profile" = "Грешка при изтриване на потребителския профил"; +/* No comment provided by engineer. */ +"Error downloading the archive" = "Грешка при изтеглянето на архива"; + /* No comment provided by engineer. */ "Error enabling delivery receipts!" = "Грешка при активирането на потвърждениeто за доставка!"; @@ -1600,29 +1763,23 @@ "Error encrypting database" = "Грешка при криптиране на базата данни"; /* No comment provided by engineer. */ -"Error exporting chat database" = "Грешка при експортиране на чат базата данни"; +"Error exporting chat database" = "Грешка при експортиране на базата данни"; /* No comment provided by engineer. */ -"Error importing chat database" = "Грешка при импортиране на чат базата данни"; +"Error importing chat database" = "Грешка при импортиране на базата данни"; /* No comment provided by engineer. */ "Error joining group" = "Грешка при присъединяване към група"; -/* No comment provided by engineer. */ -"Error loading %@ servers" = "Грешка при зареждане на %@ сървъри"; - /* No comment provided by engineer. */ "Error opening chat" = "Грешка при отваряне на чата"; -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Грешка при получаване на файл"; /* No comment provided by engineer. */ "Error removing member" = "Грешка при отстраняване на член"; -/* No comment provided by engineer. */ -"Error saving %@ servers" = "Грешка при запазване на %@ сървъра"; - /* No comment provided by engineer. */ "Error saving group profile" = "Грешка при запазване на профила на групата"; @@ -1635,6 +1792,9 @@ /* No comment provided by engineer. */ "Error saving passphrase to keychain" = "Грешка при запазване на парола в Кeychain"; +/* when migrating */ +"Error saving settings" = "Грешка при запазване на настройките"; + /* No comment provided by engineer. */ "Error saving user password" = "Грешка при запазване на потребителска парола"; @@ -1659,7 +1819,7 @@ /* No comment provided by engineer. */ "Error stopping chat" = "Грешка при спиране на чата"; -/* No comment provided by engineer. */ +/* alertTitle */ "Error switching profile!" = "Грешка при смяна на профил!"; /* No comment provided by engineer. */ @@ -1678,9 +1838,17 @@ "Error updating user privacy" = "Грешка при актуализиране на поверителността на потребителя"; /* No comment provided by engineer. */ -"Error: " = "Грешка: "; +"Error uploading the archive" = "Грешка при качването на архива"; /* No comment provided by engineer. */ +"Error verifying passphrase:" = "Грешка при проверката на паролата:"; + +/* No comment provided by engineer. */ +"Error: " = "Грешка: "; + +/* alert message +file error text +snd error text */ "Error: %@" = "Грешка: %@"; /* No comment provided by engineer. */ @@ -1692,9 +1860,6 @@ /* No comment provided by engineer. */ "Even when disabled in the conversation." = "Дори когато е деактивиран в разговора."; -/* No comment provided by engineer. */ -"event happened" = "събитие се случи"; - /* No comment provided by engineer. */ "Exit without saving" = "Изход без запазване"; @@ -1710,6 +1875,9 @@ /* No comment provided by engineer. */ "Exported database archive." = "Експортиран архив на базата данни."; +/* No comment provided by engineer. */ +"Exported file doesn't exist" = "Експортираният файл не съществува"; + /* No comment provided by engineer. */ "Exporting database archive…" = "Експортиране на архив на базата данни…"; @@ -1722,7 +1890,7 @@ /* No comment provided by engineer. */ "Faster joining and more reliable messages." = "По-бързо присъединяване и по-надеждни съобщения."; -/* No comment provided by engineer. */ +/* swipe action */ "Favorite" = "Любим"; /* No comment provided by engineer. */ @@ -1744,7 +1912,10 @@ "Files and media" = "Файлове и медия"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "Файловете и медията са забранени в тази група."; +"Files and media are prohibited." = "Файловете и медията са забранени в тази група."; + +/* No comment provided by engineer. */ +"Files and media not allowed" = "Файлове и медия не са разрешени"; /* No comment provided by engineer. */ "Files and media prohibited!" = "Файловете и медията са забранени!"; @@ -1752,6 +1923,12 @@ /* No comment provided by engineer. */ "Filter unread and favorite chats." = "Филтрирайте непрочетените и любимите чатове."; +/* No comment provided by engineer. */ +"Finalize migration" = "Завърши миграцията"; + +/* No comment provided by engineer. */ +"Finalize migration on another device." = "Завършете миграцията на другото устройство."; + /* No comment provided by engineer. */ "Finally, we have them! 🚀" = "Най-накрая ги имаме! 🚀"; @@ -1779,6 +1956,21 @@ /* No comment provided by engineer. */ "For console" = "За конзолата"; +/* chat item action */ +"Forward" = "Препрати"; + +/* No comment provided by engineer. */ +"Forward and save messages" = "Препращане и запазване на съобщения"; + +/* No comment provided by engineer. */ +"forwarded" = "препратено"; + +/* No comment provided by engineer. */ +"Forwarded" = "Препратено"; + +/* No comment provided by engineer. */ +"Forwarded from" = "Препратено от"; + /* No comment provided by engineer. */ "Found desktop" = "Намерено настолно устройство"; @@ -1791,9 +1983,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Пълно име (незадължително)"; -/* No comment provided by engineer. */ -"Full name:" = "Пълно име:"; - /* No comment provided by engineer. */ "Fully decentralized – visible only to members." = "Напълно децентрализирана – видима е само за членовете."; @@ -1842,24 +2031,6 @@ /* No comment provided by engineer. */ "Group links" = "Групови линкове"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "Членовете на групата могат да добавят реакции към съобщенията."; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "Членовете на групата могат необратимо да изтриват изпратените съобщения. (24 часа)"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "Членовете на групата могат да изпращат лични съобщения."; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "Членовете на групата могат да изпращат изчезващи съобщения."; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "Членовете на групата могат да изпращат файлове и медия."; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "Членовете на групата могат да изпращат гласови съобщения."; - /* notification */ "Group message:" = "Групово съобщение:"; @@ -1920,9 +2091,6 @@ /* time unit */ "hours" = "часове"; -/* No comment provided by engineer. */ -"How it works" = "Как работи"; - /* No comment provided by engineer. */ "How SimpleX works" = "Как работи SimpleX"; @@ -1935,6 +2103,9 @@ /* No comment provided by engineer. */ "How to use your servers" = "Как да използвате вашите сървъри"; +/* No comment provided by engineer. */ +"Hungarian interface" = "Унгарски интерфейс"; + /* No comment provided by engineer. */ "ICE servers (one per line)" = "ICE сървъри (по един на ред)"; @@ -1963,17 +2134,23 @@ "Immediately" = "Веднага"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "Защитен от спам и злоупотреби"; +"Immune to spam" = "Защитен от спам и злоупотреби"; /* No comment provided by engineer. */ "Import" = "Импортиране"; /* No comment provided by engineer. */ -"Import chat database?" = "Импортиране на чат база данни?"; +"Import chat database?" = "Импортиране на база данни?"; /* No comment provided by engineer. */ "Import database" = "Импортиране на база данни"; +/* No comment provided by engineer. */ +"Import failed" = "Неуспешно импортиране"; + +/* No comment provided by engineer. */ +"Importing archive" = "Импортиране на архив"; + /* No comment provided by engineer. */ "Improved message delivery" = "Подобрена доставка на съобщения"; @@ -1983,9 +2160,15 @@ /* No comment provided by engineer. */ "Improved server configuration" = "Подобрена конфигурация на сървъра"; +/* No comment provided by engineer. */ +"In order to continue, chat should be stopped." = "За да продължите, чатът трябва да бъде спрян."; + /* No comment provided by engineer. */ "In reply to" = "В отговор на"; +/* No comment provided by engineer. */ +"In-call sounds" = "Звуци по време на разговор"; + /* No comment provided by engineer. */ "Incognito" = "Инкогнито"; @@ -2041,10 +2224,10 @@ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Инсталирайте [SimpleX Chat за терминал](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"Instant push notifications will be hidden!\n" = "Незабавните push известия ще бъдат скрити!\n"; +"Instant" = "Мигновено"; /* No comment provided by engineer. */ -"Instantly" = "Мигновено"; +"Instant push notifications will be hidden!\n" = "Незабавните push известия ще бъдат скрити!\n"; /* No comment provided by engineer. */ "Interface" = "Интерфейс"; @@ -2067,6 +2250,9 @@ /* No comment provided by engineer. */ "Invalid link" = "Невалиден линк"; +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Невалидно потвърждение за мигриране"; + /* No comment provided by engineer. */ "Invalid name!" = "Невалидно име!"; @@ -2076,7 +2262,7 @@ /* No comment provided by engineer. */ "Invalid response" = "Невалиден отговор"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "Невалиден адрес на сървъра!"; /* item status text */ @@ -2122,7 +2308,7 @@ "Irreversible message deletion is prohibited in this chat." = "Необратимото изтриване на съобщения е забранено в този чат."; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "Необратимото изтриване на съобщения е забранено в тази група."; +"Irreversible message deletion is prohibited." = "Необратимото изтриване на съобщения е забранено в тази група."; /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "Позволява да имате много анонимни връзки без споделени данни между тях в един чат профил ."; @@ -2145,7 +2331,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "Японски интерфейс"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "Присъединяване"; /* No comment provided by engineer. */ @@ -2172,13 +2358,13 @@ /* No comment provided by engineer. */ "Joining group" = "Присъединяване към групата"; -/* No comment provided by engineer. */ +/* alert action */ "Keep" = "Запази"; /* No comment provided by engineer. */ "Keep the app open to use it from desktop" = "Дръжте приложението отворено, за да го използвате от настолното устройство"; -/* No comment provided by engineer. */ +/* alert title */ "Keep unused invitation?" = "Запази неизползваната покана за връзка?"; /* No comment provided by engineer. */ @@ -2196,7 +2382,7 @@ /* No comment provided by engineer. */ "Learn more" = "Научете повече"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "Напусни"; /* No comment provided by engineer. */ @@ -2235,9 +2421,6 @@ /* No comment provided by engineer. */ "Live messages" = "Съобщения на живо"; -/* No comment provided by engineer. */ -"Local" = "Локално"; - /* No comment provided by engineer. */ "Local name" = "Локално име"; @@ -2250,24 +2433,15 @@ /* No comment provided by engineer. */ "Lock mode" = "Режим на заключване"; -/* No comment provided by engineer. */ -"Make a private connection" = "Добави поверителна връзка"; - /* No comment provided by engineer. */ "Make one message disappear" = "Накарайте едно съобщение да изчезне"; /* No comment provided by engineer. */ "Make profile private!" = "Направи профила поверителен!"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Уверете се, че %@ сървърните адреси са в правилен формат, разделени на редове и не се дублират (%@)."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Уверете се, че адресите на WebRTC ICE сървъра са в правилен формат, разделени на редове и не са дублирани."; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Много хора попитаха: *ако SimpleX няма потребителски идентификатори, как може да доставя съобщения?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "Маркирай като изтрито за всички"; @@ -2307,6 +2481,27 @@ /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "Членът ще бъде премахнат от групата - това не може да бъде отменено!"; +/* No comment provided by engineer. */ +"Members can add message reactions." = "Членовете на групата могат да добавят реакции към съобщенията."; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "Членовете на групата могат необратимо да изтриват изпратените съобщения. (24 часа)"; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "Членовете на групата могат да изпращат лични съобщения."; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "Членовете на групата могат да изпращат изчезващи съобщения."; + +/* No comment provided by engineer. */ +"Members can send files and media." = "Членовете на групата могат да изпращат файлове и медия."; + +/* No comment provided by engineer. */ +"Members can send SimpleX links." = "Членовете на групата могат да изпращат SimpleX линкове."; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "Членовете на групата могат да изпращат гласови съобщения."; + /* item status text */ "Message delivery error" = "Грешка при доставката на съобщението"; @@ -2323,14 +2518,20 @@ "Message reactions are prohibited in this chat." = "Реакциите на съобщения са забранени в този чат."; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "Реакциите на съобщения са забранени в тази група."; +"Message reactions are prohibited." = "Реакциите на съобщения са забранени в тази група."; /* notification */ "message received" = "получено съобщение"; +/* No comment provided by engineer. */ +"Message source remains private." = "Източникът на съобщението остава скрит."; + /* No comment provided by engineer. */ "Message text" = "Текст на съобщението"; +/* No comment provided by engineer. */ +"Message too large" = "Съобщението е твърде голямо"; + /* No comment provided by engineer. */ "Messages" = "Съобщения"; @@ -2340,9 +2541,36 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "Съобщенията от %@ ще бъдат показани!"; +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Съобщенията, файловете и разговорите са защитени чрез **криптиране от край до край** с перфектна секретност при препращане, правдоподобно опровержение и възстановяване при взлом."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Съобщенията, файловете и разговорите са защитени чрез **квантово устойчиво e2e криптиране** с перфектна секретност при препращане, правдоподобно опровержение и възстановяване при взлом."; + +/* No comment provided by engineer. */ +"Migrate device" = "Мигрирай устройството"; + +/* No comment provided by engineer. */ +"Migrate from another device" = "Мигриране от друго устройство"; + +/* No comment provided by engineer. */ +"Migrate here" = "Мигрирай тук"; + +/* No comment provided by engineer. */ +"Migrate to another device" = "Миграция към друго устройство"; + +/* No comment provided by engineer. */ +"Migrate to another device via QR code." = "Мигрирайте към друго устройство чрез QR код."; + +/* No comment provided by engineer. */ +"Migrating" = "Мигриране"; + /* No comment provided by engineer. */ "Migrating database archive…" = "Архивът на базата данни се мигрира…"; +/* No comment provided by engineer. */ +"Migration complete" = "Миграцията е завършена"; + /* No comment provided by engineer. */ "Migration error:" = "Грешка при мигриране:"; @@ -2353,7 +2581,7 @@ "Migration is completed" = "Миграцията е завършена"; /* No comment provided by engineer. */ -"Migrations: %@" = "Миграции: %@"; +"Migrations:" = "Миграции:"; /* time unit */ "minutes" = "минути"; @@ -2382,34 +2610,40 @@ /* No comment provided by engineer. */ "More improvements are coming soon!" = "Очаквайте скоро още подобрения!"; +/* No comment provided by engineer. */ +"More reliable network connection." = "По-надеждна мрежова връзка."; + /* item status description */ "Most likely this connection is deleted." = "Най-вероятно тази връзка е изтрита."; -/* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "Най-вероятно този контакт е изтрил връзката с вас."; - /* No comment provided by engineer. */ "Multiple chat profiles" = "Множество профили за чат"; -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "Без звук"; /* No comment provided by engineer. */ "Muted when inactive!" = "Без звук при неактивност!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "Име"; /* No comment provided by engineer. */ "Network & servers" = "Мрежа и сървъри"; +/* No comment provided by engineer. */ +"Network connection" = "Мрежова връзка"; + +/* No comment provided by engineer. */ +"Network management" = "Управление на мрежата"; + /* No comment provided by engineer. */ "Network settings" = "Мрежови настройки"; /* No comment provided by engineer. */ "Network status" = "Състояние на мрежата"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "никога"; /* No comment provided by engineer. */ @@ -2421,9 +2655,6 @@ /* notification */ "New contact:" = "Нов контакт:"; -/* No comment provided by engineer. */ -"New database archive" = "Нов архив на база данни"; - /* No comment provided by engineer. */ "New desktop app!" = "Ново настолно приложение!"; @@ -2481,15 +2712,24 @@ /* No comment provided by engineer. */ "No history" = "Няма история"; +/* No comment provided by engineer. */ +"No network connection" = "Няма мрежова връзка"; + /* No comment provided by engineer. */ "No permission to record voice message" = "Няма разрешение за запис на гласово съобщение"; +/* No comment provided by engineer. */ +"No push server" = "Локално"; + /* No comment provided by engineer. */ "No received or sent files" = "Няма получени или изпратени файлове"; /* copied message info in history */ "no text" = "няма текст"; +/* No comment provided by engineer. */ +"No user identifiers." = "Първата платформа без никакви потребителски идентификатори – поверителна по дизайн."; + /* No comment provided by engineer. */ "Not compatible!" = "Несъвместим!"; @@ -2506,11 +2746,11 @@ "observer" = "наблюдател"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "изключено"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Изключено"; /* feature offered item */ @@ -2519,7 +2759,7 @@ /* feature offered item */ "offered %@: %@" = "предлага %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ок"; /* No comment provided by engineer. */ @@ -2528,9 +2768,6 @@ /* No comment provided by engineer. */ "Old database" = "Стара база данни"; -/* No comment provided by engineer. */ -"Old database archive" = "Стар архив на база данни"; - /* group pref value */ "on" = "включено"; @@ -2538,16 +2775,16 @@ "One-time invitation link" = "Линк за еднократна покана"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "За свързване ще са необходими Onion хостове. Изисква се активиране на VPN."; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "За свързване ще са **необходими** Onion хостове.\nИзисква се активиране на VPN."; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "Ще се използват Onion хостове, когато са налични. Изисква се активиране на VPN."; +"Onion hosts will be used when available.\nRequires compatible VPN." = "Ще се използват Onion хостове, когато са налични.\nИзисква се активиране на VPN."; /* No comment provided by engineer. */ "Onion hosts will not be used." = "Няма се използват Onion хостове."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Само потребителските устройства съхраняват потребителски профили, контакти, групи и съобщения, изпратени с **двуслойно криптиране от край до край**."; +"Only client devices store user profiles, contacts, groups, and messages." = "Само потребителските устройства съхраняват потребителски профили, контакти, групи и съобщения, изпратени с **двуслойно криптиране от край до край**."; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Само собствениците на групата могат да променят груповите настройки."; @@ -2588,7 +2825,7 @@ /* No comment provided by engineer. */ "Only your contact can send voice messages." = "Само вашият контакт може да изпраща гласови съобщения."; -/* No comment provided by engineer. */ +/* alert action */ "Open" = "Отвори"; /* No comment provided by engineer. */ @@ -2600,27 +2837,36 @@ /* No comment provided by engineer. */ "Open group" = "Отвори група"; +/* authentication reason */ +"Open migration to another device" = "Отвори миграцията към друго устройство"; + /* No comment provided by engineer. */ "Open Settings" = "Отвори настройки"; -/* authentication reason */ -"Open user profiles" = "Отвори потребителските профили"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "Протокол и код с отворен код – всеки може да оперира собствени сървъри."; - /* No comment provided by engineer. */ "Opening app…" = "Приложението се отваря…"; +/* No comment provided by engineer. */ +"Or paste archive link" = "Или постави архивен линк"; + /* No comment provided by engineer. */ "Or scan QR code" = "Или сканирай QR код"; +/* No comment provided by engineer. */ +"Or securely share this file link" = "Или сигурно споделете този линк към файла"; + /* No comment provided by engineer. */ "Or show this code" = "Или покажи този код"; +/* No comment provided by engineer. */ +"Other" = "Други"; + /* member role */ "owner" = "собственик"; +/* feature role */ +"owners" = "собственици"; + /* No comment provided by engineer. */ "Passcode" = "Код за достъп"; @@ -2658,14 +2904,14 @@ "peer-to-peer" = "peer-to-peer"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "Хората могат да се свържат с вас само чрез ликовете, които споделяте."; - -/* No comment provided by engineer. */ -"Periodically" = "Периодично"; +"Periodic" = "Периодично"; /* message decrypt error item */ "Permanent decryption error" = "Постоянна грешка при декриптиране"; +/* No comment provided by engineer. */ +"Picture-in-picture calls" = "Обаждания \"картина в картина\""; + /* No comment provided by engineer. */ "PING count" = "PING бройка"; @@ -2684,6 +2930,9 @@ /* No comment provided by engineer. */ "Please check yours and your contact preferences." = "Моля, проверете вашите настройки и тези вашия за контакт."; +/* No comment provided by engineer. */ +"Please confirm that network settings are correct for this device." = "Моля, потвърдете, че мрежовите настройки са правилни за това устройство."; + /* No comment provided by engineer. */ "Please contact developers.\nError: %@" = "Моля, свържете се с разработчиците.\nГрешка: %@"; @@ -2720,9 +2969,6 @@ /* No comment provided by engineer. */ "Preserve the last message draft, with attachments." = "Запазете последната чернова на съобщението с прикачени файлове."; -/* No comment provided by engineer. */ -"Preset server" = "Предварително зададен сървър"; - /* No comment provided by engineer. */ "Preset server address" = "Предварително зададен адрес на сървъра"; @@ -2748,15 +2994,12 @@ "Profile image" = "Профилно изображение"; /* No comment provided by engineer. */ -"Profile name" = "Име на профила"; - -/* No comment provided by engineer. */ -"Profile name:" = "Име на профила:"; +"Profile images" = "Профилни изображения"; /* No comment provided by engineer. */ "Profile password" = "Профилна парола"; -/* No comment provided by engineer. */ +/* alert message */ "Profile update will be sent to your contacts." = "Актуализацията на профила ще бъде изпратена до вашите контакти."; /* No comment provided by engineer. */ @@ -2780,6 +3023,9 @@ /* No comment provided by engineer. */ "Prohibit sending files and media." = "Забрани изпращането на файлове и медия."; +/* No comment provided by engineer. */ +"Prohibit sending SimpleX links." = "Забранете изпращането на SimpleX линкове."; + /* No comment provided by engineer. */ "Prohibit sending voice messages." = "Забрани изпращането на гласови съобщения."; @@ -2798,23 +3044,32 @@ /* No comment provided by engineer. */ "Push notifications" = "Push известия"; +/* No comment provided by engineer. */ +"Push server" = "Push сървър"; + +/* chat item text */ +"quantum resistant e2e encryption" = "квантово устойчиво e2e криптиране"; + +/* No comment provided by engineer. */ +"Quantum resistant encryption" = "Квантово устойчиво криптиране"; + /* No comment provided by engineer. */ "Rate the app" = "Оценете приложението"; /* chat item menu */ "React…" = "Реагирай…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "Прочетено"; /* No comment provided by engineer. */ "Read more" = "Прочетете още"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Прочетете повече в [Ръководство за потребителя](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Прочетете повече в [Ръководство за потребителя](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Прочетете повече в [Ръководство за потребителя](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "Прочетете повече в [Ръководство за потребителя](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)."; /* No comment provided by engineer. */ "Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Прочетете повече в [Ръководство на потребителя](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; @@ -2822,9 +3077,6 @@ /* No comment provided by engineer. */ "Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Прочетете повече в нашето [GitHub хранилище](https://github.com/simplex-chat/simplex-chat#readme)."; -/* No comment provided by engineer. */ -"Read more in our GitHub repository." = "Прочетете повече в нашето хранилище в GitHub."; - /* No comment provided by engineer. */ "Receipts are disabled" = "Потвърждениeто за доставка е деактивирано"; @@ -2858,6 +3110,9 @@ /* No comment provided by engineer. */ "Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)." = "Скорошна история и подобрен [bot за директория за групи](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPd jdLW3%23%2F%3Fv%3D1-2% 26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)."; +/* No comment provided by engineer. */ +"Recipient(s) can't see who this message is from." = "Получателят(ите) не могат да видят от кого е това съобщение."; + /* No comment provided by engineer. */ "Recipients see updates as you type them." = "Получателите виждат актуализации, докато ги въвеждате."; @@ -2876,7 +3131,8 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "Намалена консумация на батерията"; -/* reject incoming call via notification */ +/* reject incoming call via notification +swipe action */ "Reject" = "Отхвърляне"; /* No comment provided by engineer. */ @@ -2933,9 +3189,18 @@ /* No comment provided by engineer. */ "Repeat connection request?" = "Изпрати отново заявката за свързване?"; +/* No comment provided by engineer. */ +"Repeat download" = "Повтори изтеглянето"; + +/* No comment provided by engineer. */ +"Repeat import" = "Повтори импортирането"; + /* No comment provided by engineer. */ "Repeat join request?" = "Изпрати отново заявката за присъединяване?"; +/* No comment provided by engineer. */ +"Repeat upload" = "Повтори качването"; + /* chat item action */ "Reply" = "Отговори"; @@ -2975,9 +3240,6 @@ /* chat item action */ "Reveal" = "Покажи"; -/* No comment provided by engineer. */ -"Revert" = "Отмени промените"; - /* No comment provided by engineer. */ "Revoke" = "Отзови"; @@ -2993,13 +3255,17 @@ /* No comment provided by engineer. */ "Run chat" = "Стартиране на чат"; -/* chat item action */ +/* No comment provided by engineer. */ +"Safer groups" = "По-безопасни групи"; + +/* alert button +chat item action */ "Save" = "Запази"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Запази (и уведоми контактите)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "Запази и уведоми контакта"; /* No comment provided by engineer. */ @@ -3008,12 +3274,6 @@ /* No comment provided by engineer. */ "Save and update group profile" = "Запази и актуализирай профила на групата"; -/* No comment provided by engineer. */ -"Save archive" = "Запази архив"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Запази настройките за автоматично приемане"; - /* No comment provided by engineer. */ "Save group profile" = "Запази профила на групата"; @@ -3023,7 +3283,7 @@ /* No comment provided by engineer. */ "Save passphrase in Keychain" = "Запази паролата в Keychain"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "Запази настройките?"; /* No comment provided by engineer. */ @@ -3032,14 +3292,23 @@ /* No comment provided by engineer. */ "Save servers" = "Запази сървърите"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "Запази сървърите?"; /* No comment provided by engineer. */ -"Save settings?" = "Запази настройките?"; +"Save welcome message?" = "Запази съобщението при посрещане?"; /* No comment provided by engineer. */ -"Save welcome message?" = "Запази съобщението при посрещане?"; +"saved" = "запазено"; + +/* No comment provided by engineer. */ +"Saved" = "Запазено"; + +/* No comment provided by engineer. */ +"Saved from" = "Запазено от"; + +/* No comment provided by engineer. */ +"saved from %@" = "запазено от %@"; /* message info title */ "Saved message" = "Запазено съобщение"; @@ -3092,7 +3361,7 @@ /* chat item text */ "security code changed" = "кодът за сигурност е променен"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "Избери"; /* No comment provided by engineer. */ @@ -3119,9 +3388,6 @@ /* No comment provided by engineer. */ "send direct message" = "изпрати лично съобщение"; -/* No comment provided by engineer. */ -"Send direct message" = "Изпрати лично съобщение"; - /* No comment provided by engineer. */ "Send direct message to connect" = "Изпрати лично съобщение за свързване"; @@ -3137,9 +3403,6 @@ /* No comment provided by engineer. */ "Send notifications" = "Изпращай известия"; -/* No comment provided by engineer. */ -"Send notifications:" = "Изпратени известия:"; - /* No comment provided by engineer. */ "Send questions and ideas" = "Изпращайте въпроси и идеи"; @@ -3152,7 +3415,7 @@ /* No comment provided by engineer. */ "Send up to 100 last messages to new members." = "Изпращане до последните 100 съобщения на нови членове."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Подателят отмени прехвърлянето на файла."; /* No comment provided by engineer. */ @@ -3233,6 +3496,9 @@ /* No comment provided by engineer. */ "Set passcode" = "Задай kод за достъп"; +/* No comment provided by engineer. */ +"Set passphrase" = "Задаване на парола"; + /* No comment provided by engineer. */ "Set passphrase to export" = "Задай парола за експортиране"; @@ -3245,7 +3511,11 @@ /* No comment provided by engineer. */ "Settings" = "Настройки"; -/* chat item action */ +/* No comment provided by engineer. */ +"Shape profile images" = "Променете формата на профилните изображения"; + +/* alert action +chat item action */ "Share" = "Сподели"; /* No comment provided by engineer. */ @@ -3254,7 +3524,7 @@ /* No comment provided by engineer. */ "Share address" = "Сподели адрес"; -/* No comment provided by engineer. */ +/* alert title */ "Share address with contacts?" = "Сподели адреса с контактите?"; /* No comment provided by engineer. */ @@ -3278,6 +3548,9 @@ /* No comment provided by engineer. */ "Show preview" = "Показване на визуализация"; +/* No comment provided by engineer. */ +"Show QR code" = "Покажи QR код"; + /* No comment provided by engineer. */ "Show:" = "Покажи:"; @@ -3299,9 +3572,15 @@ /* simplex link type */ "SimpleX group link" = "SimpleX групов линк"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "SimpleX линкове"; +/* No comment provided by engineer. */ +"SimpleX links are prohibited." = "SimpleX линкове са забранени в тази група."; + +/* No comment provided by engineer. */ +"SimpleX links not allowed" = "SimpleX линковете не са разрешени"; + /* No comment provided by engineer. */ "SimpleX Lock" = "SimpleX заключване"; @@ -3329,15 +3608,18 @@ /* No comment provided by engineer. */ "Small groups (max 20)" = "Малки групи (максимум 20)"; -/* No comment provided by engineer. */ -"SMP servers" = "SMP сървъри"; - /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "Някои не-фатални грешки са възникнали по време на импортиране - може да видите конзолата за повече подробности."; /* notification title */ "Somebody" = "Някой"; +/* No comment provided by engineer. */ +"Square, circle, or anything in between." = "Квадрат, кръг или нещо между тях."; + +/* chat item text */ +"standard end-to-end encryption" = "стандартно криптиране от край до край"; + /* No comment provided by engineer. */ "Start chat" = "Започни чат"; @@ -3354,7 +3636,7 @@ "Stop" = "Спри"; /* No comment provided by engineer. */ -"Stop chat to enable database actions" = "Спрете чата, за да активирате действията с базата данни"; +"Stop chat" = "Спри чата"; /* No comment provided by engineer. */ "Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Спрете чата, за да експортирате, импортирате или изтриете чат базата данни. Няма да можете да получавате и изпращате съобщения, докато чатът е спрян."; @@ -3371,15 +3653,18 @@ /* No comment provided by engineer. */ "Stop sending file?" = "Спри изпращането на файла?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "Спри споделянето"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "Спри споделянето на адреса?"; /* authentication reason */ "Stop SimpleX" = "Спри SimpleX"; +/* No comment provided by engineer. */ +"Stopping chat" = "Спиране на чата"; + /* No comment provided by engineer. */ "strike" = "зачеркнат"; @@ -3419,9 +3704,6 @@ /* No comment provided by engineer. */ "Tap to scan" = "Докосни за сканиране"; -/* No comment provided by engineer. */ -"Tap to start a new chat" = "Докосни за започване на нов чат"; - /* No comment provided by engineer. */ "TCP connection timeout" = "Времето на изчакване за установяване на TCP връзка"; @@ -3443,7 +3725,7 @@ /* No comment provided by engineer. */ "Test servers" = "Тествай сървърите"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "Тестовете са неуспешни!"; /* No comment provided by engineer. */ @@ -3455,9 +3737,6 @@ /* No comment provided by engineer. */ "Thanks to the users – contribute via Weblate!" = "Благодарение на потребителите – допринесете през Weblate!"; -/* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "Първата платформа без никакви потребителски идентификатори – поверителна по дизайн."; - /* No comment provided by engineer. */ "The app can notify you when you receive messages or contact requests - please open settings to enable." = "Приложението може да ви уведоми, когато получите съобщения или заявки за контакт - моля, отворете настройките, за да активирате."; @@ -3479,6 +3758,9 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "Криптирането работи и новото споразумение за криптиране не е необходимо. Това може да доведе до грешки при свързване!"; +/* No comment provided by engineer. */ +"The future of messaging" = "Ново поколение поверителни съобщения"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "Хешът на предишното съобщение е различен."; @@ -3491,14 +3773,11 @@ /* No comment provided by engineer. */ "The message will be marked as moderated for all members." = "Съобщението ще бъде маркирано като модерирано за всички членове."; -/* No comment provided by engineer. */ -"The next generation of private messaging" = "Ново поколение поверителни съобщения"; - /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "Старата база данни не бе премахната по време на миграцията, тя може да бъде изтрита."; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "Профилът се споделя само с вашите контакти."; +"Your profile is stored on your device and only shared with your contacts." = "Профилът се споделя само с вашите контакти."; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "Втората отметка, която пропуснахме! ✅"; @@ -3512,9 +3791,6 @@ /* No comment provided by engineer. */ "The text you pasted is not a SimpleX link." = "Текстът, който поставихте, не е SimpleX линк за връзка."; -/* No comment provided by engineer. */ -"Theme" = "Тема"; - /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Тези настройки са за текущия ви профил **%@**."; @@ -3530,6 +3806,12 @@ /* No comment provided by engineer. */ "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Това действие не може да бъде отменено - вашият профил, контакти, съобщения и файлове ще бъдат безвъзвратно загубени."; +/* E2EE info chat item */ +"This chat is protected by end-to-end encryption." = "Този чат е защитен чрез криптиране от край до край."; + +/* E2EE info chat item */ +"This chat is protected by quantum resistant end-to-end encryption." = "Този чат е защитен от квантово устойчиво криптиране от край до край."; + /* notification title */ "this contact" = "този контакт"; @@ -3566,15 +3848,15 @@ /* No comment provided by engineer. */ "To make a new connection" = "За да направите нова връзка"; -/* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "За да се защити поверителността, вместо потребителски идентификатори, използвани от всички други платформи, SimpleX има идентификатори за опашки от съобщения, отделни за всеки от вашите контакти."; - /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "За да не се разкрива часовата зона, файловете с изображения/глас използват UTC."; /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "За да защитите информацията си, включете SimpleX заключване.\nЩе бъдете подканени да извършите идентификация, преди тази функция да бъде активирана."; +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "За да се защити поверителността, вместо потребителски идентификатори, използвани от всички други платформи, SimpleX има идентификатори за опашки от съобщения, отделни за всеки от вашите контакти."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "За да запишете гласово съобщение, моля, дайте разрешение за използване на микрофон."; @@ -3629,13 +3911,10 @@ /* rcv group event chat item */ "unblocked %@" = "отблокиран %@"; -/* item status description */ -"Unexpected error: %@" = "Неочаквана грешка: %@"; - /* No comment provided by engineer. */ "Unexpected migration state" = "Неочаквано състояние на миграция"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "Премахни от любимите"; /* No comment provided by engineer. */ @@ -3683,10 +3962,10 @@ /* authentication reason */ "Unlock app" = "Отключи приложението"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "Уведомявай"; -/* No comment provided by engineer. */ +/* swipe action */ "Unread" = "Непрочетено"; /* No comment provided by engineer. */ @@ -3695,18 +3974,12 @@ /* No comment provided by engineer. */ "Update" = "Актуализация"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = "Актуализиране на настройката за .onion хостове?"; - /* No comment provided by engineer. */ "Update database passphrase" = "Актуализирай паролата на базата данни"; /* No comment provided by engineer. */ "Update network settings?" = "Актуализиране на мрежовите настройки?"; -/* No comment provided by engineer. */ -"Update transport isolation mode?" = "Актуализиране на режима на изолация на транспорта?"; - /* rcv group event chat item */ "updated group profile" = "актуализиран профил на групата"; @@ -3717,14 +3990,17 @@ "Updating settings will re-connect the client to all servers." = "Актуализирането на настройките ще свърже отново клиента към всички сървъри."; /* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "Актуализирането на тази настройка ще свърже повторно клиента към всички сървъри."; +"Upgrade and open chat" = "Актуализирай и отвори чата"; /* No comment provided by engineer. */ -"Upgrade and open chat" = "Актуализирай и отвори чата"; +"Upload failed" = "Неуспешно качване"; /* server test step */ "Upload file" = "Качи файл"; +/* No comment provided by engineer. */ +"Uploading archive" = "Архивът се качва"; + /* No comment provided by engineer. */ "Use .onion hosts" = "Използвай .onion хостове"; @@ -3756,10 +4032,7 @@ "Use SimpleX Chat servers?" = "Използвай сървърите на SimpleX Chat?"; /* No comment provided by engineer. */ -"User profile" = "Потребителски профил"; - -/* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = "Използването на .onion хостове изисква съвместим VPN доставчик."; +"Use the app while in the call." = "Използвайте приложението по време на разговора."; /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Използват се сървърите на SimpleX Chat."; @@ -3782,6 +4055,12 @@ /* No comment provided by engineer. */ "Verify connections" = "Потвърждение за свързване"; +/* No comment provided by engineer. */ +"Verify database passphrase" = "Проверете паролата на базата данни"; + +/* No comment provided by engineer. */ +"Verify passphrase" = "Провери паролата"; + /* No comment provided by engineer. */ "Verify security code" = "Потвърди кода за сигурност"; @@ -3834,7 +4113,10 @@ "Voice messages are prohibited in this chat." = "Гласовите съобщения са забранени в този чат."; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "Гласовите съобщения са забранени в тази група."; +"Voice messages are prohibited." = "Гласовите съобщения са забранени в тази група."; + +/* No comment provided by engineer. */ +"Voice messages not allowed" = "Гласовите съобщения не са разрешени"; /* No comment provided by engineer. */ "Voice messages prohibited!" = "Гласовите съобщения са забранени!"; @@ -3860,6 +4142,9 @@ /* No comment provided by engineer. */ "wants to connect to you!" = "иска да се свърже с вас!"; +/* No comment provided by engineer. */ +"Warning: starting chat on multiple devices is not supported and will cause message delivery failures" = "Внимание: стартирането на чата на множество устройства не се поддържа и ще доведе до неуспешно изпращане на съобщения"; + /* No comment provided by engineer. */ "Warning: you may lose some data!" = "Предупреждение: Може да загубите някои данни!"; @@ -3875,6 +4160,9 @@ /* No comment provided by engineer. */ "Welcome message" = "Съобщение при посрещане"; +/* No comment provided by engineer. */ +"Welcome message is too long" = "Съобщението при посрещане е твърде дълго"; + /* No comment provided by engineer. */ "What's new" = "Какво е новото"; @@ -3882,11 +4170,20 @@ "When available" = "Когато са налични"; /* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "Когато хората искат да се свържат с вас, можете да ги приемете или отхвърлите."; +"When connecting audio and video calls." = "При свързване на аудио и видео разговори."; /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Когато споделяте инкогнито профил с някого, този профил ще се използва за групите, в които той ви кани."; +/* No comment provided by engineer. */ +"WiFi" = "WiFi"; + +/* No comment provided by engineer. */ +"Will be enabled in direct chats!" = "Ще бъде активирано в личните чатове!"; + +/* No comment provided by engineer. */ +"Wired ethernet" = "Кабелен Ethernet"; + /* No comment provided by engineer. */ "With encrypted files and media." = "С криптирани файлове и медия."; @@ -3902,14 +4199,14 @@ /* No comment provided by engineer. */ "Wrong passphrase!" = "Грешна парола!"; -/* No comment provided by engineer. */ -"XFTP servers" = "XFTP сървъри"; - /* pref value */ "yes" = "да"; /* No comment provided by engineer. */ -"You" = "Вие"; +"you" = "вие"; + +/* No comment provided by engineer. */ +"You **must not** use the same database on two devices." = "**Не трябва** да използвате една и съща база данни на две устройства."; /* No comment provided by engineer. */ "You accepted connection" = "Вие приехте връзката"; @@ -3971,6 +4268,9 @@ /* No comment provided by engineer. */ "You can enable them later via app Privacy & Security settings." = "Можете да ги активирате по-късно през настройките за \"Поверителност и сигурност\" на приложението."; +/* No comment provided by engineer. */ +"You can give another try." = "Можете да опитате още веднъж."; + /* No comment provided by engineer. */ "You can hide or mute a user profile - swipe it to the right." = "Можете да скриете или заглушите известията за потребителски профил - плъзнете надясно."; @@ -3978,7 +4278,7 @@ "You can make it visible to your SimpleX contacts via Settings." = "Можете да го направите видим за вашите контакти в SimpleX чрез Настройки."; /* notification body */ -"You can now send messages to %@" = "Вече можете да изпращате съобщения до %@"; +"You can now chat with %@" = "Вече можете да изпращате съобщения до %@"; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "Можете да зададете визуализация на известията на заключен екран през настройките."; @@ -3989,9 +4289,6 @@ /* No comment provided by engineer. */ "You can share this address with your contacts to let them connect with **%@**." = "Можете да споделите този адрес с вашите контакти, за да им позволите да се свържат с **%@**."; -/* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "Можете да споделите адреса си като линк или QR код - всеки може да се свърже с вас."; - /* No comment provided by engineer. */ "You can start chat via app Settings / Database or by restarting the app" = "Можете да започнете чат през Настройки на приложението / База данни или като рестартирате приложението"; @@ -4001,7 +4298,7 @@ /* No comment provided by engineer. */ "You can use markdown to format messages:" = "Можете да използвате markdown за форматиране на съобщенията:"; -/* No comment provided by engineer. */ +/* alert message */ "You can view invitation link again in connection details." = "Можете да видите отново линкът за покана в подробностите за връзката."; /* No comment provided by engineer. */ @@ -4020,10 +4317,10 @@ "you changed role of %@ to %@" = "променихте ролята на %1$@ на %2$@"; /* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "Вие контролирате през кой сървър(и) **да получавате** съобщенията, вашите контакти – сървърите, които използвате, за да им изпращате съобщения."; +"You could not be verified; please try again." = "Не можахте да бъдете потвърдени; Моля, опитайте отново."; /* No comment provided by engineer. */ -"You could not be verified; please try again." = "Не можахте да бъдете потвърдени; Моля, опитайте отново."; +"You decide who can connect." = "Хората могат да се свържат с вас само чрез ликовете, които споделяте."; /* No comment provided by engineer. */ "You have already requested connection via this address!" = "Вече сте заявили връзка през този адрес!"; @@ -4031,9 +4328,6 @@ /* No comment provided by engineer. */ "You have already requested connection!\nRepeat connection request?" = "Вече сте направили заявката за връзка!\nИзпрати отново заявката за свързване?"; -/* No comment provided by engineer. */ -"You have no chats" = "Нямате чатове"; - /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "Трябва да въвеждате парола при всяко стартиране на приложението - тя не се съхранява на устройството."; @@ -4109,24 +4403,18 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Използвате инкогнито профил за тази група - за да се предотврати споделянето на основния ви профил, поканите на контакти не са разрешени"; -/* No comment provided by engineer. */ -"Your %@ servers" = "Вашите %@ сървъри"; - /* No comment provided by engineer. */ "Your calls" = "Вашите обаждания"; /* No comment provided by engineer. */ -"Your chat database" = "Вашата чат база данни"; +"Your chat database" = "Вашата база данни"; /* No comment provided by engineer. */ -"Your chat database is not encrypted - set passphrase to encrypt it." = "Вашата чат база данни не е криптирана - задайте парола, за да я криптирате."; +"Your chat database is not encrypted - set passphrase to encrypt it." = "Вашата база данни не е криптирана - задайте парола, за да я криптирате."; /* No comment provided by engineer. */ "Your chat profiles" = "Вашите чат профили"; -/* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Вашият контакт трябва да бъде онлайн, за да осъществите връзката.\nМожете да откажете тази връзка и да премахнете контакта (и да опитате по -късно с нов линк)."; - /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "Вашият контакт изпрати файл, който е по-голям от поддържания в момента максимален размер (%@)."; @@ -4137,7 +4425,7 @@ "Your contacts will remain connected." = "Вашите контакти ще останат свързани."; /* No comment provided by engineer. */ -"Your current chat database will be DELETED and REPLACED with the imported one." = "Вашата текуща чат база данни ще бъде ИЗТРИТА и ЗАМЕНЕНА с импортираната."; +"Your current chat database will be DELETED and REPLACED with the imported one." = "Вашата текуща база данни ще бъде ИЗТРИТА и ЗАМЕНЕНА с импортираната."; /* No comment provided by engineer. */ "Your current profile" = "Вашият текущ профил"; @@ -4158,7 +4446,7 @@ "Your profile **%@** will be shared." = "Вашият профил **%@** ще бъде споделен."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Вашият профил се съхранява на вашето устройство и се споделя само с вашите контакти.\nSimpleX сървърите не могат да видят вашия профил."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Вашият профил се съхранява на вашето устройство и се споделя само с вашите контакти. SimpleX сървърите не могат да видят вашия профил."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Вашият профил, контакти и доставени съобщения се съхраняват на вашето устройство."; @@ -4166,9 +4454,6 @@ /* No comment provided by engineer. */ "Your random profile" = "Вашият автоматично генериран профил"; -/* No comment provided by engineer. */ -"Your server" = "Вашият сървър"; - /* No comment provided by engineer. */ "Your server address" = "Вашият адрес на сървъра"; @@ -4176,11 +4461,5 @@ "Your settings" = "Вашите настройки"; /* No comment provided by engineer. */ -"Your SimpleX address" = "Вашият SimpleX адрес"; - -/* No comment provided by engineer. */ -"Your SMP servers" = "Вашите SMP сървъри"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "Вашите XFTP сървъри"; +"Your SimpleX address" = "Вашият адрес в SimpleX"; diff --git a/apps/ios/cs.lproj/Localizable.strings b/apps/ios/cs.lproj/Localizable.strings index a777a33613..08a94615a3 100644 --- a/apps/ios/cs.lproj/Localizable.strings +++ b/apps/ios/cs.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (lze kopírovat)"; @@ -28,23 +13,11 @@ /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- 5 minutové hlasové zprávy.\n- vlastní čas mizení.\n- historie úprav."; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 barevný!"; /* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - -/* No comment provided by engineer. */ -")" = ")"; +"(this device v%@)" = "(toto zařízení v%@)"; /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Přispějte](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -55,9 +28,6 @@ /* No comment provided by engineer. */ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Hvězda na GitHubu](https://github.com/simplex-chat/simplex-chat)"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Přidat nový kontakt**: pro vytvoření jednorázového QR kódu nebo odkazu pro váš kontakt."; - /* No comment provided by engineer. */ "**e2e encrypted** audio call" = "**e2e šifrovaný** audio hovor"; @@ -65,16 +35,16 @@ "**e2e encrypted** video call" = "**e2e šifrovaný** videohovor"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Soukromější**: kontrolovat nové zprávy každých 20 minut. Token zařízení je sdílen se serverem SimpleX Chat, ale ne kolik máte kontaktů nebo zpráv."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Soukromější**: kontrolovat nové zprávy každých 20 minut. Token zařízení je sdílen se serverem SimpleX Chat, ale ne kolik máte kontaktů nebo zpráv."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Nejsoukromější**: nepoužívejte server oznámení SimpleX Chat, pravidelně kontrolujte zprávy na pozadí (závisí na tom, jak často aplikaci používáte)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**Nejsoukromější**: nepoužívejte server oznámení SimpleX Chat, pravidelně kontrolujte zprávy na pozadí (závisí na tom, jak často aplikaci používáte)."; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Upozornění**: Pokud heslo ztratíte, NEBUDETE jej moci obnovit ani změnit."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Doporučeno**: Token zařízení a oznámení se odesílají na oznamovací server SimpleX Chat, ale nikoli obsah, velikost nebo od koho jsou zprávy."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Doporučeno**: Token zařízení a oznámení se odesílají na oznamovací server SimpleX Chat, ale nikoli obsah, velikost nebo od koho jsou zprávy."; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Upozornění**: Okamžitě doručovaná oznámení vyžadují přístupové heslo uložené v Klíčence."; @@ -121,6 +91,9 @@ /* No comment provided by engineer. */ "%@ connected" = "%@ připojen"; +/* No comment provided by engineer. */ +"%@ downloaded" = "%@ staženo"; + /* notification title */ "%@ is connected!" = "%@ je připojen!"; @@ -130,12 +103,18 @@ /* No comment provided by engineer. */ "%@ is verified" = "%@ je ověřený"; +/* No comment provided by engineer. */ +"%@ server" = "%@ server"; + /* No comment provided by engineer. */ "%@ servers" = "%@ servery"; /* notification title */ "%@ wants to connect!" = "%@ se chce připojit!"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + /* No comment provided by engineer. */ "%@, %@ and %lld other members connected" = "%@, %@ a %lld ostatní členové připojeni"; @@ -145,6 +124,18 @@ /* time interval */ "%d days" = "%d dní"; +/* forward confirmation reason */ +"%d file(s) are still being downloaded." = "%d soubor(y) stále stahován(y)."; + +/* forward confirmation reason */ +"%d file(s) failed to download." = "%d soubor(y) se nepodařilo stáhnout."; + +/* forward confirmation reason */ +"%d file(s) were deleted." = "%d soubor(y) smazán(y)."; + +/* forward confirmation reason */ +"%d file(s) were not downloaded." = "%d soubor(y) nestažen(y)."; + /* time interval */ "%d hours" = "%d hodin"; @@ -178,15 +169,21 @@ /* No comment provided by engineer. */ "%lld members" = "%lld členové"; +/* No comment provided by engineer. */ +"%lld messages blocked" = "%lld zprávy blokovaný"; + +/* No comment provided by engineer. */ +"%lld messages blocked by admin" = "%lld zprávy blokovaný adminem"; + +/* No comment provided by engineer. */ +"%lld messages marked deleted" = "%lld zprávy označeno jako smazáno"; + /* No comment provided by engineer. */ "%lld minutes" = "%lld minut"; /* No comment provided by engineer. */ "%lld new interface languages" = "%d nové jazyky rozhraní"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld vteřin"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld vteřin"; @@ -229,7 +226,8 @@ /* No comment provided by engineer. */ "0s" = "0s"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "1 den"; /* time interval */ @@ -238,10 +236,12 @@ /* No comment provided by engineer. */ "1 minute" = "1 minutu"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "1 měsíc"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "1 týden"; /* No comment provided by engineer. */ @@ -277,23 +277,15 @@ /* No comment provided by engineer. */ "Abort changing address?" = "Přerušit změnu adresy?"; -/* No comment provided by engineer. */ -"About SimpleX" = "O SimpleX"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "O SimpleX adrese"; - /* No comment provided by engineer. */ "About SimpleX Chat" = "O SimpleX chat"; /* No comment provided by engineer. */ "above, then choose:" = "výše, pak vyberte:"; -/* No comment provided by engineer. */ -"Accent color" = "Zbarvení"; - /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "Přijmout"; /* No comment provided by engineer. */ @@ -302,7 +294,8 @@ /* notification body */ "Accept contact request from %@?" = "Přijmout žádost o kontakt od %@?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "Přijmout inkognito"; /* call status */ @@ -311,14 +304,11 @@ /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Přidejte adresu do svého profilu, aby ji vaše kontakty mohly sdílet s dalšími lidmi. Aktualizace profilu bude zaslána vašim kontaktům."; -/* No comment provided by engineer. */ -"Add preset servers" = "Přidejte přednastavené servery"; - /* No comment provided by engineer. */ "Add profile" = "Přidat profil"; /* No comment provided by engineer. */ -"Add server…" = "Přidat server…"; +"Add server" = "Přidat server"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "Přidejte servery skenováním QR kódů."; @@ -440,6 +430,9 @@ /* No comment provided by engineer. */ "Answer call" = "Přijmout hovor"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "Servery může provozovat kdokoli."; + /* No comment provided by engineer. */ "App build: %@" = "Sestavení aplikace: %@"; @@ -566,7 +559,8 @@ /* No comment provided by engineer. */ "Can't invite contacts!" = "Nelze pozvat kontakty!"; -/* No comment provided by engineer. */ +/* alert action +alert button */ "Cancel" = "Zrušit"; /* feature offered item */ @@ -575,7 +569,7 @@ /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "Nelze získat přístup ke klíčence pro uložení hesla databáze"; -/* No comment provided by engineer. */ +/* alert title */ "Cannot receive file" = "Nelze přijmout soubor"; /* No comment provided by engineer. */ @@ -606,7 +600,7 @@ "Change self-destruct mode" = "Změnit režim sebedestrukce"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "Změnit sebedestrukční heslo"; /* chat item text */ @@ -624,9 +618,6 @@ /* chat item text */ "changing address…" = "změna adresy…"; -/* No comment provided by engineer. */ -"Chat archive" = "Chat se archivuje"; - /* No comment provided by engineer. */ "Chat console" = "Konzola pro chat"; @@ -649,9 +640,12 @@ "Chat preferences" = "Předvolby chatu"; /* No comment provided by engineer. */ -"Chats" = "Chaty"; +"Chat profile" = "Profil uživatele"; /* No comment provided by engineer. */ +"Chats" = "Chaty"; + +/* alert title */ "Check server address and try again." = "Zkontrolujte adresu serveru a zkuste to znovu."; /* No comment provided by engineer. */ @@ -663,7 +657,7 @@ /* No comment provided by engineer. */ "Choose from library" = "Vybrat z knihovny"; -/* No comment provided by engineer. */ +/* swipe action */ "Clear" = "Vyčistit"; /* No comment provided by engineer. */ @@ -678,9 +672,6 @@ /* No comment provided by engineer. */ "colored" = "barevné"; -/* No comment provided by engineer. */ -"Colors" = "Barvy"; - /* server test step */ "Compare file" = "Porovnat soubor"; @@ -753,7 +744,7 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "Připojování k serveru... (chyba: %@)"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "připojení…"; /* No comment provided by engineer. */ @@ -795,9 +786,6 @@ /* notification */ "Contact is connected" = "Kontakt je připojen"; -/* No comment provided by engineer. */ -"Contact is not connected yet!" = "Kontakt ještě není připojen!"; - /* No comment provided by engineer. */ "Contact name" = "Jméno kontaktu"; @@ -813,7 +801,7 @@ /* No comment provided by engineer. */ "Continue" = "Pokračovat"; -/* chat item action */ +/* No comment provided by engineer. */ "Copy" = "Kopírovat"; /* No comment provided by engineer. */ @@ -822,9 +810,6 @@ /* No comment provided by engineer. */ "Create" = "Vytvořit"; -/* No comment provided by engineer. */ -"Create an address to let people connect with you." = "Vytvořit adresu, aby se s vámi lidé mohli spojit."; - /* server test step */ "Create file" = "Vytvořit soubor"; @@ -837,6 +822,9 @@ /* No comment provided by engineer. */ "Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" = "Vytvořit nový profil v [desktop app](https://simplex.chat/downloads/). 💻"; +/* No comment provided by engineer. */ +"Create profile" = "Vytvořte si profil"; + /* server test step */ "Create queue" = "Vytvořit frontu"; @@ -849,9 +837,6 @@ /* No comment provided by engineer. */ "Create your profile" = "Vytvořte si profil"; -/* No comment provided by engineer. */ -"Created on %@" = "Vytvořeno na %@"; - /* No comment provided by engineer. */ "creator" = "tvůrce"; @@ -939,7 +924,8 @@ /* message decrypt error item */ "Decryption error" = "Chyba dešifrování"; -/* pref value */ +/* delete after time +pref value */ "default (%@)" = "výchozí (%@)"; /* No comment provided by engineer. */ @@ -948,7 +934,8 @@ /* No comment provided by engineer. */ "default (yes)" = "výchozí (ano)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "Smazat"; /* No comment provided by engineer. */ @@ -963,12 +950,6 @@ /* No comment provided by engineer. */ "Delete all files" = "Odstranit všechny soubory"; -/* No comment provided by engineer. */ -"Delete archive" = "Smazat archiv"; - -/* No comment provided by engineer. */ -"Delete chat archive?" = "Smazat archiv chatu?"; - /* No comment provided by engineer. */ "Delete chat profile" = "Smazat chat profil"; @@ -981,9 +962,6 @@ /* No comment provided by engineer. */ "Delete contact" = "Smazat kontakt"; -/* No comment provided by engineer. */ -"Delete Contact" = "Smazat kontakt"; - /* No comment provided by engineer. */ "Delete database" = "Odstranění databáze"; @@ -1023,7 +1001,7 @@ /* No comment provided by engineer. */ "Delete message?" = "Smazat zprávu?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "Smazat zprávy"; /* No comment provided by engineer. */ @@ -1035,9 +1013,6 @@ /* No comment provided by engineer. */ "Delete old database?" = "Smazat starou databázi?"; -/* No comment provided by engineer. */ -"Delete pending connection" = "Smazat čekající připojení"; - /* No comment provided by engineer. */ "Delete pending connection?" = "Smazat čekající připojení?"; @@ -1102,7 +1077,7 @@ "Direct messages" = "Přímé zprávy"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "Přímé zprávy mezi členy jsou v této skupině zakázány."; +"Direct messages between members are prohibited." = "Přímé zprávy mezi členy jsou v této skupině zakázány."; /* No comment provided by engineer. */ "Disable (keep overrides)" = "Vypnout (zachovat přepsání)"; @@ -1126,7 +1101,7 @@ "Disappearing messages are prohibited in this chat." = "Mizící zprávy jsou v tomto chatu zakázány."; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "Mizící zprávy jsou v této skupině zakázány."; +"Disappearing messages are prohibited." = "Mizící zprávy jsou v této skupině zakázány."; /* No comment provided by engineer. */ "Disappears at" = "Zmizí v"; @@ -1185,7 +1160,7 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "Povolit (zachovat přepsání)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "Povolit automatické mazání zpráv?"; /* No comment provided by engineer. */ @@ -1320,9 +1295,6 @@ /* No comment provided by engineer. */ "Error accepting contact request" = "Chyba při přijímání žádosti o kontakt"; -/* No comment provided by engineer. */ -"Error accessing database file" = "Chyba přístupu k souboru databáze"; - /* No comment provided by engineer. */ "Error adding member(s)" = "Chyba přidávání člena(ů)"; @@ -1362,9 +1334,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "Chyba při mazání připojení"; -/* No comment provided by engineer. */ -"Error deleting contact" = "Chyba mazání kontaktu"; - /* No comment provided by engineer. */ "Error deleting database" = "Chyba při mazání databáze"; @@ -1395,18 +1364,12 @@ /* No comment provided by engineer. */ "Error joining group" = "Chyba při připojování ke skupině"; -/* No comment provided by engineer. */ -"Error loading %@ servers" = "Chyba načítání %@ serverů"; - -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Chyba při příjmu souboru"; /* No comment provided by engineer. */ "Error removing member" = "Chyba při odebrání člena"; -/* No comment provided by engineer. */ -"Error saving %@ servers" = "Chyba při ukládání serverů %@"; - /* No comment provided by engineer. */ "Error saving group profile" = "Chyba při ukládání profilu skupiny"; @@ -1440,7 +1403,7 @@ /* No comment provided by engineer. */ "Error stopping chat" = "Chyba při zastavení chatu"; -/* No comment provided by engineer. */ +/* alertTitle */ "Error switching profile!" = "Chyba při přepínání profilu!"; /* No comment provided by engineer. */ @@ -1461,7 +1424,9 @@ /* No comment provided by engineer. */ "Error: " = "Chyba: "; -/* No comment provided by engineer. */ +/* alert message +file error text +snd error text */ "Error: %@" = "Chyba: %@"; /* No comment provided by engineer. */ @@ -1494,7 +1459,7 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "Rychle a bez čekání, než bude odesílatel online!"; -/* No comment provided by engineer. */ +/* swipe action */ "Favorite" = "Oblíbené"; /* No comment provided by engineer. */ @@ -1516,7 +1481,7 @@ "Files and media" = "Soubory a média"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "Soubory a média jsou zakázány v této skupině."; +"Files and media are prohibited." = "Soubory a média jsou zakázány v této skupině."; /* No comment provided by engineer. */ "Files and media prohibited!" = "Soubory a média jsou zakázány!"; @@ -1560,9 +1525,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Celé jméno (volitelně)"; -/* No comment provided by engineer. */ -"Full name:" = "Celé jméno:"; - /* No comment provided by engineer. */ "Fully re-implemented - work in background!" = "Plně přepracováno, prácuje na pozadí!"; @@ -1602,24 +1564,6 @@ /* No comment provided by engineer. */ "Group links" = "Odkazy na skupiny"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "Členové skupin mohou přidávat reakce na zprávy."; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "Členové skupiny mohou nevratně mazat odeslané zprávy. (24 hodin)"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "Členové skupiny mohou posílat přímé zprávy."; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "Členové skupiny mohou posílat mizící zprávy."; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "Členové skupiny mohou posílat soubory a média."; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "Členové skupiny mohou posílat hlasové zprávy."; - /* notification */ "Group message:" = "Skupinová zpráva:"; @@ -1677,9 +1621,6 @@ /* time unit */ "hours" = "hodin"; -/* No comment provided by engineer. */ -"How it works" = "Jak to funguje"; - /* No comment provided by engineer. */ "How SimpleX works" = "Jak SimpleX funguje"; @@ -1720,7 +1661,7 @@ "Immediately" = "Ihned"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "Odolná vůči spamu a zneužití"; +"Immune to spam" = "Odolná vůči spamu a zneužití"; /* No comment provided by engineer. */ "Import" = "Import"; @@ -1789,10 +1730,10 @@ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Nainstalujte [SimpleX Chat pro terminál](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"Instant push notifications will be hidden!\n" = "Okamžitá oznámení budou skryta!\n"; +"Instant" = "Okamžitě"; /* No comment provided by engineer. */ -"Instantly" = "Okamžitě"; +"Instant push notifications will be hidden!\n" = "Okamžitá oznámení budou skryta!\n"; /* No comment provided by engineer. */ "Interface" = "Rozhranní"; @@ -1809,7 +1750,7 @@ /* invalid chat item */ "invalid data" = "neplatné údaje"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "Neplatná adresa serveru!"; /* item status text */ @@ -1855,7 +1796,7 @@ "Irreversible message deletion is prohibited in this chat." = "Nevratné mazání zpráv je v tomto chatu zakázáno."; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "Nevratné mazání zpráv je v této skupině zakázáno."; +"Irreversible message deletion is prohibited." = "Nevratné mazání zpráv je v této skupině zakázáno."; /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "Umožňuje mít v jednom profilu chatu mnoho anonymních spojení bez jakýchkoli sdílených údajů mezi nimi."; @@ -1878,7 +1819,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "Japonské rozhraní"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "Připojte se na"; /* No comment provided by engineer. */ @@ -1908,7 +1849,7 @@ /* No comment provided by engineer. */ "Learn more" = "Zjistit více"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "Opustit"; /* No comment provided by engineer. */ @@ -1938,9 +1879,6 @@ /* No comment provided by engineer. */ "Live messages" = "Živé zprávy"; -/* No comment provided by engineer. */ -"Local" = "Místní"; - /* No comment provided by engineer. */ "Local name" = "Místní název"; @@ -1953,24 +1891,15 @@ /* No comment provided by engineer. */ "Lock mode" = "Režim zámku"; -/* No comment provided by engineer. */ -"Make a private connection" = "Vytvořte si soukromé připojení"; - /* No comment provided by engineer. */ "Make one message disappear" = "Nechat jednu zprávu zmizet"; /* No comment provided by engineer. */ "Make profile private!" = "Změnit profil na soukromý!"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Ujistěte se, že adresy %@ serverů jsou ve správném formátu, oddělené řádky a nejsou duplicitní (%@)."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Ujistěte se, že adresy serverů WebRTC ICE jsou ve správném formátu, oddělené na řádcích a nejsou duplicitní."; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Mnoho lidí se ptalo: *Pokud SimpleX nemá žádné uživatelské identifikátory, jak může doručovat zprávy?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "Označit jako smazané pro všechny"; @@ -2007,6 +1936,24 @@ /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "Člen bude odstraněn ze skupiny - toto nelze vzít zpět!"; +/* No comment provided by engineer. */ +"Members can add message reactions." = "Členové skupin mohou přidávat reakce na zprávy."; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "Členové skupiny mohou nevratně mazat odeslané zprávy. (24 hodin)"; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "Členové skupiny mohou posílat přímé zprávy."; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "Členové skupiny mohou posílat mizící zprávy."; + +/* No comment provided by engineer. */ +"Members can send files and media." = "Členové skupiny mohou posílat soubory a média."; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "Členové skupiny mohou posílat hlasové zprávy."; + /* item status text */ "Message delivery error" = "Chyba doručení zprávy"; @@ -2023,7 +1970,7 @@ "Message reactions are prohibited in this chat." = "Reakce na zprávy jsou v tomto chatu zakázány."; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "Reakce na zprávy jsou v této skupině zakázány."; +"Message reactions are prohibited." = "Reakce na zprávy jsou v této skupině zakázány."; /* notification */ "message received" = "zpráva přijata"; @@ -2050,7 +1997,7 @@ "Migration is completed" = "Přenesení dokončeno"; /* No comment provided by engineer. */ -"Migrations: %@" = "Migrace: %@"; +"Migrations:" = "Migrace:"; /* time unit */ "minutes" = "minut"; @@ -2082,19 +2029,16 @@ /* item status description */ "Most likely this connection is deleted." = "Pravděpodobně je toto spojení smazáno."; -/* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "Tento kontakt s největší pravděpodobností smazal spojení s vámi."; - /* No comment provided by engineer. */ "Multiple chat profiles" = "Více chatovacích profilů"; -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "Ztlumit"; /* No comment provided by engineer. */ "Muted when inactive!" = "Ztlumit při neaktivitě!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "Jméno"; /* No comment provided by engineer. */ @@ -2106,7 +2050,7 @@ /* No comment provided by engineer. */ "Network status" = "Stav sítě"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "nikdy"; /* notification */ @@ -2115,9 +2059,6 @@ /* notification */ "New contact:" = "Nový kontakt:"; -/* No comment provided by engineer. */ -"New database archive" = "Archiv nové databáze"; - /* No comment provided by engineer. */ "New desktop app!" = "Nová desktopová aplikace!"; @@ -2178,12 +2119,18 @@ /* No comment provided by engineer. */ "No permission to record voice message" = "Nemáte oprávnění nahrávat hlasové zprávy"; +/* No comment provided by engineer. */ +"No push server" = "Místní"; + /* No comment provided by engineer. */ "No received or sent files" = "Žádné přijaté ani odeslané soubory"; /* copied message info in history */ "no text" = "žádný text"; +/* No comment provided by engineer. */ +"No user identifiers." = "Bez uživatelských identifikátorů"; + /* No comment provided by engineer. */ "Notifications" = "Oznámení"; @@ -2197,11 +2144,11 @@ "observer" = "pozorovatel"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "vypnuto"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Vypnout"; /* feature offered item */ @@ -2210,15 +2157,12 @@ /* feature offered item */ "offered %@: %@" = "nabídl %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ "Old database" = "Stará databáze"; -/* No comment provided by engineer. */ -"Old database archive" = "Archiv staré databáze"; - /* group pref value */ "on" = "zapnuto"; @@ -2226,16 +2170,16 @@ "One-time invitation link" = "Jednorázový zvací odkaz"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "Pro připojení budou vyžadováni Onion hostitelé. Vyžaduje povolení sítě VPN."; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "Pro připojení budou vyžadováni Onion hostitelé.\nVyžaduje povolení sítě VPN."; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "Onion hostitelé budou použiti, pokud jsou k dispozici. Vyžaduje povolení sítě VPN."; +"Onion hosts will be used when available.\nRequires compatible VPN." = "Onion hostitelé budou použiti, pokud jsou k dispozici.\nVyžaduje povolení sítě VPN."; /* No comment provided by engineer. */ "Onion hosts will not be used." = "Onion hostitelé nebudou použiti."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Pouze klientská zařízení ukládají uživatelské profily, kontakty, skupiny a zprávy odeslané s **2vrstvým šifrováním typu end-to-end**."; +"Only client devices store user profiles, contacts, groups, and messages." = "Pouze klientská zařízení ukládají uživatelské profily, kontakty, skupiny a zprávy odeslané s **2vrstvým šifrováním typu end-to-end**."; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Předvolby skupiny mohou měnit pouze vlastníci skupiny."; @@ -2276,7 +2220,7 @@ /* No comment provided by engineer. */ "Only your contact can send voice messages." = "Hlasové zprávy může odesílat pouze váš kontakt."; -/* No comment provided by engineer. */ +/* alert action */ "Open" = "Otevřít"; /* No comment provided by engineer. */ @@ -2288,12 +2232,6 @@ /* No comment provided by engineer. */ "Open Settings" = "Otevřít nastavení"; -/* authentication reason */ -"Open user profiles" = "Otevřít uživatelské profily"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "Protokol a kód s otevřeným zdrojovým kódem - servery může provozovat kdokoli."; - /* member role */ "owner" = "vlastník"; @@ -2322,10 +2260,7 @@ "peer-to-peer" = "peer-to-peer"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "Lidé se s vámi mohou spojit pouze prostřednictvím odkazů, které sdílíte."; - -/* No comment provided by engineer. */ -"Periodically" = "Pravidelně"; +"Periodic" = "Pravidelně"; /* message decrypt error item */ "Permanent decryption error" = "Chyba dešifrování"; @@ -2381,9 +2316,6 @@ /* No comment provided by engineer. */ "Preserve the last message draft, with attachments." = "Zachování posledního návrhu zprávy s přílohami."; -/* No comment provided by engineer. */ -"Preset server" = "Přednastavený server"; - /* No comment provided by engineer. */ "Preset server address" = "Přednastavená adresa serveru"; @@ -2408,7 +2340,7 @@ /* No comment provided by engineer. */ "Profile password" = "Heslo profilu"; -/* No comment provided by engineer. */ +/* alert message */ "Profile update will be sent to your contacts." = "Aktualizace profilu bude zaslána vašim kontaktům."; /* No comment provided by engineer. */ @@ -2456,14 +2388,14 @@ /* chat item menu */ "React…" = "Reagovat…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "Číst"; /* No comment provided by engineer. */ "Read more" = "Přečíst více"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Další informace naleznete v [Uživatelské příručce](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "Další informace naleznete v [Uživatelské příručce](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)."; /* No comment provided by engineer. */ "Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Přečtěte si více v [Uživatelské příručce](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; @@ -2471,9 +2403,6 @@ /* No comment provided by engineer. */ "Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Přečtěte si více v našem [GitHub repozitáři](https://github.com/simplex-chat/simplex-chat#readme)."; -/* No comment provided by engineer. */ -"Read more in our GitHub repository." = "Další informace najdete v našem repozitáři GitHub."; - /* No comment provided by engineer. */ "Receipts are disabled" = "Informace o dodání jsou zakázány"; @@ -2522,7 +2451,8 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "Snížení spotřeby baterie"; -/* reject incoming call via notification */ +/* reject incoming call via notification +swipe action */ "Reject" = "Odmítnout"; /* No comment provided by engineer. */ @@ -2606,9 +2536,6 @@ /* chat item action */ "Reveal" = "Odhalit"; -/* No comment provided by engineer. */ -"Revert" = "Vrátit"; - /* No comment provided by engineer. */ "Revoke" = "Odvolat"; @@ -2624,13 +2551,14 @@ /* No comment provided by engineer. */ "Run chat" = "Spustit chat"; -/* chat item action */ +/* alert button +chat item action */ "Save" = "Uložit"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Uložit (a informovat kontakty)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "Uložit a upozornit kontakt"; /* No comment provided by engineer. */ @@ -2639,12 +2567,6 @@ /* No comment provided by engineer. */ "Save and update group profile" = "Uložit a aktualizovat profil skupiny"; -/* No comment provided by engineer. */ -"Save archive" = "Uložit archiv"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Uložit nastavení automatického přijímání"; - /* No comment provided by engineer. */ "Save group profile" = "Uložení profilu skupiny"; @@ -2654,7 +2576,7 @@ /* No comment provided by engineer. */ "Save passphrase in Keychain" = "Uložit přístupovou frázi do Klíčenky"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "Uložit předvolby?"; /* No comment provided by engineer. */ @@ -2663,12 +2585,9 @@ /* No comment provided by engineer. */ "Save servers" = "Uložit servery"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "Uložit servery?"; -/* No comment provided by engineer. */ -"Save settings?" = "Uložit nastavení?"; - /* No comment provided by engineer. */ "Save welcome message?" = "Uložit uvítací zprávu?"; @@ -2711,7 +2630,7 @@ /* chat item text */ "security code changed" = "bezpečnostní kód změněn"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "Vybrat"; /* No comment provided by engineer. */ @@ -2738,9 +2657,6 @@ /* No comment provided by engineer. */ "send direct message" = "odeslat přímou zprávu"; -/* No comment provided by engineer. */ -"Send direct message" = "Odeslat přímou zprávu"; - /* No comment provided by engineer. */ "Send direct message to connect" = "Odeslat přímou zprávu pro připojení"; @@ -2756,9 +2672,6 @@ /* No comment provided by engineer. */ "Send notifications" = "Odeslat oznámení"; -/* No comment provided by engineer. */ -"Send notifications:" = "Odeslat oznámení:"; - /* No comment provided by engineer. */ "Send questions and ideas" = "Zasílání otázek a nápadů"; @@ -2768,7 +2681,7 @@ /* No comment provided by engineer. */ "Send them from gallery or custom keyboards." = "Odeslat je z galerie nebo vlastní klávesnice."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Odesílatel zrušil přenos souboru."; /* No comment provided by engineer. */ @@ -2852,7 +2765,8 @@ /* No comment provided by engineer. */ "Settings" = "Nastavení"; -/* chat item action */ +/* alert action +chat item action */ "Share" = "Sdílet"; /* No comment provided by engineer. */ @@ -2861,7 +2775,7 @@ /* No comment provided by engineer. */ "Share address" = "Sdílet adresu"; -/* No comment provided by engineer. */ +/* alert title */ "Share address with contacts?" = "Sdílet adresu s kontakty?"; /* No comment provided by engineer. */ @@ -2903,7 +2817,7 @@ /* simplex link type */ "SimpleX group link" = "Skupinový odkaz SimpleX"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "Odkazy na SimpleX"; /* No comment provided by engineer. */ @@ -2933,9 +2847,6 @@ /* No comment provided by engineer. */ "Small groups (max 20)" = "Malé skupiny (max. 20)"; -/* No comment provided by engineer. */ -"SMP servers" = "SMP servery"; - /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "Během importu došlo k nezávažným chybám - podrobnosti naleznete v chat konzoli."; @@ -2954,9 +2865,6 @@ /* No comment provided by engineer. */ "Stop" = "Zastavit"; -/* No comment provided by engineer. */ -"Stop chat to enable database actions" = "Zastavte chat pro povolení akcí databáze"; - /* No comment provided by engineer. */ "Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Zastavení chatu pro export, import nebo smazání databáze chatu. Během zastavení chatu nebudete moci přijímat a odesílat zprávy."; @@ -2972,10 +2880,10 @@ /* No comment provided by engineer. */ "Stop sending file?" = "Zastavit odesílání souboru?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "Přestat sdílet"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "Přestat sdílet adresu?"; /* authentication reason */ @@ -3011,9 +2919,6 @@ /* No comment provided by engineer. */ "Tap to join incognito" = "Klepnutím se připojíte inkognito"; -/* No comment provided by engineer. */ -"Tap to start a new chat" = "Klepnutím na zahájíte nový chat"; - /* No comment provided by engineer. */ "TCP connection timeout" = "Časový limit připojení TCP"; @@ -3035,7 +2940,7 @@ /* No comment provided by engineer. */ "Test servers" = "Testovací servery"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "Testy selhaly!"; /* No comment provided by engineer. */ @@ -3047,9 +2952,6 @@ /* No comment provided by engineer. */ "Thanks to the users – contribute via Weblate!" = "Díky uživatelům - přispívejte prostřednictvím Weblate!"; -/* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "1. Platforma bez identifikátorů uživatelů - soukromá už od záměru."; - /* No comment provided by engineer. */ "The app can notify you when you receive messages or contact requests - please open settings to enable." = "Aplikace vás může upozornit na přijaté zprávy nebo žádosti o kontakt - povolte to v nastavení."; @@ -3068,6 +2970,9 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "Šifrování funguje a nové povolení šifrování není vyžadováno. To může vyvolat chybu v připojení!"; +/* No comment provided by engineer. */ +"The future of messaging" = "Nová generace soukromých zpráv"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "Hash předchozí zprávy se liší."; @@ -3080,14 +2985,11 @@ /* No comment provided by engineer. */ "The message will be marked as moderated for all members." = "Zpráva bude pro všechny členy označena jako moderovaná."; -/* No comment provided by engineer. */ -"The next generation of private messaging" = "Nová generace soukromých zpráv"; - /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "Stará databáze nebyla během přenášení odstraněna, lze ji smazat."; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "Profil je sdílen pouze s vašimi kontakty."; +"Your profile is stored on your device and only shared with your contacts." = "Profil je sdílen pouze s vašimi kontakty."; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "Druhé zaškrtnutí jsme přehlédli! ✅"; @@ -3098,9 +3000,6 @@ /* No comment provided by engineer. */ "The servers for new connections of your current chat profile **%@**." = "Servery pro nová připojení vašeho aktuálního chat profilu **%@**."; -/* No comment provided by engineer. */ -"Theme" = "Téma"; - /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Toto nastavení je pro váš aktuální profil **%@**."; @@ -3137,15 +3036,15 @@ /* No comment provided by engineer. */ "To make a new connection" = "Vytvoření nového připojení"; -/* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Pro ochranu soukromí namísto ID uživatelů používaných všemi ostatními platformami má SimpleX identifikátory pro fronty zpráv, oddělené pro každý z vašich kontaktů."; - /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "K ochraně časového pásma používají obrazové/hlasové soubory UTC."; /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Chcete-li chránit své informace, zapněte zámek SimpleX Lock.\nPřed zapnutím této funkce budete vyzváni k dokončení ověření."; +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "Pro ochranu soukromí namísto ID uživatelů používaných všemi ostatními platformami má SimpleX identifikátory pro fronty zpráv, oddělené pro každý z vašich kontaktů."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Chcete-li nahrávat hlasové zprávy, udělte povolení k použití mikrofonu."; @@ -3179,13 +3078,10 @@ /* No comment provided by engineer. */ "Unable to record voice message" = "Nelze nahrát hlasovou zprávu"; -/* item status description */ -"Unexpected error: %@" = "Neočekávaná chyba: %@"; - /* No comment provided by engineer. */ "Unexpected migration state" = "Neočekávaný stav přenášení"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "Odobl."; /* No comment provided by engineer. */ @@ -3224,36 +3120,27 @@ /* authentication reason */ "Unlock app" = "Odemknout aplikaci"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "Zrušit ztlumení"; -/* No comment provided by engineer. */ +/* swipe action */ "Unread" = "Nepřečtený"; /* No comment provided by engineer. */ "Update" = "Aktualizovat"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = "Aktualizovat nastavení hostitelů .onion?"; - /* No comment provided by engineer. */ "Update database passphrase" = "Aktualizovat přístupovou frázi databáze"; /* No comment provided by engineer. */ "Update network settings?" = "Aktualizovat nastavení sítě?"; -/* No comment provided by engineer. */ -"Update transport isolation mode?" = "Aktualizovat režim dopravní izolace?"; - /* rcv group event chat item */ "updated group profile" = "aktualizoval profil skupiny"; /* No comment provided by engineer. */ "Updating settings will re-connect the client to all servers." = "Aktualizací nastavení se klient znovu připojí ke všem serverům."; -/* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "Aktualizace tohoto nastavení znovu připojí klienta ke všem serverům."; - /* No comment provided by engineer. */ "Upgrade and open chat" = "Zvýšit a otevřít chat"; @@ -3284,12 +3171,6 @@ /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Používat servery SimpleX Chat?"; -/* No comment provided by engineer. */ -"User profile" = "Profil uživatele"; - -/* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = "Použití hostitelů .onion vyžaduje kompatibilního poskytovatele VPN."; - /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Používat servery SimpleX Chat."; @@ -3345,7 +3226,7 @@ "Voice messages are prohibited in this chat." = "Hlasové zprávy jsou v tomto chatu zakázány."; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "Hlasové zprávy jsou v této skupině zakázány."; +"Voice messages are prohibited." = "Hlasové zprávy jsou v této skupině zakázány."; /* No comment provided by engineer. */ "Voice messages prohibited!" = "Hlasové zprávy jsou zakázány!"; @@ -3389,9 +3270,6 @@ /* No comment provided by engineer. */ "When available" = "Když je k dispozici"; -/* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "Když někdo požádá o připojení, můžete žádost přijmout nebo odmítnout."; - /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Pokud s někým sdílíte inkognito profil, bude tento profil použit pro skupiny, do kterých vás pozve."; @@ -3404,15 +3282,9 @@ /* No comment provided by engineer. */ "Wrong passphrase!" = "Špatná přístupová fráze!"; -/* No comment provided by engineer. */ -"XFTP servers" = "XFTP servery"; - /* pref value */ "yes" = "ano"; -/* No comment provided by engineer. */ -"You" = "Vy"; - /* No comment provided by engineer. */ "You accepted connection" = "Přijali jste spojení"; @@ -3453,7 +3325,7 @@ "You can hide or mute a user profile - swipe it to the right." = "Profil uživatele můžete skrýt nebo ztlumit - přejeďte prstem doprava."; /* notification body */ -"You can now send messages to %@" = "Nyní můžete posílat zprávy %@"; +"You can now chat with %@" = "Nyní můžete posílat zprávy %@"; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "Náhled oznámení na zamykací obrazovce můžete změnit v nastavení."; @@ -3464,9 +3336,6 @@ /* No comment provided by engineer. */ "You can share this address with your contacts to let them connect with **%@**." = "Tuto adresu můžete sdílet s vašimi kontakty, abyse se mohli spojit s **%@**."; -/* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "Můžete sdílet svou adresu jako odkaz nebo jako QR kód - kdokoli se k vám bude moci připojit."; - /* No comment provided by engineer. */ "You can start chat via app Settings / Database or by restarting the app" = "Chat můžete zahájit prostřednictvím aplikace Nastavení / Databáze nebo restartováním aplikace"; @@ -3491,14 +3360,11 @@ /* snd group event chat item */ "you changed role of %@ to %@" = "změnili jste roli z %1$@ na %2$@"; -/* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "Sami řídíte, přes který server(y) **přijímat** zprávy, své kontakty – servery, které používáte k odesílání zpráv."; - /* No comment provided by engineer. */ "You could not be verified; please try again." = "Nemohli jste být ověřeni; Zkuste to prosím znovu."; /* No comment provided by engineer. */ -"You have no chats" = "Nemáte žádné konverzace"; +"You decide who can connect." = "Lidé se s vámi mohou spojit pouze prostřednictvím odkazu, který sdílíte."; /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "Musíte zadat přístupovou frázi při každém spuštění aplikace - není uložena v zařízení."; @@ -3566,9 +3432,6 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Pro tuto skupinu používáte inkognito profil - abyste zabránili sdílení svého hlavního profilu, není pozvání kontaktů povoleno"; -/* No comment provided by engineer. */ -"Your %@ servers" = "Vaše servery %@"; - /* No comment provided by engineer. */ "Your calls" = "Vaše hovory"; @@ -3581,9 +3444,6 @@ /* No comment provided by engineer. */ "Your chat profiles" = "Vaše chat profily"; -/* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "K dokončení připojení, musí být váš kontakt online.\nToto připojení můžete zrušit a kontakt odebrat (a zkusit to později s novým odkazem)."; - /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "Kontakt odeslal soubor, který je větší než aktuálně podporovaná maximální velikost (%@)."; @@ -3612,7 +3472,7 @@ "Your profile **%@** will be shared." = "Váš profil **%@** bude sdílen."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Váš profil je uložen ve vašem zařízení a sdílen pouze s vašimi kontakty.\nServery SimpleX nevidí váš profil."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Váš profil je uložen ve vašem zařízení a sdílen pouze s vašimi kontakty. Servery SimpleX nevidí váš profil."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Váš profil, kontakty a doručené zprávy jsou uloženy ve vašem zařízení."; @@ -3620,9 +3480,6 @@ /* No comment provided by engineer. */ "Your random profile" = "Váš náhodný profil"; -/* No comment provided by engineer. */ -"Your server" = "Váš server"; - /* No comment provided by engineer. */ "Your server address" = "Adresa vašeho serveru"; @@ -3632,9 +3489,3 @@ /* No comment provided by engineer. */ "Your SimpleX address" = "Vaše SimpleX adresa"; -/* No comment provided by engineer. */ -"Your SMP servers" = "Vaše servery SMP"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "Vaše XFTP servery"; - diff --git a/apps/ios/de.lproj/Localizable.strings b/apps/ios/de.lproj/Localizable.strings index 6eb9067684..8da7835c43 100644 --- a/apps/ios/de.lproj/Localizable.strings +++ b/apps/ios/de.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (kann kopiert werden)"; @@ -31,30 +16,15 @@ /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- Bis zu 5 Minuten lange Sprachnachrichten\n- Zeitdauer für verschwindende Nachrichten anpassen\n- Nachrichtenverlauf bearbeiten"; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 farbig!"; -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - /* No comment provided by engineer. */ "(new)" = "(Neu)"; /* No comment provided by engineer. */ "(this device v%@)" = "(Dieses Gerät hat v%@)"; -/* No comment provided by engineer. */ -")" = ")"; - /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Unterstützen Sie uns](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -65,10 +35,7 @@ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Stern auf GitHub vergeben](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add contact**: to create a new invitation link, or connect via a link you received." = "**Kontakt hinzufügen**: Um einen neuen Einladungslink zu erstellen oder eine Verbindung über einen Link herzustellen, den Sie erhalten haben."; - -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Neuen Kontakt hinzufügen**: Um einen Einmal-QR-Code oder -Link für Ihren Kontakt zu erzeugen."; +"**Create 1-time link**: to create and share a new invitation link." = "**Kontakt hinzufügen**: Um einen neuen Einladungslink zu erstellen."; /* No comment provided by engineer. */ "**Create group**: to create a new group." = "**Gruppe erstellen**: Um eine neue Gruppe zu erstellen."; @@ -80,20 +47,29 @@ "**e2e encrypted** video call" = "**E2E-verschlüsselter** Videoanruf"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Mehr Privatsphäre**: Es wird alle 20 Minuten auf neue Nachrichten geprüft. Nur Ihr Geräte-Token wird dem SimpleX-Chat-Server mitgeteilt, aber nicht wie viele Kontakte Sie haben oder welche Nachrichten Sie empfangen."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Mehr Privatsphäre**: Es wird alle 20 Minuten auf neue Nachrichten geprüft. Nur Ihr Geräte-Token wird dem SimpleX-Chat-Server mitgeteilt, aber nicht wie viele Kontakte Sie haben oder welche Nachrichten Sie empfangen."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Beste Privatsphäre**: Es wird kein SimpleX-Chat-Benachrichtigungs-Server genutzt, Nachrichten werden in periodischen Abständen im Hintergrund geprüft (dies hängt davon ab, wie häufig Sie die App nutzen)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**Beste Privatsphäre**: Es wird kein SimpleX-Chat-Benachrichtigungs-Server genutzt, Nachrichten werden in periodischen Abständen im Hintergrund geprüft (dies hängt davon ab, wie häufig Sie die App nutzen)."; + +/* No comment provided by engineer. */ +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Bitte beachten Sie**: Aus Sicherheitsgründen wird die Nachrichtenentschlüsselung Ihrer Verbindungen abgebrochen, wenn Sie die gleiche Datenbank auf zwei Geräten nutzen."; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Bitte beachten Sie**: Das Passwort kann NICHT wiederhergestellt oder geändert werden, wenn Sie es vergessen haben oder verlieren."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Empfohlen**: Nur Ihr Geräte-Token und ihre Benachrichtigungen werden an den SimpleX-Chat-Benachrichtigungs-Server gesendet, aber weder der Nachrichteninhalt noch deren Größe oder von wem sie gesendet wurde."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Empfohlen**: Nur Ihr Geräte-Token und ihre Benachrichtigungen werden an den SimpleX-Chat-Benachrichtigungs-Server gesendet, aber weder der Nachrichteninhalt noch deren Größe oder von wem sie gesendet wurde."; + +/* No comment provided by engineer. */ +"**Scan / Paste link**: to connect via a link you received." = "**Link scannen / einfügen**: Um eine Verbindung über den Link herzustellen, den Sie erhalten haben."; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Warnung**: Sofortige Push-Benachrichtigungen erfordern die Eingabe eines Passworts, welches in Ihrem Schlüsselbund gespeichert ist."; +/* No comment provided by engineer. */ +"**Warning**: the archive will be removed." = "**Warnung**: Das Archiv wird gelöscht."; + /* No comment provided by engineer. */ "*bold*" = "\\*fett*"; @@ -136,6 +112,9 @@ /* No comment provided by engineer. */ "%@ connected" = "%@ wurde mit Ihnen verbunden"; +/* No comment provided by engineer. */ +"%@ downloaded" = "%@ heruntergeladen"; + /* notification title */ "%@ is connected!" = "%@ ist mit Ihnen verbunden!"; @@ -146,11 +125,20 @@ "%@ is verified" = "%@ wurde erfolgreich überprüft"; /* No comment provided by engineer. */ -"%@ servers" = "%@-Server"; +"%@ server" = "%@ Server"; + +/* No comment provided by engineer. */ +"%@ servers" = "%@ Server"; + +/* No comment provided by engineer. */ +"%@ uploaded" = "%@ hochgeladen"; /* notification title */ "%@ wants to connect!" = "%@ will sich mit Ihnen verbinden!"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + /* No comment provided by engineer. */ "%@, %@ and %lld members" = "%@, %@ und %lld Mitglieder"; @@ -163,9 +151,24 @@ /* time interval */ "%d days" = "%d Tage"; +/* forward confirmation reason */ +"%d file(s) are still being downloaded." = "%d Datei(en) wird/werden immer noch heruntergeladen."; + +/* forward confirmation reason */ +"%d file(s) failed to download." = "Bei %d Datei(en) ist das Herunterladen fehlgeschlagen."; + +/* forward confirmation reason */ +"%d file(s) were deleted." = "%d Datei(en) wurde(n) gelöscht."; + +/* forward confirmation reason */ +"%d file(s) were not downloaded." = "%d Datei(en) wurde(n) nicht heruntergeladen."; + /* time interval */ "%d hours" = "%d Stunden"; +/* alert title */ +"%d messages not forwarded" = "%d Nachrichten wurden nicht weitergeleitet"; + /* time interval */ "%d min" = "%d min"; @@ -175,6 +178,9 @@ /* time interval */ "%d sec" = "%d s"; +/* delete after time */ +"%d seconds(s)" = "%d Sekunde(n)"; + /* integrity error chat item */ "%d skipped message(s)" = "%d übersprungene Nachricht(en)"; @@ -217,9 +223,6 @@ /* No comment provided by engineer. */ "%lld new interface languages" = "%lld neue Sprachen für die Bedienoberfläche"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld Sekunde(n)"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld Sekunden"; @@ -265,8 +268,9 @@ /* No comment provided by engineer. */ "0s" = "0s"; -/* time interval */ -"1 day" = "täglich"; +/* delete after time +time interval */ +"1 day" = "Älter als ein Tag"; /* time interval */ "1 hour" = "1 Stunde"; @@ -274,11 +278,22 @@ /* No comment provided by engineer. */ "1 minute" = "1 Minute"; -/* time interval */ -"1 month" = "monatlich"; +/* delete after time +time interval */ +"1 month" = "Älter als ein Monat"; -/* time interval */ -"1 week" = "wöchentlich"; +/* delete after time +time interval */ +"1 week" = "Älter als eine Woche"; + +/* delete after time */ +"1 year" = "Älter als ein Jahr"; + +/* No comment provided by engineer. */ +"1-time link" = "Einmal-Link"; + +/* No comment provided by engineer. */ +"1-time link can be used *with one contact only* - share in person or via any messenger." = "Ein Einmal-Link kann *nur mit einem Kontakt* genutzt werden - teilen Sie in nur persönlich oder über einen beliebigen Messenger."; /* No comment provided by engineer. */ "5 minutes" = "5 Minuten"; @@ -305,19 +320,16 @@ "A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "**Für jeden Kontakt und jedes Gruppenmitglied** wird eine separate TCP-Verbindung genutzt.\n**Bitte beachten Sie**: Wenn Sie viele Verbindungen haben, kann der Batterieverbrauch und die Datennutzung wesentlich höher sein und einige Verbindungen können scheitern."; /* No comment provided by engineer. */ -"Abort" = "Abbrechen"; +"Abort" = "Beenden"; /* No comment provided by engineer. */ -"Abort changing address" = "Wechsel der Empfängeradresse abbrechen"; +"Abort changing address" = "Wechsel der Empfängeradresse beenden"; /* No comment provided by engineer. */ -"Abort changing address?" = "Wechsel der Empfängeradresse abbrechen?"; +"Abort changing address?" = "Wechsel der Empfängeradresse beenden?"; /* No comment provided by engineer. */ -"About SimpleX" = "Über SimpleX"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "Über die SimpleX-Adresse"; +"About operators" = "Über die Betreiber"; /* No comment provided by engineer. */ "About SimpleX Chat" = "Über SimpleX Chat"; @@ -326,105 +338,200 @@ "above, then choose:" = "Danach die gewünschte Aktion auswählen:"; /* No comment provided by engineer. */ -"Accent color" = "Akzentfarbe"; +"Accent" = "Akzent"; /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "Annehmen"; +/* No comment provided by engineer. */ +"Accept conditions" = "Nutzungsbedingungen akzeptieren"; + /* No comment provided by engineer. */ "Accept connection request?" = "Kontaktanfrage annehmen?"; /* notification body */ "Accept contact request from %@?" = "Die Kontaktanfrage von %@ annehmen?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "Inkognito akzeptieren"; /* call status */ "accepted call" = "Anruf angenommen"; +/* No comment provided by engineer. */ +"Accepted conditions" = "Akzeptierte Nutzungsbedingungen"; + +/* chat list item title */ +"accepted invitation" = "Einladung angenommen"; + +/* No comment provided by engineer. */ +"Acknowledged" = "Bestätigt"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Fehler bei der Bestätigung"; + +/* token status text */ +"Active" = "Aktiv"; + +/* No comment provided by engineer. */ +"Active connections" = "Aktive Verbindungen"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Fügen Sie die Adresse Ihrem Profil hinzu, damit Ihre Kontakte sie mit anderen Personen teilen können. Es wird eine Profilaktualisierung an Ihre Kontakte gesendet."; /* No comment provided by engineer. */ -"Add contact" = "Kontakt hinzufügen"; +"Add friends" = "Freunde aufnehmen"; /* No comment provided by engineer. */ -"Add preset servers" = "Füge voreingestellte Server hinzu"; +"Add list" = "Liste hinzufügen"; /* No comment provided by engineer. */ "Add profile" = "Profil hinzufügen"; /* No comment provided by engineer. */ -"Add server…" = "Füge Server hinzu…"; +"Add server" = "Server hinzufügen"; /* No comment provided by engineer. */ -"Add servers by scanning QR codes." = "Fügen Sie Server durch Scannen der QR Codes hinzu."; +"Add servers by scanning QR codes." = "Server durch Scannen von QR Codes hinzufügen."; + +/* No comment provided by engineer. */ +"Add team members" = "Team-Mitglieder aufnehmen"; /* No comment provided by engineer. */ "Add to another device" = "Einem anderen Gerät hinzufügen"; +/* No comment provided by engineer. */ +"Add to list" = "Zur Liste hinzufügen"; + /* No comment provided by engineer. */ "Add welcome message" = "Begrüßungsmeldung hinzufügen"; +/* No comment provided by engineer. */ +"Add your team members to the conversations." = "Nehmen Sie Team-Mitglieder in Ihre Unterhaltungen auf."; + +/* No comment provided by engineer. */ +"Added media & file servers" = "Medien- und Dateiserver hinzugefügt"; + +/* No comment provided by engineer. */ +"Added message servers" = "Nachrichtenserver hinzugefügt"; + +/* No comment provided by engineer. */ +"Additional accent" = "Erste Akzentfarbe"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Zusätzlicher Akzent 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Zweite Akzentfarbe"; + /* No comment provided by engineer. */ "Address" = "Adresse"; /* No comment provided by engineer. */ -"Address change will be aborted. Old receiving address will be used." = "Der Wechsel der Empfängeradresse wird abgebrochen. Die bisherige Adresse wird weiter verwendet."; +"Address change will be aborted. Old receiving address will be used." = "Der Wechsel der Empfängeradresse wird beendet. Die bisherige Adresse wird weiter verwendet."; + +/* No comment provided by engineer. */ +"Address or 1-time link?" = "Adress- oder Einmal-Link?"; + +/* No comment provided by engineer. */ +"Address settings" = "Adress-Einstellungen"; /* member role */ "admin" = "Admin"; +/* feature role */ +"admins" = "Administratoren"; + +/* No comment provided by engineer. */ +"Admins can block a member for all." = "Administratoren können ein Gruppenmitglied für Alle blockieren."; + /* No comment provided by engineer. */ "Admins can create the links to join groups." = "Administratoren können Links für den Beitritt zu Gruppen erzeugen."; /* No comment provided by engineer. */ "Advanced network settings" = "Erweiterte Netzwerkeinstellungen"; +/* No comment provided by engineer. */ +"Advanced settings" = "Erweiterte Einstellungen"; + /* chat item text */ "agreeing encryption for %@…" = "Verschlüsselung von %@ zustimmen…"; /* chat item text */ "agreeing encryption…" = "Verschlüsselung zustimmen…"; +/* No comment provided by engineer. */ +"All" = "Alle"; + /* No comment provided by engineer. */ "All app data is deleted." = "Werden die App-Daten komplett gelöscht."; /* No comment provided by engineer. */ -"All chats and messages will be deleted - this cannot be undone!" = "Alle Chats und Nachrichten werden gelöscht! Dies kann nicht rückgängig gemacht werden!"; +"All chats and messages will be deleted - this cannot be undone!" = "Es werden alle Chats und Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden!"; + +/* alert message */ +"All chats will be removed from the list %@, and the list deleted." = "Alle Chats werden von der Liste %@ entfernt und danach wird die Liste gelöscht."; /* No comment provided by engineer. */ "All data is erased when it is entered." = "Alle Daten werden gelöscht, sobald dieser eingegeben wird."; +/* No comment provided by engineer. */ +"All data is kept private on your device." = "Alle Daten werden nur auf Ihrem Gerät gespeichert."; + /* No comment provided by engineer. */ "All group members will remain connected." = "Alle Gruppenmitglieder bleiben verbunden."; -/* No comment provided by engineer. */ -"All messages will be deleted - this cannot be undone!" = "Es werden alle Nachrichten gelöscht. Dieser Vorgang kann nicht rückgängig gemacht werden!"; +/* feature role */ +"all members" = "Alle Mitglieder"; /* No comment provided by engineer. */ -"All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Alle Nachrichten werden gelöscht - dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht."; +"All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages." = "Alle Nachrichten und Dateien werden **Ende-zu-Ende verschlüsselt** versendet - in Direkt-Nachrichten mit Post-Quantum-Security."; + +/* No comment provided by engineer. */ +"All messages will be deleted - this cannot be undone!" = "Es werden alle Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden!"; + +/* No comment provided by engineer. */ +"All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Es werden alle Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht."; /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Von %@ werden alle neuen Nachrichten ausgeblendet!"; +/* profile dropdown */ +"All profiles" = "Alle Profile"; + +/* No comment provided by engineer. */ +"All reports will be archived for you." = "Alle Meldungen werden für Sie archiviert."; + +/* No comment provided by engineer. */ +"All servers" = "Alle Server"; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Alle Ihre Kontakte bleiben verbunden."; /* No comment provided by engineer. */ "All your contacts will remain connected. Profile update will be sent to your contacts." = "Alle Ihre Kontakte bleiben verbunden. Es wird eine Profilaktualisierung an Ihre Kontakte gesendet."; +/* No comment provided by engineer. */ +"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Alle Ihre Kontakte, Unterhaltungen und Dateien werden sicher verschlüsselt und in Daten-Paketen auf die konfigurierten XTFP-Relais hochgeladen."; + /* No comment provided by engineer. */ "Allow" = "Erlauben"; /* No comment provided by engineer. */ "Allow calls only if your contact allows them." = "Erlauben Sie Anrufe nur dann, wenn es Ihr Kontakt ebenfalls erlaubt."; +/* No comment provided by engineer. */ +"Allow calls?" = "Anrufe erlauben?"; + /* No comment provided by engineer. */ "Allow disappearing messages only if your contact allows it to you." = "Erlauben Sie verschwindende Nachrichten nur dann, wenn es Ihr Kontakt ebenfalls erlaubt."; +/* No comment provided by engineer. */ +"Allow downgrade" = "Herabstufung erlauben"; + /* No comment provided by engineer. */ "Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Erlauben Sie das unwiederbringliche Löschen von Nachrichten nur dann, wenn es Ihnen Ihr Kontakt ebenfalls erlaubt. (24 Stunden)"; @@ -440,12 +547,21 @@ /* No comment provided by engineer. */ "Allow sending disappearing messages." = "Das Senden von verschwindenden Nachrichten erlauben."; +/* No comment provided by engineer. */ +"Allow sharing" = "Teilen erlauben"; + /* No comment provided by engineer. */ "Allow to irreversibly delete sent messages. (24 hours)" = "Unwiederbringliches löschen von gesendeten Nachrichten erlauben. (24 Stunden)"; +/* No comment provided by engineer. */ +"Allow to report messsages to moderators." = "Melden von Nachrichten an Moderatoren erlauben."; + /* No comment provided by engineer. */ "Allow to send files and media." = "Das Senden von Dateien und Medien erlauben."; +/* No comment provided by engineer. */ +"Allow to send SimpleX links." = "Das Senden von SimpleX-Links erlauben."; + /* No comment provided by engineer. */ "Allow to send voice messages." = "Das Senden von Sprachnachrichten erlauben."; @@ -482,6 +598,9 @@ /* pref value */ "always" = "Immer"; +/* No comment provided by engineer. */ +"Always use private routing." = "Sie nutzen immer privates Routing."; + /* No comment provided by engineer. */ "Always use relay" = "Über ein Relais verbinden"; @@ -491,15 +610,27 @@ /* No comment provided by engineer. */ "and %lld other events" = "und %lld weitere Ereignisse"; +/* report reason */ +"Another reason" = "Anderer Grund"; + /* No comment provided by engineer. */ "Answer call" = "Anruf annehmen"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "Jeder kann seine eigenen Server aufsetzen."; + /* No comment provided by engineer. */ "App build: %@" = "App Build: %@"; +/* No comment provided by engineer. */ +"App data migration" = "App-Daten-Migration"; + /* No comment provided by engineer. */ "App encrypts new local files (except videos)." = "Neue lokale Dateien (außer Video-Dateien) werden von der App verschlüsselt."; +/* No comment provided by engineer. */ +"App group:" = "App-Gruppe:"; + /* No comment provided by engineer. */ "App icon" = "App-Icon"; @@ -509,6 +640,9 @@ /* No comment provided by engineer. */ "App passcode is replaced with self-destruct passcode." = "App-Zugangscode wurde durch den Selbstzerstörungs-Zugangscode ersetzt."; +/* No comment provided by engineer. */ +"App session" = "App-Sitzung"; + /* No comment provided by engineer. */ "App version" = "App Version"; @@ -518,9 +652,51 @@ /* No comment provided by engineer. */ "Appearance" = "Erscheinungsbild"; +/* No comment provided by engineer. */ +"Apply" = "Anwenden"; + +/* No comment provided by engineer. */ +"Apply to" = "Anwenden auf"; + +/* No comment provided by engineer. */ +"Archive" = "Archiv"; + +/* No comment provided by engineer. */ +"Archive %lld reports?" = "Archiviere %lld Meldungen?"; + +/* No comment provided by engineer. */ +"Archive all reports?" = "Alle Meldungen archivieren?"; + +/* No comment provided by engineer. */ +"Archive and upload" = "Archivieren und Hochladen"; + +/* No comment provided by engineer. */ +"Archive contacts to chat later." = "Kontakte für spätere Chats archivieren."; + +/* No comment provided by engineer. */ +"Archive report" = "Meldung archivieren"; + +/* No comment provided by engineer. */ +"Archive report?" = "Meldung archivieren?"; + +/* swipe action */ +"Archive reports" = "Meldungen archivieren"; + +/* No comment provided by engineer. */ +"Archived contacts" = "Archivierte Kontakte"; + +/* No comment provided by engineer. */ +"archived report" = "Archivierte Meldung"; + +/* No comment provided by engineer. */ +"Archiving database" = "Datenbank wird archiviert"; + /* No comment provided by engineer. */ "Attach" = "Anhängen"; +/* No comment provided by engineer. */ +"attempts" = "Versuche"; + /* No comment provided by engineer. */ "Audio & video calls" = "Audio- & Videoanrufe"; @@ -560,9 +736,15 @@ /* No comment provided by engineer. */ "Auto-accept images" = "Bilder automatisch akzeptieren"; +/* alert title */ +"Auto-accept settings" = "Einstellungen automatisch akzeptieren"; + /* No comment provided by engineer. */ "Back" = "Zurück"; +/* No comment provided by engineer. */ +"Background" = "Hintergrund-Farbe"; + /* No comment provided by engineer. */ "Bad desktop address" = "Falsche Desktop-Adresse"; @@ -578,12 +760,39 @@ /* No comment provided by engineer. */ "Bad message ID" = "Falsche Nachrichten-ID"; +/* No comment provided by engineer. */ +"Better calls" = "Verbesserte Anrufe"; + /* No comment provided by engineer. */ "Better groups" = "Bessere Gruppen"; +/* No comment provided by engineer. */ +"Better groups performance" = "Bessere Leistung von Gruppen"; + +/* No comment provided by engineer. */ +"Better message dates." = "Verbesserte Nachrichten-Datumsinformation"; + /* No comment provided by engineer. */ "Better messages" = "Verbesserungen bei Nachrichten"; +/* No comment provided by engineer. */ +"Better networking" = "Kontrollieren Sie Ihr Netzwerk"; + +/* No comment provided by engineer. */ +"Better notifications" = "Verbesserte Benachrichtigungen"; + +/* No comment provided by engineer. */ +"Better privacy and security" = "Bessere(r) Security und Datenschutz"; + +/* No comment provided by engineer. */ +"Better security ✅" = "Verbesserte Sicherheit ✅"; + +/* No comment provided by engineer. */ +"Better user experience" = "Verbesserte Nutzer-Erfahrung"; + +/* No comment provided by engineer. */ +"Black" = "Schwarz"; + /* No comment provided by engineer. */ "Block" = "Blockieren"; @@ -608,12 +817,19 @@ /* rcv group event chat item */ "blocked %@" = "%@ wurde blockiert"; -/* marked deleted chat item preview text */ +/* blocked chat item +marked deleted chat item preview text */ "blocked by admin" = "wurde vom Administrator blockiert"; /* No comment provided by engineer. */ "Blocked by admin" = "wurde vom Administrator blockiert"; +/* No comment provided by engineer. */ +"Blur for better privacy." = "Für bessere Privatsphäre verpixeln."; + +/* No comment provided by engineer. */ +"Blur media" = "Medium verpixeln"; + /* No comment provided by engineer. */ "bold" = "fett"; @@ -635,9 +851,24 @@ /* No comment provided by engineer. */ "Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Bulgarisch, Finnisch, Thailändisch und Ukrainisch - Dank der Nutzer und [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"; +/* No comment provided by engineer. */ +"Business address" = "Geschäftliche Adresse"; + +/* No comment provided by engineer. */ +"Business chats" = "Geschäftliche Chats"; + +/* No comment provided by engineer. */ +"Businesses" = "Unternehmen"; + /* No comment provided by engineer. */ "By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Per Chat-Profil (Voreinstellung) oder [per Verbindung](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)."; +/* No comment provided by engineer. */ +"By using SimpleX Chat you agree to:\n- send only legal content in public groups.\n- respect other users – no spam." = "Durch die Nutzung von SimpleX Chat erklären Sie sich damit einverstanden:\n- nur legale Inhalte in öffentlichen Gruppen zu versenden.\n- andere Nutzer zu respektieren - kein Spam."; + +/* No comment provided by engineer. */ +"call" = "Anrufen"; + /* No comment provided by engineer. */ "Call already ended!" = "Anruf ist bereits beendet!"; @@ -653,9 +884,18 @@ /* No comment provided by engineer. */ "Calls" = "Anrufe"; +/* No comment provided by engineer. */ +"Calls prohibited!" = "Anrufe nicht zugelassen!"; + /* No comment provided by engineer. */ "Camera not available" = "Kamera nicht verfügbar"; +/* No comment provided by engineer. */ +"Can't call contact" = "Kontakt kann nicht angerufen werden"; + +/* No comment provided by engineer. */ +"Can't call member" = "Mitglied kann nicht angerufen werden"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Kontakt kann nicht eingeladen werden!"; @@ -663,8 +903,15 @@ "Can't invite contacts!" = "Kontakte können nicht eingeladen werden!"; /* No comment provided by engineer. */ +"Can't message member" = "Mitglied kann nicht benachrichtigt werden"; + +/* alert action +alert button */ "Cancel" = "Abbrechen"; +/* No comment provided by engineer. */ +"Cancel migration" = "Migration abbrechen"; + /* feature offered item */ "cancelled %@" = "abgebrochen %@"; @@ -672,11 +919,26 @@ "Cannot access keychain to save database password" = "Die App kann nicht auf den Schlüsselbund zugreifen, um das Datenbank-Passwort zu speichern"; /* No comment provided by engineer. */ +"Cannot forward message" = "Die Nachricht kann nicht weitergeleitet werden"; + +/* alert title */ "Cannot receive file" = "Datei kann nicht empfangen werden"; +/* snd error text */ +"Capacity exceeded - recipient did not receive previously sent messages." = "Kapazität überschritten - der Empfänger hat die zuvor gesendeten Nachrichten nicht empfangen."; + +/* No comment provided by engineer. */ +"Cellular" = "Mobilfunknetz"; + /* No comment provided by engineer. */ "Change" = "Ändern"; +/* alert title */ +"Change automatic message deletion?" = "Automatisches Löschen von Nachrichten ändern?"; + +/* authentication reason */ +"Change chat profiles" = "Chat-Profile wechseln"; + /* No comment provided by engineer. */ "Change database passphrase?" = "Datenbank-Passwort ändern?"; @@ -702,7 +964,7 @@ "Change self-destruct mode" = "Selbstzerstörungs-Modus ändern"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "Selbstzerstörungs-Zugangscode ändern"; /* chat item text */ @@ -721,7 +983,16 @@ "changing address…" = "Wechsel der Empfängeradresse wurde gestartet…"; /* No comment provided by engineer. */ -"Chat archive" = "Datenbank Archiv"; +"Chat" = "Chat"; + +/* No comment provided by engineer. */ +"Chat already exists" = "Chat besteht bereits"; + +/* No comment provided by engineer. */ +"Chat already exists!" = "Chat besteht bereits!"; + +/* No comment provided by engineer. */ +"Chat colors" = "Chat-Farben"; /* No comment provided by engineer. */ "Chat console" = "Chat-Konsole"; @@ -732,6 +1003,9 @@ /* No comment provided by engineer. */ "Chat database deleted" = "Chat-Datenbank gelöscht"; +/* No comment provided by engineer. */ +"Chat database exported" = "Chat-Datenbank wurde exportiert"; + /* No comment provided by engineer. */ "Chat database imported" = "Chat-Datenbank importiert"; @@ -744,18 +1018,48 @@ /* No comment provided by engineer. */ "Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "Der Chat ist angehalten. Wenn Sie diese Datenbank bereits auf einem anderen Gerät genutzt haben, sollten Sie diese vor dem Starten des Chats wieder zurückspielen."; +/* No comment provided by engineer. */ +"Chat list" = "Chat-Liste"; + +/* No comment provided by engineer. */ +"Chat migrated!" = "Chat wurde migriert!"; + /* No comment provided by engineer. */ "Chat preferences" = "Chat-Präferenzen"; +/* alert message */ +"Chat preferences were changed." = "Die Chat-Präferenzen wurden geändert."; + +/* No comment provided by engineer. */ +"Chat profile" = "Benutzerprofil"; + +/* No comment provided by engineer. */ +"Chat theme" = "Chat-Design"; + +/* No comment provided by engineer. */ +"Chat will be deleted for all members - this cannot be undone!" = "Der Chat wird für alle Mitglieder gelöscht. Dies kann nicht rückgängig gemacht werden!"; + +/* No comment provided by engineer. */ +"Chat will be deleted for you - this cannot be undone!" = "Der Chat wird für Sie gelöscht. Dies kann nicht rückgängig gemacht werden!"; + /* No comment provided by engineer. */ "Chats" = "Chats"; /* No comment provided by engineer. */ +"Check messages every 20 min." = "Alle 20min Nachrichten überprüfen."; + +/* No comment provided by engineer. */ +"Check messages when allowed." = "Wenn es erlaubt ist, Nachrichten überprüfen."; + +/* alert title */ "Check server address and try again." = "Überprüfen Sie die Serveradresse und versuchen Sie es nochmal."; /* No comment provided by engineer. */ "Chinese and Spanish interface" = "Chinesische und spanische Bedienoberfläche"; +/* No comment provided by engineer. */ +"Choose _Migrate from another device_ on the new device and scan QR code." = "Wählen Sie auf dem neuen Gerät _Von einem anderen Gerät migrieren_ und scannen Sie den QR-Code."; + /* No comment provided by engineer. */ "Choose file" = "Datei auswählen"; @@ -763,25 +1067,46 @@ "Choose from library" = "Aus dem Fotoalbum auswählen"; /* No comment provided by engineer. */ -"Clear" = "Löschen"; +"Chunks deleted" = "Daten-Pakete gelöscht"; /* No comment provided by engineer. */ -"Clear conversation" = "Chatinhalte löschen"; +"Chunks downloaded" = "Daten-Pakete heruntergeladen"; /* No comment provided by engineer. */ -"Clear conversation?" = "Unterhaltung löschen?"; +"Chunks uploaded" = "Daten-Pakete hochgeladen"; + +/* swipe action */ +"Clear" = "Entfernen"; /* No comment provided by engineer. */ -"Clear private notes?" = "Private Notizen löschen?"; +"Clear conversation" = "Chat-Inhalte entfernen"; + +/* No comment provided by engineer. */ +"Clear conversation?" = "Chat-Inhalte entfernen?"; + +/* No comment provided by engineer. */ +"Clear group?" = "Gruppe entfernen?"; + +/* No comment provided by engineer. */ +"Clear or delete group?" = "Gruppe entfernen oder löschen?"; + +/* No comment provided by engineer. */ +"Clear private notes?" = "Private Notizen entfernen?"; /* No comment provided by engineer. */ "Clear verification" = "Überprüfung zurücknehmen"; /* No comment provided by engineer. */ -"colored" = "farbig"; +"Color chats with the new themes." = "Farbige Chats mit neuen Designs."; /* No comment provided by engineer. */ -"Colors" = "Farben"; +"Color mode" = "Farbvariante"; + +/* No comment provided by engineer. */ +"colored" = "farbig"; + +/* report reason */ +"Community guidelines violation" = "Verstoß gegen die Gemeinschaftsrichtlinien"; /* server test step */ "Compare file" = "Datei vergleichen"; @@ -792,15 +1117,51 @@ /* No comment provided by engineer. */ "complete" = "vollständig"; +/* No comment provided by engineer. */ +"Completed" = "Abgeschlossen"; + +/* No comment provided by engineer. */ +"Conditions accepted on: %@." = "Die Nutzungsbedingungen wurden akzeptiert am: %@."; + +/* No comment provided by engineer. */ +"Conditions are accepted for the operator(s): **%@**." = "Die Nutzungsbedingungen der/des Betreiber(s) werden akzeptiert: **%@**."; + +/* No comment provided by engineer. */ +"Conditions are already accepted for these operator(s): **%@**." = "Die Nutzungsbedingungen der/des folgenden Betreiber(s) wurden schon akzeptiert: **%@**."; + +/* No comment provided by engineer. */ +"Conditions of use" = "Nutzungsbedingungen"; + +/* No comment provided by engineer. */ +"Conditions will be accepted for the operator(s): **%@**." = "Die Nutzungsbedingungen der/des Betreiber(s) werden akzeptiert: **%@**."; + +/* No comment provided by engineer. */ +"Conditions will be accepted on: %@." = "Die Nutzungsbedingungen werden akzeptiert am: %@."; + +/* No comment provided by engineer. */ +"Conditions will be automatically accepted for enabled operators on: %@." = "Die Nutzungsbedingungen der aktivierten Betreiber werden automatisch akzeptiert am: %@."; + /* No comment provided by engineer. */ "Configure ICE servers" = "ICE-Server konfigurieren"; +/* No comment provided by engineer. */ +"Configure server operators" = "Server-Betreiber konfigurieren"; + /* No comment provided by engineer. */ "Confirm" = "Bestätigen"; +/* No comment provided by engineer. */ +"Confirm contact deletion?" = "Löschen des Kontakts bestätigen?"; + /* No comment provided by engineer. */ "Confirm database upgrades" = "Datenbank-Aktualisierungen bestätigen"; +/* No comment provided by engineer. */ +"Confirm files from unknown servers." = "Dateien von unbekannten Servern bestätigen."; + +/* No comment provided by engineer. */ +"Confirm network settings" = "Bestätigen Sie die Netzwerkeinstellungen"; + /* No comment provided by engineer. */ "Confirm new passphrase…" = "Neues Passwort bestätigen…"; @@ -810,6 +1171,15 @@ /* No comment provided by engineer. */ "Confirm password" = "Passwort bestätigen"; +/* No comment provided by engineer. */ +"Confirm that you remember database passphrase to migrate it." = "Bitte bestätigen Sie für die Migration, dass Sie sich an Ihr Datenbank-Passwort erinnern."; + +/* No comment provided by engineer. */ +"Confirm upload" = "Hochladen bestätigen"; + +/* token status text */ +"Confirmed" = "Bestätigt"; + /* server test step */ "Connect" = "Verbinden"; @@ -825,6 +1195,9 @@ /* No comment provided by engineer. */ "connect to SimpleX Chat developers." = "Mit den SimpleX Chat-Entwicklern verbinden."; +/* No comment provided by engineer. */ +"Connect to your friends faster." = "Schneller mit Ihren Freunden verbinden."; + /* No comment provided by engineer. */ "Connect to yourself?" = "Mit Ihnen selbst verbinden?"; @@ -849,18 +1222,27 @@ /* No comment provided by engineer. */ "connected" = "Verbunden"; +/* No comment provided by engineer. */ +"Connected" = "Verbunden"; + /* No comment provided by engineer. */ "Connected desktop" = "Verbundener Desktop"; /* rcv group event chat item */ "connected directly" = "Direkt miteinander verbunden"; +/* No comment provided by engineer. */ +"Connected servers" = "Verbundene Server"; + /* No comment provided by engineer. */ "Connected to desktop" = "Mit dem Desktop verbunden"; /* No comment provided by engineer. */ "connecting" = "verbinde"; +/* No comment provided by engineer. */ +"Connecting" = "Verbinden"; + /* No comment provided by engineer. */ "connecting (accepted)" = "Verbindung (angenommen)"; @@ -882,15 +1264,24 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "Mit dem Server verbinden… (Fehler: %@)"; +/* No comment provided by engineer. */ +"Connecting to contact, please wait or check later!" = "Verbinde mit Kontakt, bitte warten oder später erneut überprüfen!"; + /* No comment provided by engineer. */ "Connecting to desktop" = "Mit dem Desktop verbinden"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "Verbinde…"; /* No comment provided by engineer. */ "Connection" = "Verbindung"; +/* No comment provided by engineer. */ +"Connection and servers status." = "Verbindungs- und Server-Status."; + +/* No comment provided by engineer. */ +"Connection blocked" = "Verbindung blockiert"; + /* No comment provided by engineer. */ "Connection error" = "Verbindungsfehler"; @@ -900,18 +1291,39 @@ /* chat list item title (it should not be shown */ "connection established" = "Verbindung hergestellt"; +/* No comment provided by engineer. */ +"Connection is blocked by server operator:\n%@" = "Die Verbindung wurde vom Server-Betreiber blockiert:\n%@"; + +/* No comment provided by engineer. */ +"Connection not ready." = "Verbindung noch nicht bereit."; + +/* No comment provided by engineer. */ +"Connection notifications" = "Verbindungsbenachrichtigungen"; + /* No comment provided by engineer. */ "Connection request sent!" = "Verbindungsanfrage wurde gesendet!"; +/* No comment provided by engineer. */ +"Connection requires encryption renegotiation." = "Die Verbindung erfordert eine Neuverhandlung der Verschlüsselung."; + +/* No comment provided by engineer. */ +"Connection security" = "Verbindungs-Sicherheit"; + /* No comment provided by engineer. */ "Connection terminated" = "Verbindung beendet"; /* No comment provided by engineer. */ "Connection timeout" = "Verbindungszeitüberschreitung"; +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "Die Verbindung mit dem Desktop wurde gestoppt"; + /* connection information */ "connection:%@" = "Verbindung:%@"; +/* No comment provided by engineer. */ +"Connections" = "Verbindungen"; + /* profile update event chat item */ "contact %@ changed to %@" = "Der Kontaktname wurde von %1$@ auf %2$@ geändert"; @@ -921,6 +1333,9 @@ /* No comment provided by engineer. */ "Contact already exists" = "Der Kontakt ist bereits vorhanden"; +/* No comment provided by engineer. */ +"Contact deleted!" = "Kontakt gelöscht!"; + /* No comment provided by engineer. */ "contact has e2e encryption" = "Kontakt nutzt E2E-Verschlüsselung"; @@ -934,7 +1349,7 @@ "Contact is connected" = "Mit Ihrem Kontakt verbunden"; /* No comment provided by engineer. */ -"Contact is not connected yet!" = "Ihr Kontakt ist noch nicht verbunden!"; +"Contact is deleted." = "Kontakt wurde gelöscht."; /* No comment provided by engineer. */ "Contact name" = "Kontaktname"; @@ -942,21 +1357,36 @@ /* No comment provided by engineer. */ "Contact preferences" = "Kontakt-Präferenzen"; +/* No comment provided by engineer. */ +"Contact will be deleted - this cannot be undone!" = "Kontakt wird gelöscht. Dies kann nicht rückgängig gemacht werden!"; + /* No comment provided by engineer. */ "Contacts" = "Kontakte"; /* No comment provided by engineer. */ "Contacts can mark messages for deletion; you will be able to view them." = "Ihre Kontakte können Nachrichten zum Löschen markieren. Sie können diese Nachrichten trotzdem anschauen."; +/* blocking reason */ +"Content violates conditions of use" = "Inhalt verletzt Nutzungsbedingungen"; + /* No comment provided by engineer. */ "Continue" = "Weiter"; -/* chat item action */ +/* No comment provided by engineer. */ +"Conversation deleted!" = "Chat-Inhalte entfernt!"; + +/* No comment provided by engineer. */ "Copy" = "Kopieren"; +/* No comment provided by engineer. */ +"Copy error" = "Fehlermeldung kopieren"; + /* No comment provided by engineer. */ "Core version: v%@" = "Core Version: v%@"; +/* No comment provided by engineer. */ +"Corner" = "Abrundung Ecken"; + /* No comment provided by engineer. */ "Correct name to %@?" = "Richtiger Name für %@?"; @@ -964,10 +1394,10 @@ "Create" = "Erstellen"; /* No comment provided by engineer. */ -"Create a group using a random profile." = "Erstellen Sie eine Gruppe mit einem zufälligen Profil."; +"Create 1-time link" = "Einmal-Link erstellen"; /* No comment provided by engineer. */ -"Create an address to let people connect with you." = "Erstellen Sie eine Adresse, damit sich Personen mit Ihnen verbinden können."; +"Create a group using a random profile." = "Erstellen Sie eine Gruppe mit einem zufälligen Profil."; /* server test step */ "Create file" = "Datei erstellen"; @@ -981,6 +1411,9 @@ /* No comment provided by engineer. */ "Create link" = "Link erzeugen"; +/* No comment provided by engineer. */ +"Create list" = "Liste erstellen"; + /* No comment provided by engineer. */ "Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" = "Neues Profil in der [Desktop-App] erstellen (https://simplex.chat/downloads/). 💻"; @@ -999,6 +1432,9 @@ /* No comment provided by engineer. */ "Create your profile" = "Erstellen Sie Ihr Profil"; +/* No comment provided by engineer. */ +"Created" = "Erstellt"; + /* No comment provided by engineer. */ "Created at" = "Erstellt um"; @@ -1006,7 +1442,7 @@ "Created at: %@" = "Erstellt um: %@"; /* No comment provided by engineer. */ -"Created on %@" = "Erstellt am %@"; +"Creating archive link" = "Archiv-Link erzeugen"; /* No comment provided by engineer. */ "Creating link…" = "Link wird erstellt…"; @@ -1014,12 +1450,18 @@ /* No comment provided by engineer. */ "creator" = "Ersteller"; +/* No comment provided by engineer. */ +"Current conditions text couldn't be loaded, you can review conditions via this link:" = "Der Text der aktuellen Nutzungsbedingungen konnte nicht geladen werden. Sie können die Nutzungsbedingungen unter diesem Link einsehen:"; + /* No comment provided by engineer. */ "Current Passcode" = "Aktueller Zugangscode"; /* No comment provided by engineer. */ "Current passphrase…" = "Aktuelles Passwort…"; +/* No comment provided by engineer. */ +"Current profile" = "Aktuelles Profil"; + /* No comment provided by engineer. */ "Currently maximum supported file size is %@." = "Die derzeit maximal unterstützte Dateigröße beträgt %@."; @@ -1029,9 +1471,18 @@ /* No comment provided by engineer. */ "Custom time" = "Zeit anpassen"; +/* No comment provided by engineer. */ +"Customizable message shape." = "Anpassbares Format des Nachrichtenfelds"; + +/* No comment provided by engineer. */ +"Customize theme" = "Design anpassen"; + /* No comment provided by engineer. */ "Dark" = "Dunkel"; +/* No comment provided by engineer. */ +"Dark mode colors" = "Farben für die dunkle Variante"; + /* No comment provided by engineer. */ "Database downgrade" = "Datenbank auf alte Version herabstufen"; @@ -1092,14 +1543,21 @@ /* time unit */ "days" = "Tage"; +/* No comment provided by engineer. */ +"Debug delivery" = "Debugging-Zustellung"; + /* No comment provided by engineer. */ "Decentralized" = "Dezentral"; /* message decrypt error item */ "Decryption error" = "Entschlüsselungsfehler"; -/* pref value */ -"default (%@)" = "Voreinstellung (%@)"; +/* No comment provided by engineer. */ +"decryption errors" = "Entschlüsselungs-Fehler"; + +/* delete after time +pref value */ +"default (%@)" = "Default (%@)"; /* No comment provided by engineer. */ "default (no)" = "Voreinstellung (Nein)"; @@ -1107,9 +1565,13 @@ /* No comment provided by engineer. */ "default (yes)" = "Voreinstellung (Ja)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "Löschen"; +/* No comment provided by engineer. */ +"Delete %lld messages of members?" = "%lld Nachrichten der Mitglieder löschen?"; + /* No comment provided by engineer. */ "Delete %lld messages?" = "%lld Nachrichten löschen?"; @@ -1129,10 +1591,10 @@ "Delete and notify contact" = "Kontakt löschen und benachrichtigen"; /* No comment provided by engineer. */ -"Delete archive" = "Archiv löschen"; +"Delete chat" = "Chat löschen"; /* No comment provided by engineer. */ -"Delete chat archive?" = "Chat Archiv löschen?"; +"Delete chat messages from your device." = "Chat-Nachrichten von Ihrem Gerät löschen."; /* No comment provided by engineer. */ "Delete chat profile" = "Chat-Profil löschen"; @@ -1140,6 +1602,9 @@ /* No comment provided by engineer. */ "Delete chat profile?" = "Chat-Profil löschen?"; +/* No comment provided by engineer. */ +"Delete chat?" = "Chat löschen?"; + /* No comment provided by engineer. */ "Delete connection" = "Verbindung löschen"; @@ -1147,14 +1612,14 @@ "Delete contact" = "Kontakt löschen"; /* No comment provided by engineer. */ -"Delete Contact" = "Kontakt löschen"; - -/* No comment provided by engineer. */ -"Delete contact?\nThis cannot be undone!" = "Kontakt löschen?\nDas kann nicht rückgängig gemacht werden!"; +"Delete contact?" = "Kontakt löschen?"; /* No comment provided by engineer. */ "Delete database" = "Datenbank löschen"; +/* No comment provided by engineer. */ +"Delete database from this device" = "Datenbank auf diesem Gerät löschen"; + /* server test step */ "Delete file" = "Datei löschen"; @@ -1185,17 +1650,20 @@ /* No comment provided by engineer. */ "Delete link?" = "Link löschen?"; +/* alert title */ +"Delete list?" = "Liste löschen?"; + /* No comment provided by engineer. */ "Delete member message?" = "Nachricht des Mitglieds löschen?"; /* No comment provided by engineer. */ "Delete message?" = "Die Nachricht löschen?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "Nachrichten löschen"; /* No comment provided by engineer. */ -"Delete messages after" = "Löschen der Nachrichten"; +"Delete messages after" = "Nachrichten löschen"; /* No comment provided by engineer. */ "Delete old database" = "Alte Datenbank löschen"; @@ -1204,10 +1672,10 @@ "Delete old database?" = "Alte Datenbank löschen?"; /* No comment provided by engineer. */ -"Delete pending connection" = "Ausstehende Verbindung löschen"; +"Delete or moderate up to 200 messages." = "Bis zu 200 Nachrichten löschen oder moderieren"; /* No comment provided by engineer. */ -"Delete pending connection?" = "Die ausstehende Verbindung löschen?"; +"Delete pending connection?" = "Ausstehende Verbindung löschen?"; /* No comment provided by engineer. */ "Delete profile" = "Profil löschen"; @@ -1215,12 +1683,24 @@ /* server test step */ "Delete queue" = "Lösche Warteschlange"; +/* No comment provided by engineer. */ +"Delete report" = "Meldung löschen"; + +/* No comment provided by engineer. */ +"Delete up to 20 messages at once." = "Löschen Sie bis zu 20 Nachrichten auf einmal."; + /* No comment provided by engineer. */ "Delete user profile?" = "Benutzerprofil löschen?"; +/* No comment provided by engineer. */ +"Delete without notification" = "Ohne Benachrichtigung löschen"; + /* deleted chat item */ "deleted" = "Gelöscht"; +/* No comment provided by engineer. */ +"Deleted" = "Gelöscht"; + /* No comment provided by engineer. */ "Deleted at" = "Gelöscht um"; @@ -1233,6 +1713,12 @@ /* rcv group event chat item */ "deleted group" = "Gruppe gelöscht"; +/* No comment provided by engineer. */ +"Deletion errors" = "Fehler beim Löschen"; + +/* No comment provided by engineer. */ +"Delivered even when Apple drops them." = "Auslieferung, selbst wenn Apple sie löscht."; + /* No comment provided by engineer. */ "Delivery" = "Zustellung"; @@ -1254,9 +1740,27 @@ /* No comment provided by engineer. */ "Desktop devices" = "Desktop-Geräte"; +/* No comment provided by engineer. */ +"Destination server address of %@ is incompatible with forwarding server %@ settings." = "Adresse des Zielservers von %@ ist nicht kompatibel mit den Einstellungen des Weiterleitungsservers %@."; + +/* snd error text */ +"Destination server error: %@" = "Zielserver-Fehler: %@"; + +/* No comment provided by engineer. */ +"Destination server version of %@ is incompatible with forwarding server %@." = "Die Version des Zielservers %@ ist nicht kompatibel mit dem Weiterleitungsserver %@."; + +/* No comment provided by engineer. */ +"Detailed statistics" = "Detaillierte Statistiken"; + +/* No comment provided by engineer. */ +"Details" = "Details"; + /* No comment provided by engineer. */ "Develop" = "Entwicklung"; +/* No comment provided by engineer. */ +"Developer options" = "Optionen für Entwickler"; + /* No comment provided by engineer. */ "Developer tools" = "Entwicklertools"; @@ -1282,11 +1786,20 @@ "Direct messages" = "Direkte Nachrichten"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "In dieser Gruppe sind Direktnachrichten zwischen Mitgliedern nicht erlaubt."; +"Direct messages between members are prohibited in this chat." = "In diesem Chat sind Direktnachrichten zwischen Mitgliedern nicht erlaubt."; + +/* No comment provided by engineer. */ +"Direct messages between members are prohibited." = "In dieser Gruppe sind Direktnachrichten zwischen Mitgliedern nicht erlaubt."; /* No comment provided by engineer. */ "Disable (keep overrides)" = "Deaktivieren (vorgenommene Einstellungen bleiben erhalten)"; +/* alert title */ +"Disable automatic message deletion?" = "Automatisches Löschen von Nachrichten deaktivieren?"; + +/* alert button */ +"Disable delete messages" = "Löschen von Nachrichten deaktivieren"; + /* No comment provided by engineer. */ "Disable for all" = "Für Alle deaktivieren"; @@ -1296,6 +1809,9 @@ /* No comment provided by engineer. */ "disabled" = "deaktiviert"; +/* No comment provided by engineer. */ +"Disabled" = "Deaktiviert"; + /* No comment provided by engineer. */ "Disappearing message" = "Verschwindende Nachricht"; @@ -1306,7 +1822,7 @@ "Disappearing messages are prohibited in this chat." = "In diesem Chat sind verschwindende Nachrichten nicht erlaubt."; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "In dieser Gruppe sind verschwindende Nachrichten nicht erlaubt."; +"Disappearing messages are prohibited." = "In dieser Gruppe sind verschwindende Nachrichten nicht erlaubt."; /* No comment provided by engineer. */ "Disappears at" = "Verschwindet um"; @@ -1333,7 +1849,19 @@ "Do not send history to new members." = "Den Nachrichtenverlauf nicht an neue Mitglieder senden."; /* No comment provided by engineer. */ -"Do NOT use SimpleX for emergency calls." = "Nutzen Sie SimpleX nicht für Notrufe."; +"Do NOT send messages directly, even if your or destination server does not support private routing." = "Nachrichten werden nicht direkt versendet, selbst wenn Ihr oder der Zielserver kein privates Routing unterstützt."; + +/* No comment provided by engineer. */ +"Do not use credentials with proxy." = "Verwenden Sie keine Anmeldeinformationen mit einem Proxy."; + +/* No comment provided by engineer. */ +"Do NOT use private routing." = "Sie nutzen KEIN privates Routing."; + +/* No comment provided by engineer. */ +"Do NOT use SimpleX for emergency calls." = "SimpleX NICHT für Notrufe nutzen."; + +/* No comment provided by engineer. */ +"Documents:" = "Dokumente:"; /* No comment provided by engineer. */ "Don't create address" = "Keine Adresse erstellt"; @@ -1341,27 +1869,64 @@ /* No comment provided by engineer. */ "Don't enable" = "Nicht aktivieren"; +/* No comment provided by engineer. */ +"Don't miss important messages." = "Verpassen Sie keine wichtigen Nachrichten."; + /* No comment provided by engineer. */ "Don't show again" = "Nicht nochmals anzeigen"; +/* No comment provided by engineer. */ +"Done" = "Fertig"; + /* No comment provided by engineer. */ "Downgrade and open chat" = "Datenbank herabstufen und den Chat öffnen"; +/* alert button +chat item action */ +"Download" = "Herunterladen"; + +/* No comment provided by engineer. */ +"Download errors" = "Fehler beim Herunterladen"; + +/* No comment provided by engineer. */ +"Download failed" = "Herunterladen fehlgeschlagen"; + /* server test step */ "Download file" = "Datei herunterladen"; +/* alert action */ +"Download files" = "Dateien herunterladen"; + +/* No comment provided by engineer. */ +"Downloaded" = "Heruntergeladen"; + +/* No comment provided by engineer. */ +"Downloaded files" = "Heruntergeladene Dateien"; + +/* No comment provided by engineer. */ +"Downloading archive" = "Archiv wird heruntergeladen"; + +/* No comment provided by engineer. */ +"Downloading link details" = "Link-Details werden heruntergeladen"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Doppelter Anzeigename!"; /* integrity error chat item */ "duplicate message" = "Doppelte Nachricht"; +/* No comment provided by engineer. */ +"duplicates" = "Duplikate"; + /* No comment provided by engineer. */ "Duration" = "Dauer"; /* No comment provided by engineer. */ "e2e encrypted" = "E2E-verschlüsselt"; +/* No comment provided by engineer. */ +"E2E encrypted notifications." = "E2E-verschlüsselte Benachrichtigungen."; + /* chat item action */ "Edit" = "Bearbeiten"; @@ -1374,15 +1939,21 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "Aktivieren (vorgenommene Einstellungen bleiben erhalten)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "Automatisches Löschen von Nachrichten aktivieren?"; /* No comment provided by engineer. */ "Enable camera access" = "Kamera-Zugriff aktivieren"; +/* No comment provided by engineer. */ +"Enable Flux in Network & servers settings for better metadata privacy." = "Für einen besseren Metadatenschutz Flux in den Netzwerk- und Servereinstellungen aktivieren."; + /* No comment provided by engineer. */ "Enable for all" = "Für Alle aktivieren"; +/* No comment provided by engineer. */ +"Enable in direct chats (BETA)!" = "Kann in direkten Chats aktiviert werden (BETA)!"; + /* No comment provided by engineer. */ "Enable instant notifications?" = "Sofortige Benachrichtigungen aktivieren?"; @@ -1410,6 +1981,12 @@ /* enabled status */ "enabled" = "Aktiviert"; +/* No comment provided by engineer. */ +"Enabled" = "Aktiviert"; + +/* No comment provided by engineer. */ +"Enabled for" = "Aktiviert für"; + /* enabled status */ "enabled for contact" = "Für Kontakt aktiviert"; @@ -1482,6 +2059,9 @@ /* chat item text */ "encryption re-negotiation required for %@" = "Neuaushandlung der Verschlüsselung von %@ notwendig"; +/* No comment provided by engineer. */ +"Encryption renegotiation in progress." = "Die Neuverhandlung der Verschlüsselung läuft."; + /* No comment provided by engineer. */ "ended" = "beendet"; @@ -1497,6 +2077,9 @@ /* No comment provided by engineer. */ "Enter Passcode" = "Zugangscode eingeben"; +/* No comment provided by engineer. */ +"Enter passphrase" = "Passwort eingeben"; + /* No comment provided by engineer. */ "Enter passphrase…" = "Passwort eingeben…"; @@ -1525,26 +2108,41 @@ "Error" = "Fehler"; /* No comment provided by engineer. */ -"Error aborting address change" = "Fehler beim Abbrechen des Adresswechsels"; +"Error aborting address change" = "Fehler beim Beenden des Adresswechsels"; + +/* alert title */ +"Error accepting conditions" = "Fehler beim Akzeptieren der Nutzungsbedingungen"; /* No comment provided by engineer. */ "Error accepting contact request" = "Fehler beim Annehmen der Kontaktanfrage"; -/* No comment provided by engineer. */ -"Error accessing database file" = "Fehler beim Zugriff auf die Datenbankdatei"; - /* No comment provided by engineer. */ "Error adding member(s)" = "Fehler beim Hinzufügen von Mitgliedern"; +/* alert title */ +"Error adding server" = "Fehler beim Hinzufügen des Servers"; + /* No comment provided by engineer. */ "Error changing address" = "Fehler beim Wechseln der Empfängeradresse"; +/* No comment provided by engineer. */ +"Error changing connection profile" = "Fehler beim Wechseln des Verbindungs-Profils"; + /* No comment provided by engineer. */ "Error changing role" = "Fehler beim Ändern der Rolle"; /* No comment provided by engineer. */ "Error changing setting" = "Fehler beim Ändern der Einstellung"; +/* No comment provided by engineer. */ +"Error changing to incognito!" = "Fehler beim Wechseln zum Inkognito-Profil!"; + +/* No comment provided by engineer. */ +"Error checking token status" = "Fehler beim Überprüfen des Token-Status"; + +/* No comment provided by engineer. */ +"Error connecting to forwarding server %@. Please try later." = "Fehler beim Verbinden mit dem Weiterleitungsserver %@. Bitte versuchen Sie es später erneut."; + /* No comment provided by engineer. */ "Error creating address" = "Fehler beim Erstellen der Adresse"; @@ -1554,6 +2152,9 @@ /* No comment provided by engineer. */ "Error creating group link" = "Fehler beim Erzeugen des Gruppen-Links"; +/* alert title */ +"Error creating list" = "Fehler beim Erstellen der Liste"; + /* No comment provided by engineer. */ "Error creating member contact" = "Fehler beim Anlegen eines Mitglied-Kontaktes"; @@ -1563,6 +2164,9 @@ /* No comment provided by engineer. */ "Error creating profile!" = "Fehler beim Erstellen des Profils!"; +/* No comment provided by engineer. */ +"Error creating report" = "Fehler beim Erstellen der Meldung"; + /* No comment provided by engineer. */ "Error decrypting file" = "Fehler beim Entschlüsseln der Datei"; @@ -1575,9 +2179,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "Fehler beim Löschen der Verbindung"; -/* No comment provided by engineer. */ -"Error deleting contact" = "Fehler beim Löschen des Kontakts"; - /* No comment provided by engineer. */ "Error deleting database" = "Fehler beim Löschen der Datenbank"; @@ -1590,6 +2191,9 @@ /* No comment provided by engineer. */ "Error deleting user profile" = "Fehler beim Löschen des Benutzerprofils"; +/* No comment provided by engineer. */ +"Error downloading the archive" = "Fehler beim Herunterladen des Archivs"; + /* No comment provided by engineer. */ "Error enabling delivery receipts!" = "Fehler beim Aktivieren von Empfangsbestätigungen!"; @@ -1602,26 +2206,47 @@ /* No comment provided by engineer. */ "Error exporting chat database" = "Fehler beim Exportieren der Chat-Datenbank"; +/* No comment provided by engineer. */ +"Error exporting theme: %@" = "Fehler beim Exportieren des Designs: %@"; + /* No comment provided by engineer. */ "Error importing chat database" = "Fehler beim Importieren der Chat-Datenbank"; /* No comment provided by engineer. */ "Error joining group" = "Fehler beim Beitritt zur Gruppe"; +/* alert title */ +"Error loading servers" = "Fehler beim Laden der Server"; + /* No comment provided by engineer. */ -"Error loading %@ servers" = "Fehler beim Laden von %@ Servern"; +"Error migrating settings" = "Fehler beim Migrieren der Einstellungen"; /* No comment provided by engineer. */ "Error opening chat" = "Fehler beim Öffnen des Chats"; +/* alert title */ +"Error receiving file" = "Fehler beim Herunterladen der Datei"; + /* No comment provided by engineer. */ -"Error receiving file" = "Fehler beim Empfangen der Datei"; +"Error reconnecting server" = "Fehler beim Wiederherstellen der Verbindung zum Server"; + +/* No comment provided by engineer. */ +"Error reconnecting servers" = "Fehler beim Wiederherstellen der Verbindungen zu den Servern"; + +/* alert title */ +"Error registering for notifications" = "Fehler beim Registrieren für Benachrichtigungen"; /* No comment provided by engineer. */ "Error removing member" = "Fehler beim Entfernen des Mitglieds"; +/* alert title */ +"Error reordering lists" = "Fehler beim Umsortieren der Listen"; + /* No comment provided by engineer. */ -"Error saving %@ servers" = "Fehler beim Speichern der %@-Server"; +"Error resetting statistics" = "Fehler beim Zurücksetzen der Statistiken"; + +/* alert title */ +"Error saving chat list" = "Fehler beim Speichern der Chat-Liste"; /* No comment provided by engineer. */ "Error saving group profile" = "Fehler beim Speichern des Gruppenprofils"; @@ -1635,6 +2260,12 @@ /* No comment provided by engineer. */ "Error saving passphrase to keychain" = "Fehler beim Speichern des Passworts in den Schlüsselbund"; +/* alert title */ +"Error saving servers" = "Fehler beim Speichern der Server"; + +/* when migrating */ +"Error saving settings" = "Fehler beim Abspeichern der Einstellungen"; + /* No comment provided by engineer. */ "Error saving user password" = "Fehler beim Speichern des Benutzer-Passworts"; @@ -1660,17 +2291,26 @@ "Error stopping chat" = "Fehler beim Beenden des Chats"; /* No comment provided by engineer. */ +"Error switching profile" = "Fehler beim Wechseln des Profils"; + +/* alertTitle */ "Error switching profile!" = "Fehler beim Umschalten des Profils!"; /* No comment provided by engineer. */ "Error synchronizing connection" = "Fehler beim Synchronisieren der Verbindung"; +/* No comment provided by engineer. */ +"Error testing server connection" = "Fehler beim Testen der Server-Verbindung"; + /* No comment provided by engineer. */ "Error updating group link" = "Fehler beim Aktualisieren des Gruppen-Links"; /* No comment provided by engineer. */ "Error updating message" = "Fehler beim Aktualisieren der Nachricht"; +/* alert title */ +"Error updating server" = "Fehler beim Aktualisieren des Servers"; + /* No comment provided by engineer. */ "Error updating settings" = "Fehler beim Aktualisieren der Einstellungen"; @@ -1678,9 +2318,17 @@ "Error updating user privacy" = "Fehler beim Aktualisieren der Benutzer-Privatsphäre"; /* No comment provided by engineer. */ -"Error: " = "Fehler: "; +"Error uploading the archive" = "Fehler beim Hochladen des Archivs"; /* No comment provided by engineer. */ +"Error verifying passphrase:" = "Fehler bei der Überprüfung des Passworts:"; + +/* No comment provided by engineer. */ +"Error: " = "Fehler: "; + +/* alert message +file error text +snd error text */ "Error: %@" = "Fehler: %@"; /* No comment provided by engineer. */ @@ -1690,10 +2338,13 @@ "Error: URL is invalid" = "Fehler: URL ist ungültig"; /* No comment provided by engineer. */ -"Even when disabled in the conversation." = "Auch wenn sie im Chat deaktiviert sind."; +"Errors" = "Fehler"; + +/* servers error */ +"Errors in servers configuration." = "Fehler in der Server-Konfiguration."; /* No comment provided by engineer. */ -"event happened" = "event happened"; +"Even when disabled in the conversation." = "Auch wenn sie im Chat deaktiviert sind."; /* No comment provided by engineer. */ "Exit without saving" = "Beenden ohne Speichern"; @@ -1701,15 +2352,27 @@ /* chat item action */ "Expand" = "Erweitern"; +/* No comment provided by engineer. */ +"expired" = "Abgelaufen"; + +/* token status text */ +"Expired" = "Abgelaufen"; + /* No comment provided by engineer. */ "Export database" = "Datenbank exportieren"; /* No comment provided by engineer. */ "Export error:" = "Fehler beim Export:"; +/* No comment provided by engineer. */ +"Export theme" = "Design exportieren"; + /* No comment provided by engineer. */ "Exported database archive." = "Exportiertes Datenbankarchiv."; +/* No comment provided by engineer. */ +"Exported file doesn't exist" = "Die exportierte Datei ist nicht vorhanden"; + /* No comment provided by engineer. */ "Exporting database archive…" = "Exportieren des Datenbank-Archivs…"; @@ -1719,24 +2382,57 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "Schnell und ohne warten auf den Absender, bis er online ist!"; +/* No comment provided by engineer. */ +"Faster deletion of groups." = "Schnelleres löschen von Gruppen."; + /* No comment provided by engineer. */ "Faster joining and more reliable messages." = "Schnellerer Gruppenbeitritt und zuverlässigere Nachrichtenzustellung."; /* No comment provided by engineer. */ +"Faster sending messages." = "Schnelleres versenden von Nachrichten."; + +/* swipe action */ "Favorite" = "Favorit"; +/* No comment provided by engineer. */ +"Favorites" = "Favoriten"; + +/* file error alert title */ +"File error" = "Datei-Fehler"; + +/* alert message */ +"File errors:\n%@" = "Datei-Fehler:\n%@"; + +/* file error text */ +"File is blocked by server operator:\n%@." = "Datei wurde vom Server-Betreiber blockiert:\n%@."; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "Datei nicht gefunden - höchstwahrscheinlich wurde die Datei gelöscht oder der Transfer abgebrochen."; + +/* file error text */ +"File server error: %@" = "Datei-Server Fehler: %@"; + +/* No comment provided by engineer. */ +"File status" = "Datei-Status"; + +/* copied message info */ +"File status: %@" = "Datei-Status: %@"; + /* No comment provided by engineer. */ "File will be deleted from servers." = "Die Datei wird von den Servern gelöscht."; /* No comment provided by engineer. */ -"File will be received when your contact completes uploading it." = "Die Datei wird empfangen, sobald das Hochladen durch ihren Kontakt abgeschlossen ist."; +"File will be received when your contact completes uploading it." = "Die Datei wird heruntergeladen, sobald das Hochladen durch ihren Kontakt abgeschlossen ist."; /* No comment provided by engineer. */ -"File will be received when your contact is online, please wait or check later!" = "Die Datei wird empfangen, sobald Ihr Kontakt online ist. Bitte warten oder schauen Sie später nochmal nach!"; +"File will be received when your contact is online, please wait or check later!" = "Die Datei wird heruntergeladen, sobald Ihr Kontakt online ist. Bitte warten oder schauen Sie später nochmal nach!"; /* No comment provided by engineer. */ "File: %@" = "Datei: %@"; +/* No comment provided by engineer. */ +"Files" = "Dateien"; + /* No comment provided by engineer. */ "Files & media" = "Dateien & Medien"; @@ -1744,7 +2440,10 @@ "Files and media" = "Dateien und Medien"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "In dieser Gruppe sind Dateien und Medien nicht erlaubt."; +"Files and media are prohibited." = "In dieser Gruppe sind Dateien und Medien nicht erlaubt."; + +/* No comment provided by engineer. */ +"Files and media not allowed" = "Dateien und Medien sind nicht erlaubt"; /* No comment provided by engineer. */ "Files and media prohibited!" = "Dateien und Medien sind nicht erlaubt!"; @@ -1752,6 +2451,12 @@ /* No comment provided by engineer. */ "Filter unread and favorite chats." = "Nach ungelesenen und favorisierten Chats filtern."; +/* No comment provided by engineer. */ +"Finalize migration" = "Die Migration wird abgeschlossen"; + +/* No comment provided by engineer. */ +"Finalize migration on another device." = "Die Migration auf dem anderen Gerät wird abgeschlossen."; + /* No comment provided by engineer. */ "Finally, we have them! 🚀" = "Endlich haben wir sie! 🚀"; @@ -1776,9 +2481,72 @@ /* No comment provided by engineer. */ "Fix not supported by group member" = "Reparatur wird vom Gruppenmitglied nicht unterstützt"; +/* No comment provided by engineer. */ +"For all moderators" = "Für alle Moderatoren"; + +/* servers error */ +"For chat profile %@:" = "Für das Chat-Profil %@:"; + /* No comment provided by engineer. */ "For console" = "Für Konsole"; +/* No comment provided by engineer. */ +"For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server." = "Wenn Ihr Kontakt beispielsweise Nachrichten über einen SimpleX-Chatserver empfängt, wird Ihre App diese über einen der Server von Flux versenden."; + +/* No comment provided by engineer. */ +"For me" = "Für mich"; + +/* No comment provided by engineer. */ +"For private routing" = "Für privates Routing"; + +/* No comment provided by engineer. */ +"For social media" = "Für soziale Medien"; + +/* chat item action */ +"Forward" = "Weiterleiten"; + +/* alert title */ +"Forward %d message(s)?" = "%d Nachricht(en) weiterleiten?"; + +/* No comment provided by engineer. */ +"Forward and save messages" = "Nachrichten weiterleiten und speichern"; + +/* alert action */ +"Forward messages" = "Nachrichten weiterleiten"; + +/* alert message */ +"Forward messages without files?" = "Nachrichten ohne Dateien weiterleiten?"; + +/* No comment provided by engineer. */ +"Forward up to 20 messages at once." = "Bis zu 20 Nachrichten auf einmal weiterleiten"; + +/* No comment provided by engineer. */ +"forwarded" = "weitergeleitet"; + +/* No comment provided by engineer. */ +"Forwarded" = "Weitergeleitet"; + +/* No comment provided by engineer. */ +"Forwarded from" = "Weitergeleitet aus"; + +/* No comment provided by engineer. */ +"Forwarding %lld messages" = "%lld Nachricht(en) wird/werden weitergeleitet"; + +/* No comment provided by engineer. */ +"Forwarding server %@ failed to connect to destination server %@. Please try later." = "Weiterleitungsserver %@ konnte sich nicht mit dem Zielserver %@ verbinden. Bitte versuchen Sie es später erneut."; + +/* No comment provided by engineer. */ +"Forwarding server address is incompatible with network settings: %@." = "Adresse des Weiterleitungsservers ist nicht kompatibel mit den Netzwerkeinstellungen: %@."; + +/* No comment provided by engineer. */ +"Forwarding server version is incompatible with network settings: %@." = "Version des Weiterleitungsservers ist nicht kompatibel mit den Netzwerkeinstellungen: %@."; + +/* snd error text */ +"Forwarding server: %@\nDestination server error: %@" = "Weiterleitungsserver: %1$@\nZielserver Fehler: %2$@"; + +/* snd error text */ +"Forwarding server: %@\nError: %@" = "Weiterleitungsserver: %1$@\nFehler: %2$@"; + /* No comment provided by engineer. */ "Found desktop" = "Gefundener Desktop"; @@ -1791,9 +2559,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Vollständiger Name (optional)"; -/* No comment provided by engineer. */ -"Full name:" = "Vollständiger Name:"; - /* No comment provided by engineer. */ "Fully decentralized – visible only to members." = "Vollständig dezentralisiert – nur für Mitglieder sichtbar."; @@ -1803,9 +2568,18 @@ /* No comment provided by engineer. */ "Further reduced battery usage" = "Weiter reduzierter Batterieverbrauch"; +/* No comment provided by engineer. */ +"Get notified when mentioned." = "Bei Erwähnung benachrichtigt werden."; + /* No comment provided by engineer. */ "GIFs and stickers" = "GIFs und Sticker"; +/* message preview */ +"Good afternoon!" = "Guten Nachmittag!"; + +/* message preview */ +"Good morning!" = "Guten Morgen!"; + /* No comment provided by engineer. */ "Group" = "Gruppe"; @@ -1842,24 +2616,6 @@ /* No comment provided by engineer. */ "Group links" = "Gruppen-Links"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "Gruppenmitglieder können eine Reaktion auf Nachrichten geben."; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "Gruppenmitglieder können gesendete Nachrichten unwiederbringlich löschen. (24 Stunden)"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "Gruppenmitglieder können Direktnachrichten versenden."; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "Gruppenmitglieder können verschwindende Nachrichten senden."; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "Gruppenmitglieder können Dateien und Medien senden."; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "Gruppenmitglieder können Sprachnachrichten versenden."; - /* notification */ "Group message:" = "Grppennachricht:"; @@ -1882,14 +2638,20 @@ "Group welcome message" = "Gruppen-Begrüßungsmeldung"; /* No comment provided by engineer. */ -"Group will be deleted for all members - this cannot be undone!" = "Die Gruppe wird für alle Mitglieder gelöscht - dies kann nicht rückgängig gemacht werden!"; +"Group will be deleted for all members - this cannot be undone!" = "Die Gruppe wird für alle Mitglieder gelöscht. Dies kann nicht rückgängig gemacht werden!"; /* No comment provided by engineer. */ -"Group will be deleted for you - this cannot be undone!" = "Die Gruppe wird für Sie gelöscht - dies kann nicht rückgängig gemacht werden!"; +"Group will be deleted for you - this cannot be undone!" = "Die Gruppe wird nur bei Ihnen gelöscht. Dies kann nicht rückgängig gemacht werden!"; + +/* No comment provided by engineer. */ +"Groups" = "Gruppen"; /* No comment provided by engineer. */ "Help" = "Hilfe"; +/* No comment provided by engineer. */ +"Help admins moderating their groups." = "Helfen Sie Administratoren bei der Moderation ihrer Gruppen."; + /* No comment provided by engineer. */ "Hidden" = "Verborgen"; @@ -1921,6 +2683,12 @@ "hours" = "Stunden"; /* No comment provided by engineer. */ +"How it affects privacy" = "Wie es die Privatsphäre beeinflusst"; + +/* No comment provided by engineer. */ +"How it helps privacy" = "Wie es die Privatsphäre schützt"; + +/* alert button */ "How it works" = "Wie es funktioniert"; /* No comment provided by engineer. */ @@ -1935,6 +2703,9 @@ /* No comment provided by engineer. */ "How to use your servers" = "Wie Sie Ihre Server nutzen"; +/* No comment provided by engineer. */ +"Hungarian interface" = "Ungarische Bedienoberfläche"; + /* No comment provided by engineer. */ "ICE servers (one per line)" = "ICE-Server (einer pro Zeile)"; @@ -1954,16 +2725,16 @@ "Ignore" = "Ignorieren"; /* No comment provided by engineer. */ -"Image will be received when your contact completes uploading it." = "Das Bild wird empfangen, sobald das Hochladen durch ihren Kontakt abgeschlossen ist."; +"Image will be received when your contact completes uploading it." = "Das Bild wird heruntergeladen, sobald das Hochladen durch ihren Kontakt abgeschlossen ist."; /* No comment provided by engineer. */ -"Image will be received when your contact is online, please wait or check later!" = "Das Bild wird empfangen, sobald Ihr Kontakt online ist. Bitte warten oder schauen Sie später nochmal nach!"; +"Image will be received when your contact is online, please wait or check later!" = "Das Bild wird heruntergeladen, sobald Ihr Kontakt online ist. Bitte warten oder schauen Sie später nochmal nach!"; /* No comment provided by engineer. */ "Immediately" = "Sofort"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "Immun gegen Spam und Missbrauch"; +"Immune to spam" = "Immun gegen Spam und Missbrauch"; /* No comment provided by engineer. */ "Import" = "Importieren"; @@ -1974,6 +2745,18 @@ /* No comment provided by engineer. */ "Import database" = "Datenbank importieren"; +/* No comment provided by engineer. */ +"Import failed" = "Import ist fehlgeschlagen"; + +/* No comment provided by engineer. */ +"Import theme" = "Design importieren"; + +/* No comment provided by engineer. */ +"Importing archive" = "Archiv wird importiert"; + +/* No comment provided by engineer. */ +"Improved delivery, reduced traffic usage.\nMore improvements are coming soon!" = "Verbesserte Nachrichten-Auslieferung und verringerter Datenverbrauch.\nWeitere Verbesserungen sind bald verfügbar!"; + /* No comment provided by engineer. */ "Improved message delivery" = "Verbesserte Zustellung von Nachrichten"; @@ -1983,9 +2766,24 @@ /* No comment provided by engineer. */ "Improved server configuration" = "Verbesserte Serverkonfiguration"; +/* No comment provided by engineer. */ +"In order to continue, chat should be stopped." = "Um fortzufahren, sollte der Chat beendet werden."; + /* No comment provided by engineer. */ "In reply to" = "Als Antwort auf"; +/* No comment provided by engineer. */ +"In-call sounds" = "Klingeltöne"; + +/* No comment provided by engineer. */ +"inactive" = "Inaktiv"; + +/* report reason */ +"Inappropriate content" = "Unangemessener Inhalt"; + +/* report reason */ +"Inappropriate profile" = "Unangemessenes Profil"; + /* No comment provided by engineer. */ "Incognito" = "Inkognito"; @@ -2017,10 +2815,10 @@ "Incoming video call" = "Eingehender Videoanruf"; /* No comment provided by engineer. */ -"Incompatible database version" = "Inkompatible Datenbank-Version"; +"Incompatible database version" = "Datenbank-Version nicht kompatibel"; /* No comment provided by engineer. */ -"Incompatible version" = "Inkompatible Version"; +"Incompatible version" = "Version nicht kompatibel"; /* PIN entry */ "Incorrect passcode" = "Zugangscode ist falsch"; @@ -2040,14 +2838,32 @@ /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Installieren Sie [SimpleX Chat als Terminalanwendung](https://github.com/simplex-chat/simplex-chat)"; +/* No comment provided by engineer. */ +"Instant" = "Sofort"; + /* No comment provided by engineer. */ "Instant push notifications will be hidden!\n" = "Sofortige Push-Benachrichtigungen werden verborgen!\n"; /* No comment provided by engineer. */ -"Instantly" = "Sofort"; +"Interface" = "Schnittstelle"; /* No comment provided by engineer. */ -"Interface" = "Schnittstelle"; +"Interface colors" = "Interface-Farben"; + +/* token status text */ +"Invalid" = "Ungültig"; + +/* token status text */ +"Invalid (bad token)" = "Ungültig (falsches Token)"; + +/* token status text */ +"Invalid (expired)" = "Ungültig (abgelaufen)"; + +/* token status text */ +"Invalid (unregistered)" = "Ungültig (nicht registriert)"; + +/* token status text */ +"Invalid (wrong topic)" = "Ungültig (falsches Thema)"; /* invalid chat data */ "invalid chat" = "Ungültiger Chat"; @@ -2067,6 +2883,9 @@ /* No comment provided by engineer. */ "Invalid link" = "Ungültiger Link"; +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Migrations-Bestätigung ungültig"; + /* No comment provided by engineer. */ "Invalid name!" = "Ungültiger Name!"; @@ -2076,7 +2895,7 @@ /* No comment provided by engineer. */ "Invalid response" = "Ungültige Reaktion"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "Ungültige Serveradresse!"; /* item status text */ @@ -2088,12 +2907,18 @@ /* group name */ "invitation to group %@" = "Einladung zur Gruppe %@"; +/* No comment provided by engineer. */ +"invite" = "Einladen"; + /* No comment provided by engineer. */ "Invite friends" = "Freunde einladen"; /* No comment provided by engineer. */ "Invite members" = "Mitglieder einladen"; +/* No comment provided by engineer. */ +"Invite to chat" = "Zum Chat einladen"; + /* No comment provided by engineer. */ "Invite to group" = "In Gruppe einladen"; @@ -2115,6 +2940,9 @@ /* No comment provided by engineer. */ "iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "Für die sichere Speicherung des Passworts nach dem Neustart der App und dem Wechsel des Passworts wird der iOS Schlüsselbund verwendet - dies erlaubt den Empfang von Push-Benachrichtigungen."; +/* No comment provided by engineer. */ +"IP address" = "IP-Adresse"; + /* No comment provided by engineer. */ "Irreversible message deletion" = "Unwiederbringliches löschen einer Nachricht"; @@ -2122,7 +2950,7 @@ "Irreversible message deletion is prohibited in this chat." = "In diesem Chat ist das unwiederbringliche Löschen von Nachrichten nicht erlaubt."; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "In dieser Gruppe ist das unwiederbringliche Löschen von Nachrichten nicht erlaubt."; +"Irreversible message deletion is prohibited." = "In dieser Gruppe ist das unwiederbringliche Löschen von Nachrichten nicht erlaubt."; /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "Er ermöglicht mehrere anonyme Verbindungen in einem einzigen Chat-Profil ohne Daten zwischen diesen zu teilen."; @@ -2133,6 +2961,9 @@ /* No comment provided by engineer. */ "It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Dies kann unter folgenden Umständen passieren:\n1. Die Nachrichten verfallen auf dem sendenden Client-System nach 2 Tagen oder auf dem Server nach 30 Tagen.\n2. Die Nachrichten-Entschlüsselung ist fehlgeschlagen, da von Ihnen oder Ihrem Kontakt ein altes Datenbank-Backup genutzt wurde.\n3. Die Verbindung wurde kompromittiert."; +/* No comment provided by engineer. */ +"It protects your IP address and connections." = "Ihre IP-Adresse und Verbindungen werden geschützt."; + /* No comment provided by engineer. */ "It seems like you are already connected via this link. If it is not the case, there was an error (%@)." = "Es sieht so aus, als ob Sie bereits über diesen Link verbunden sind. Wenn das nicht der Fall ist, gab es einen Fehler (%@)."; @@ -2145,7 +2976,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "Japanische Benutzeroberfläche"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "Beitreten"; /* No comment provided by engineer. */ @@ -2172,13 +3003,16 @@ /* No comment provided by engineer. */ "Joining group" = "Der Gruppe beitreten"; -/* No comment provided by engineer. */ +/* alert action */ "Keep" = "Behalten"; +/* No comment provided by engineer. */ +"Keep conversation" = "Chat-Inhalte beibehalten"; + /* No comment provided by engineer. */ "Keep the app open to use it from desktop" = "Die App muss geöffnet bleiben, um sie vom Desktop aus nutzen zu können"; -/* No comment provided by engineer. */ +/* alert title */ "Keep unused invitation?" = "Nicht genutzte Einladung behalten?"; /* No comment provided by engineer. */ @@ -2196,9 +3030,15 @@ /* No comment provided by engineer. */ "Learn more" = "Mehr erfahren"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "Verlassen"; +/* No comment provided by engineer. */ +"Leave chat" = "Chat verlassen"; + +/* No comment provided by engineer. */ +"Leave chat?" = "Chat verlassen?"; + /* No comment provided by engineer. */ "Leave group" = "Gruppe verlassen"; @@ -2226,6 +3066,15 @@ /* No comment provided by engineer. */ "Linked desktops" = "Verknüpfte Desktops"; +/* swipe action */ +"List" = "Liste"; + +/* No comment provided by engineer. */ +"List name and emoji should be different for all lists." = "Der Listenname und das Emoji sollen für alle Listen unterschiedlich sein."; + +/* No comment provided by engineer. */ +"List name..." = "Listenname..."; + /* No comment provided by engineer. */ "LIVE" = "LIVE"; @@ -2235,9 +3084,6 @@ /* No comment provided by engineer. */ "Live messages" = "Live Nachrichten"; -/* No comment provided by engineer. */ -"Local" = "Lokal"; - /* No comment provided by engineer. */ "Local name" = "Lokaler Name"; @@ -2250,24 +3096,15 @@ /* No comment provided by engineer. */ "Lock mode" = "Sperr-Modus"; -/* No comment provided by engineer. */ -"Make a private connection" = "Stellen Sie eine private Verbindung her"; - /* No comment provided by engineer. */ "Make one message disappear" = "Eine verschwindende Nachricht verfassen"; /* No comment provided by engineer. */ "Make profile private!" = "Privates Profil erzeugen!"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Stellen Sie sicher, dass die %@-Server-Adressen das richtige Format haben, zeilenweise getrennt und nicht doppelt vorhanden sind (%@)."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Stellen Sie sicher, dass die WebRTC ICE-Server Adressen das richtige Format haben, zeilenweise getrennt und nicht doppelt vorhanden sind."; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Viele Menschen haben gefragt: *Wie kann SimpleX Nachrichten zustellen, wenn es keine Benutzerkennungen gibt?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "Für Alle als gelöscht markieren"; @@ -2286,6 +3123,12 @@ /* No comment provided by engineer. */ "Max 30 seconds, received instantly." = "Max. 30 Sekunden, sofort erhalten."; +/* No comment provided by engineer. */ +"Media & file servers" = "Medien- und Datei-Server"; + +/* blur media */ +"Medium" = "Medium"; + /* member role */ "member" = "Mitglied"; @@ -2298,6 +3141,15 @@ /* rcv group event chat item */ "member connected" = "ist der Gruppe beigetreten"; +/* item status text */ +"Member inactive" = "Mitglied inaktiv"; + +/* chat feature */ +"Member reports" = "Mitglieder-Meldungen"; + +/* No comment provided by engineer. */ +"Member role will be changed to \"%@\". All chat members will be notified." = "Die Rolle des Mitglieds wird auf \"%@\" geändert. Alle Chat-Mitglieder werden darüber informiert."; + /* No comment provided by engineer. */ "Member role will be changed to \"%@\". All group members will be notified." = "Die Mitgliederrolle wird auf \"%@\" geändert. Alle Mitglieder der Gruppe werden benachrichtigt."; @@ -2305,7 +3157,43 @@ "Member role will be changed to \"%@\". The member will receive a new invitation." = "Die Mitgliederrolle wird auf \"%@\" geändert. Das Mitglied wird eine neue Einladung erhalten."; /* No comment provided by engineer. */ -"Member will be removed from group - this cannot be undone!" = "Das Mitglied wird aus der Gruppe entfernt - dies kann nicht rückgängig gemacht werden!"; +"Member will be removed from chat - this cannot be undone!" = "Das Mitglied wird aus dem Chat entfernt. Dies kann nicht rückgängig gemacht werden!"; + +/* No comment provided by engineer. */ +"Member will be removed from group - this cannot be undone!" = "Das Mitglied wird aus der Gruppe entfernt. Dies kann nicht rückgängig gemacht werden!"; + +/* No comment provided by engineer. */ +"Members can add message reactions." = "Gruppenmitglieder können eine Reaktion auf Nachrichten geben."; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "Gruppenmitglieder können gesendete Nachrichten unwiederbringlich löschen. (24 Stunden)"; + +/* No comment provided by engineer. */ +"Members can report messsages to moderators." = "Mitglieder können Nachrichten an Moderatoren melden."; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "Gruppenmitglieder können Direktnachrichten versenden."; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "Gruppenmitglieder können verschwindende Nachrichten versenden."; + +/* No comment provided by engineer. */ +"Members can send files and media." = "Gruppenmitglieder können Dateien und Medien versenden."; + +/* No comment provided by engineer. */ +"Members can send SimpleX links." = "Gruppenmitglieder können SimpleX-Links versenden."; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "Gruppenmitglieder können Sprachnachrichten versenden."; + +/* No comment provided by engineer. */ +"Mention members 👋" = "Erwähnung von Mitgliedern 👋"; + +/* No comment provided by engineer. */ +"Menus" = "Menüs"; + +/* No comment provided by engineer. */ +"message" = "Nachricht"; /* item status text */ "Message delivery error" = "Fehler bei der Nachrichtenzustellung"; @@ -2313,9 +3201,21 @@ /* No comment provided by engineer. */ "Message delivery receipts!" = "Empfangsbestätigungen für Nachrichten!"; +/* item status text */ +"Message delivery warning" = "Warnung bei der Nachrichtenzustellung"; + /* No comment provided by engineer. */ "Message draft" = "Nachrichtenentwurf"; +/* item status text */ +"Message forwarded" = "Nachricht weitergeleitet"; + +/* item status description */ +"Message may be delivered later if member becomes active." = "Die Nachricht kann später zugestellt werden, wenn das Mitglied aktiv wird."; + +/* No comment provided by engineer. */ +"Message queue info" = "Nachrichten-Warteschlangen-Information"; + /* chat feature */ "Message reactions" = "Reaktionen auf Nachrichten"; @@ -2323,14 +3223,35 @@ "Message reactions are prohibited in this chat." = "In diesem Chat sind Reaktionen auf Nachrichten nicht erlaubt."; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "In dieser Gruppe sind Reaktionen auf Nachrichten nicht erlaubt."; +"Message reactions are prohibited." = "In dieser Gruppe sind Reaktionen auf Nachrichten nicht erlaubt."; /* notification */ "message received" = "Nachricht empfangen"; +/* No comment provided by engineer. */ +"Message reception" = "Nachrichtenempfang"; + +/* No comment provided by engineer. */ +"Message servers" = "Nachrichten-Server"; + +/* No comment provided by engineer. */ +"Message shape" = "Nachrichten-Form"; + +/* No comment provided by engineer. */ +"Message source remains private." = "Die Nachrichtenquelle bleibt privat."; + +/* No comment provided by engineer. */ +"Message status" = "Nachrichten-Status"; + +/* copied message info */ +"Message status: %@" = "Nachrichten-Status: %@"; + /* No comment provided by engineer. */ "Message text" = "Nachrichtentext"; +/* No comment provided by engineer. */ +"Message too large" = "Die Nachricht ist zu lang"; + /* No comment provided by engineer. */ "Messages" = "Nachrichten"; @@ -2340,9 +3261,48 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "Die Nachrichten von %@ werden angezeigt!"; +/* alert message */ +"Messages in this chat will never be deleted." = "Nachrichten in diesem Chat werden nie gelöscht."; + +/* No comment provided by engineer. */ +"Messages received" = "Empfangene Nachrichten"; + +/* No comment provided by engineer. */ +"Messages sent" = "Gesendete Nachrichten"; + +/* alert message */ +"Messages were deleted after you selected them." = "Die Nachrichten wurden gelöscht, nachdem Sie sie ausgewählt hatten."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Nachrichten, Dateien und Anrufe sind durch **Ende-zu-Ende-Verschlüsselung** mit Perfect Forward Secrecy, Abstreitbarkeit und Wiederherstellung nach einer Kompromittierung geschützt."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Nachrichten, Dateien und Anrufe sind durch **Quantum-resistente E2E-Verschlüsselung** mit Perfect Forward Secrecy, Abstreitbarkeit und Wiederherstellung nach einer Kompromittierung geschützt."; + +/* No comment provided by engineer. */ +"Migrate device" = "Gerät migrieren"; + +/* No comment provided by engineer. */ +"Migrate from another device" = "Von einem anderen Gerät migrieren"; + +/* No comment provided by engineer. */ +"Migrate here" = "Hierher migrieren"; + +/* No comment provided by engineer. */ +"Migrate to another device" = "Auf ein anderes Gerät migrieren"; + +/* No comment provided by engineer. */ +"Migrate to another device via QR code." = "Daten können über einen QR-Code auf ein anderes Gerät migriert werden."; + +/* No comment provided by engineer. */ +"Migrating" = "Migrieren"; + /* No comment provided by engineer. */ "Migrating database archive…" = "Datenbank-Archiv wird migriert…"; +/* No comment provided by engineer. */ +"Migration complete" = "Migration abgeschlossen"; + /* No comment provided by engineer. */ "Migration error:" = "Fehler bei der Migration:"; @@ -2353,7 +3313,7 @@ "Migration is completed" = "Die Migration wurde abgeschlossen"; /* No comment provided by engineer. */ -"Migrations: %@" = "Migrationen: %@"; +"Migrations:" = "Migrationen:"; /* time unit */ "minutes" = "Minuten"; @@ -2376,63 +3336,99 @@ /* marked deleted chat item preview text */ "moderated by %@" = "Von %@ moderiert"; +/* member role */ +"moderator" = "Moderator"; + /* time unit */ "months" = "Monate"; +/* swipe action */ +"More" = "Mehr"; + /* No comment provided by engineer. */ "More improvements are coming soon!" = "Weitere Verbesserungen sind bald verfügbar!"; +/* No comment provided by engineer. */ +"More reliable network connection." = "Zuverlässigere Netzwerkverbindung."; + +/* No comment provided by engineer. */ +"More reliable notifications" = "Zuverlässigere Benachrichtigungen"; + /* item status description */ "Most likely this connection is deleted." = "Wahrscheinlich ist diese Verbindung gelöscht worden."; -/* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "Dieser Kontakt hat sehr wahrscheinlich die Verbindung mit Ihnen gelöscht."; - /* No comment provided by engineer. */ "Multiple chat profiles" = "Mehrere Chat-Profile"; -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "Stummschalten"; +/* notification label action */ +"Mute all" = "Alle stummschalten"; + /* No comment provided by engineer. */ "Muted when inactive!" = "Bei Inaktivität stummgeschaltet!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "Name"; /* No comment provided by engineer. */ "Network & servers" = "Netzwerk & Server"; +/* No comment provided by engineer. */ +"Network connection" = "Netzwerkverbindung"; + +/* No comment provided by engineer. */ +"Network decentralization" = "Dezentralisiertes Netzwerk"; + +/* snd error text */ +"Network issues - message expired after many attempts to send it." = "Netzwerk-Fehler - die Nachricht ist nach vielen Sende-Versuchen abgelaufen."; + +/* No comment provided by engineer. */ +"Network management" = "Netzwerk-Verwaltung"; + +/* No comment provided by engineer. */ +"Network operator" = "Netzwerk-Betreiber"; + /* No comment provided by engineer. */ "Network settings" = "Netzwerkeinstellungen"; /* No comment provided by engineer. */ "Network status" = "Netzwerkstatus"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "nie"; +/* token status text */ +"New" = "Neu"; + /* No comment provided by engineer. */ "New chat" = "Neuer Chat"; +/* No comment provided by engineer. */ +"New chat experience 🎉" = "Neue Chat-Erfahrung 🎉"; + /* notification */ "New contact request" = "Neue Kontaktanfrage"; /* notification */ "New contact:" = "Neuer Kontakt:"; -/* No comment provided by engineer. */ -"New database archive" = "Neues Datenbankarchiv"; - /* No comment provided by engineer. */ "New desktop app!" = "Neue Desktop-App!"; /* No comment provided by engineer. */ "New display name" = "Neuer Anzeigename"; +/* notification */ +"New events" = "Neue Ereignisse"; + /* No comment provided by engineer. */ "New in %@" = "Neu in %@"; +/* No comment provided by engineer. */ +"New media options" = "Neue Medien-Optionen"; + /* No comment provided by engineer. */ "New member role" = "Neue Mitgliedsrolle"; @@ -2448,6 +3444,15 @@ /* No comment provided by engineer. */ "New passphrase…" = "Neues Passwort…"; +/* No comment provided by engineer. */ +"New server" = "Neuer Server"; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used every time you start the app." = "Jedes Mal wenn Sie die App starten, werden neue SOCKS-Anmeldeinformationen genutzt"; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used for each server." = "Für jeden Server werden neue SOCKS-Anmeldeinformationen genutzt"; + /* pref value */ "no" = "Nein"; @@ -2457,6 +3462,15 @@ /* Authentication unavailable */ "No app password" = "Kein App-Passwort"; +/* No comment provided by engineer. */ +"No chats" = "Keine Chats"; + +/* No comment provided by engineer. */ +"No chats found" = "Keine Chats gefunden"; + +/* No comment provided by engineer. */ +"No chats in list %@" = "Keine Chats in der Liste %@"; + /* No comment provided by engineer. */ "No contacts selected" = "Keine Kontakte ausgewählt"; @@ -2469,6 +3483,9 @@ /* No comment provided by engineer. */ "No device token!" = "Kein Geräte-Token!"; +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Bisher keine direkte Verbindung. Nachricht wird von einem Admin weitergeleitet."; + /* No comment provided by engineer. */ "no e2e encryption" = "Keine E2E-Verschlüsselung"; @@ -2481,24 +3498,87 @@ /* No comment provided by engineer. */ "No history" = "Kein Nachrichtenverlauf"; +/* No comment provided by engineer. */ +"No info, try to reload" = "Keine Information - es wird versucht neu zu laden"; + +/* servers error */ +"No media & file servers." = "Keine Medien- und Dateiserver."; + +/* No comment provided by engineer. */ +"No message" = "Keine Nachricht"; + +/* servers error */ +"No message servers." = "Keine Nachrichten-Server."; + +/* No comment provided by engineer. */ +"No network connection" = "Keine Netzwerkverbindung"; + +/* No comment provided by engineer. */ +"No permission to record speech" = "Keine Genehmigung für Sprach-Aufnahmen"; + +/* No comment provided by engineer. */ +"No permission to record video" = "Keine Genehmigung für Video-Aufnahmen"; + /* No comment provided by engineer. */ "No permission to record voice message" = "Keine Berechtigung für das Aufnehmen von Sprachnachrichten"; /* No comment provided by engineer. */ -"No received or sent files" = "Keine empfangenen oder gesendeten Dateien"; +"No push server" = "Lokal"; + +/* No comment provided by engineer. */ +"No received or sent files" = "Keine herunter- oder hochgeladenen Dateien"; + +/* servers error */ +"No servers for private message routing." = "Keine Server für privates Nachrichten-Routing."; + +/* servers error */ +"No servers to receive files." = "Keine Server für das Herunterladen von Dateien."; + +/* servers error */ +"No servers to receive messages." = "Keine Server für den Empfang von Nachrichten."; + +/* servers error */ +"No servers to send files." = "Keine Server für das Versenden von Dateien."; /* copied message info in history */ "no text" = "Kein Text"; +/* alert title */ +"No token!" = "Kein Token!"; + +/* No comment provided by engineer. */ +"No unread chats" = "Keine ungelesenen Chats"; + +/* No comment provided by engineer. */ +"No user identifiers." = "Keine Benutzerkennungen."; + /* No comment provided by engineer. */ "Not compatible!" = "Nicht kompatibel!"; +/* No comment provided by engineer. */ +"Notes" = "Anmerkungen"; + +/* No comment provided by engineer. */ +"Nothing selected" = "Nichts ausgewählt"; + +/* alert title */ +"Nothing to forward!" = "Es gibt nichts zum Weiterleiten!"; + /* No comment provided by engineer. */ "Notifications" = "Benachrichtigungen"; /* No comment provided by engineer. */ "Notifications are disabled!" = "Benachrichtigungen sind deaktiviert!"; +/* alert title */ +"Notifications error" = "Benachrichtigungs-Fehler"; + +/* No comment provided by engineer. */ +"Notifications privacy" = "Datenschutz für Benachrichtigungen"; + +/* alert title */ +"Notifications status" = "Benachrichtigungs-Status"; + /* No comment provided by engineer. */ "Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Administratoren können nun\n- Nachrichten von Gruppenmitgliedern löschen\n- Gruppenmitglieder deaktivieren (\"Beobachter\"-Rolle)"; @@ -2506,11 +3586,11 @@ "observer" = "Beobachter"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "Aus"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Aus"; /* feature offered item */ @@ -2519,7 +3599,7 @@ /* feature offered item */ "offered %@: %@" = "angeboten %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ @@ -2528,9 +3608,6 @@ /* No comment provided by engineer. */ "Old database" = "Alte Datenbank"; -/* No comment provided by engineer. */ -"Old database archive" = "Altes Datenbankarchiv"; - /* group pref value */ "on" = "Ein"; @@ -2538,16 +3615,22 @@ "One-time invitation link" = "Einmal-Einladungslink"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "Für die Verbindung werden Onion-Hosts benötigt. Dies erfordert die Aktivierung eines VPNs."; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "Für diese Verbindung werden Onion-Hosts benötigt.\nDies erfordert die Aktivierung eines VPNs."; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "Onion-Hosts werden verwendet, sobald sie verfügbar sind. Dies erfordert die Aktivierung eines VPNs."; +"Onion hosts will be used when available.\nRequires compatible VPN." = "Wenn Onion-Hosts verfügbar sind, werden sie verwendet.\nDies erfordert die Aktivierung eines VPNs."; /* No comment provided by engineer. */ "Onion hosts will not be used." = "Onion-Hosts werden nicht verwendet."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Nur die Endgeräte speichern die Benutzerprofile, Kontakte, Gruppen und Nachrichten, welche über eine **2-Schichten Ende-zu-Ende-Verschlüsselung** gesendet werden."; +"Only chat owners can change preferences." = "Nur Chat-Eigentümer können die Präferenzen ändern."; + +/* No comment provided by engineer. */ +"Only client devices store user profiles, contacts, groups, and messages." = "Nur die Endgeräte speichern die Benutzerprofile, Kontakte, Gruppen und Nachrichten, welche über eine **2-Schichten Ende-zu-Ende-Verschlüsselung** gesendet werden."; + +/* No comment provided by engineer. */ +"Only delete conversation" = "Nur die Chat-Inhalte löschen"; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Gruppen-Präferenzen können nur von Gruppen-Eigentümern geändert werden."; @@ -2558,6 +3641,12 @@ /* No comment provided by engineer. */ "Only group owners can enable voice messages." = "Sprachnachrichten können nur von Gruppen-Eigentümern aktiviert werden."; +/* No comment provided by engineer. */ +"Only sender and moderators see it" = "Nur Absender und Moderatoren sehen es"; + +/* No comment provided by engineer. */ +"Only you and moderators see it" = "Nur Sie und Moderatoren sehen es"; + /* No comment provided by engineer. */ "Only you can add message reactions." = "Nur Sie können Reaktionen auf Nachrichten geben."; @@ -2588,39 +3677,78 @@ /* No comment provided by engineer. */ "Only your contact can send voice messages." = "Nur Ihr Kontakt kann Sprachnachrichten versenden."; -/* No comment provided by engineer. */ +/* alert action */ "Open" = "Öffnen"; +/* No comment provided by engineer. */ +"Open changes" = "Änderungen öffnen"; + /* No comment provided by engineer. */ "Open chat" = "Chat öffnen"; /* authentication reason */ "Open chat console" = "Chat-Konsole öffnen"; +/* No comment provided by engineer. */ +"Open conditions" = "Nutzungsbedingungen öffnen"; + /* No comment provided by engineer. */ "Open group" = "Gruppe öffnen"; +/* authentication reason */ +"Open migration to another device" = "Migration auf ein anderes Gerät öffnen"; + /* No comment provided by engineer. */ "Open Settings" = "Geräte-Einstellungen öffnen"; -/* authentication reason */ -"Open user profiles" = "Benutzerprofile öffnen"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "Open-Source-Protokoll und -Code – Jede Person kann ihre eigenen Server aufsetzen und nutzen."; - /* No comment provided by engineer. */ "Opening app…" = "App wird geöffnet…"; +/* No comment provided by engineer. */ +"Operator" = "Betreiber"; + +/* alert title */ +"Operator server" = "Betreiber-Server"; + +/* No comment provided by engineer. */ +"Or import archive file" = "Oder importieren Sie eine Archiv-Datei"; + +/* No comment provided by engineer. */ +"Or paste archive link" = "Oder fügen Sie den Archiv-Link ein"; + /* No comment provided by engineer. */ "Or scan QR code" = "Oder den QR-Code scannen"; +/* No comment provided by engineer. */ +"Or securely share this file link" = "Oder teilen Sie diesen Datei-Link sicher"; + /* No comment provided by engineer. */ "Or show this code" = "Oder diesen QR-Code anzeigen"; +/* No comment provided by engineer. */ +"Or to share privately" = "Oder zum privaten Teilen"; + +/* No comment provided by engineer. */ +"Organize chats into lists" = "Chats in Listen verwalten"; + +/* No comment provided by engineer. */ +"other" = "Andere"; + +/* No comment provided by engineer. */ +"Other" = "Andere"; + +/* No comment provided by engineer. */ +"other errors" = "Andere Fehler"; + +/* alert message */ +"Other file errors:\n%@" = "Andere(r) Datei-Fehler:\n%@"; + /* member role */ "owner" = "Eigentümer"; +/* feature role */ +"owners" = "Eigentümer"; + /* No comment provided by engineer. */ "Passcode" = "Zugangscode"; @@ -2636,6 +3764,9 @@ /* No comment provided by engineer. */ "Passcode set!" = "Zugangscode eingestellt!"; +/* No comment provided by engineer. */ +"Password" = "Passwort"; + /* No comment provided by engineer. */ "Password to show" = "Passwort anzeigen"; @@ -2658,23 +3789,41 @@ "peer-to-peer" = "Peer-to-Peer"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "Verbindungen mit Kontakten sind nur über Links möglich, die Sie oder Ihre Kontakte untereinander teilen."; +"pending" = "ausstehend"; /* No comment provided by engineer. */ -"Periodically" = "Periodisch"; +"Pending" = "Ausstehend"; + +/* No comment provided by engineer. */ +"pending approval" = "ausstehende Genehmigung"; + +/* No comment provided by engineer. */ +"Periodic" = "Periodisch"; /* message decrypt error item */ "Permanent decryption error" = "Entschlüsselungsfehler"; +/* No comment provided by engineer. */ +"Picture-in-picture calls" = "Bild-in-Bild-Anrufe"; + /* No comment provided by engineer. */ "PING count" = "PING-Zähler"; /* No comment provided by engineer. */ "PING interval" = "PING-Intervall"; +/* No comment provided by engineer. */ +"Play from the chat list." = "Direkt aus der Chat-Liste abspielen."; + +/* No comment provided by engineer. */ +"Please ask your contact to enable calls." = "Bitten Sie Ihren Kontakt darum, Anrufe zu aktivieren."; + /* No comment provided by engineer. */ "Please ask your contact to enable sending voice messages." = "Bitten Sie Ihren Kontakt darum, das Senden von Sprachnachrichten zu aktivieren."; +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Bitte überprüfen Sie, ob sich das Mobiltelefon und die Desktop-App im gleichen lokalen Netzwerk befinden, und die Desktop-Firewall die Verbindung erlaubt.\nBitte teilen Sie weitere mögliche Probleme den Entwicklern mit."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Überprüfen Sie bitte, ob Sie den richtigen Link genutzt haben oder bitten Sie Ihren Kontakt nochmal darum, Ihnen einen Link zuzusenden."; @@ -2684,6 +3833,9 @@ /* No comment provided by engineer. */ "Please check yours and your contact preferences." = "Bitte überprüfen sie sowohl Ihre, als auch die Präferenzen Ihres Kontakts."; +/* No comment provided by engineer. */ +"Please confirm that network settings are correct for this device." = "Bitte bestätigen Sie, dass die Netzwerkeinstellungen auf diesem Gerät richtig sind."; + /* No comment provided by engineer. */ "Please contact developers.\nError: %@" = "Bitte nehmen Sie Kontakt mit den Entwicklern auf.\nFehler: %@"; @@ -2711,9 +3863,21 @@ /* No comment provided by engineer. */ "Please store passphrase securely, you will NOT be able to change it if you lose it." = "Bitte bewahren Sie das Passwort sicher auf, Sie können es NICHT mehr ändern, wenn Sie es vergessen haben oder verlieren."; +/* token info */ +"Please try to disable and re-enable notfications." = "Bitte versuchen Sie, die Benachrichtigungen zu deaktivieren und wieder zu aktivieren."; + +/* token info */ +"Please wait for token activation to complete." = "Bitte warten Sie, bis die Token-Aktivierung abgeschlossen ist."; + +/* token info */ +"Please wait for token to be registered." = "Bitte warten Sie auf die Registrierung des Tokens."; + /* No comment provided by engineer. */ "Polish interface" = "Polnische Bedienoberfläche"; +/* No comment provided by engineer. */ +"Port" = "Port"; + /* server test error */ "Possibly, certificate fingerprint in server address is incorrect" = "Der Fingerabdruck des Zertifikats in der Serveradresse ist wahrscheinlich ungültig"; @@ -2721,26 +3885,53 @@ "Preserve the last message draft, with attachments." = "Den letzten Nachrichtenentwurf, auch mit seinen Anhängen, aufbewahren."; /* No comment provided by engineer. */ -"Preset server" = "Voreingestellter Server"; +"Preset server address" = "Voreingestellte Serveradresse"; /* No comment provided by engineer. */ -"Preset server address" = "Voreingestellte Serveradresse"; +"Preset servers" = "Voreingestellte Server"; /* No comment provided by engineer. */ "Preview" = "Vorschau"; +/* No comment provided by engineer. */ +"Previously connected servers" = "Bisher verbundene Server"; + /* No comment provided by engineer. */ "Privacy & security" = "Datenschutz & Sicherheit"; +/* No comment provided by engineer. */ +"Privacy for your customers." = "Schutz der Privatsphäre Ihrer Kunden."; + +/* No comment provided by engineer. */ +"Privacy policy and conditions of use." = "Datenschutz- und Nutzungsbedingungen."; + /* No comment provided by engineer. */ "Privacy redefined" = "Datenschutz neu definiert"; +/* No comment provided by engineer. */ +"Private chats, groups and your contacts are not accessible to server operators." = "Private Chats, Gruppen und Ihre Kontakte sind für Server-Betreiber nicht zugänglich."; + /* No comment provided by engineer. */ "Private filenames" = "Neutrale Dateinamen"; +/* No comment provided by engineer. */ +"Private media file names." = "Medien mit anonymisierten Dateinamen."; + +/* No comment provided by engineer. */ +"Private message routing" = "Privates Nachrichten-Routing"; + +/* No comment provided by engineer. */ +"Private message routing 🚀" = "Privates Nachrichten-Routing 🚀"; + /* name of notes to self */ "Private notes" = "Private Notizen"; +/* No comment provided by engineer. */ +"Private routing" = "Privates Routing"; + +/* No comment provided by engineer. */ +"Private routing error" = "Fehler beim privaten Routing"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profil und Serververbindungen"; @@ -2748,15 +3939,15 @@ "Profile image" = "Profilbild"; /* No comment provided by engineer. */ -"Profile name" = "Profilname"; - -/* No comment provided by engineer. */ -"Profile name:" = "Profilname:"; +"Profile images" = "Profil-Bilder"; /* No comment provided by engineer. */ "Profile password" = "Passwort für Profil"; /* No comment provided by engineer. */ +"Profile theme" = "Profil-Design"; + +/* alert message */ "Profile update will be sent to your contacts." = "Profil-Aktualisierung wird an Ihre Kontakte gesendet."; /* No comment provided by engineer. */ @@ -2771,50 +3962,83 @@ /* No comment provided by engineer. */ "Prohibit messages reactions." = "Reaktionen auf Nachrichten nicht erlauben."; +/* No comment provided by engineer. */ +"Prohibit reporting messages to moderators." = "Melden von Nachrichten an Moderatoren nicht erlauben."; + /* No comment provided by engineer. */ "Prohibit sending direct messages to members." = "Das Senden von Direktnachrichten an Gruppenmitglieder nicht erlauben."; /* No comment provided by engineer. */ -"Prohibit sending disappearing messages." = "Das Senden von verschwindenden Nachrichten verbieten."; +"Prohibit sending disappearing messages." = "Das Senden von verschwindenden Nachrichten nicht erlauben."; /* No comment provided by engineer. */ "Prohibit sending files and media." = "Das Senden von Dateien und Medien nicht erlauben."; +/* No comment provided by engineer. */ +"Prohibit sending SimpleX links." = "Das Senden von SimpleX-Links nicht erlauben."; + /* No comment provided by engineer. */ "Prohibit sending voice messages." = "Das Senden von Sprachnachrichten nicht erlauben."; /* No comment provided by engineer. */ "Protect app screen" = "App-Bildschirm schützen"; +/* No comment provided by engineer. */ +"Protect IP address" = "IP-Adresse schützen"; + /* No comment provided by engineer. */ "Protect your chat profiles with a password!" = "Ihre Chat-Profile mit einem Passwort schützen!"; +/* No comment provided by engineer. */ +"Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." = "Schützen Sie Ihre IP-Adresse vor den Nachrichten-Relais, die Ihre Kontakte ausgewählt haben.\nAktivieren Sie es in den *Netzwerk & Server* Einstellungen."; + /* No comment provided by engineer. */ "Protocol timeout" = "Protokollzeitüberschreitung"; /* No comment provided by engineer. */ "Protocol timeout per KB" = "Protokollzeitüberschreitung pro kB"; +/* No comment provided by engineer. */ +"Proxied" = "Proxied"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Proxy-Server"; + +/* No comment provided by engineer. */ +"Proxy requires password" = "Der Proxy benötigt ein Passwort"; + /* No comment provided by engineer. */ "Push notifications" = "Push-Benachrichtigungen"; +/* No comment provided by engineer. */ +"Push server" = "Push-Server"; + +/* chat item text */ +"quantum resistant e2e encryption" = "Quantum-resistente E2E-Verschlüsselung"; + +/* No comment provided by engineer. */ +"Quantum resistant encryption" = "Quantum-resistente Verschlüsselung"; + /* No comment provided by engineer. */ "Rate the app" = "Bewerten Sie die App"; +/* No comment provided by engineer. */ +"Reachable chat toolbar" = "Chat-Symbolleiste unten"; + /* chat item menu */ "React…" = "Reagiere…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "Gelesen"; /* No comment provided by engineer. */ "Read more" = "Mehr erfahren"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Mehr dazu in der [Benutzeranleitung](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address) lesen."; +"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Lesen Sie mehr dazu im [Benutzerhandbuch](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Lesen Sie mehr dazu im [Benutzerhandbuch](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "Mehr dazu in der [Benutzeranleitung](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses) lesen."; /* No comment provided by engineer. */ "Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Mehr dazu in der [Benutzeranleitung](https://simplex.chat/docs/guide/readme.html#connect-to-friends) lesen."; @@ -2823,10 +4047,10 @@ "Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Erfahren Sie in unserem [GitHub-Repository](https://github.com/simplex-chat/simplex-chat#readme) mehr dazu."; /* No comment provided by engineer. */ -"Read more in our GitHub repository." = "Erfahren Sie in unserem GitHub-Repository mehr dazu."; +"Receipts are disabled" = "Bestätigungen sind deaktiviert"; /* No comment provided by engineer. */ -"Receipts are disabled" = "Bestätigungen sind deaktiviert"; +"Receive errors" = "Fehler beim Empfang"; /* No comment provided by engineer. */ "received answer…" = "Antwort erhalten…"; @@ -2846,11 +4070,20 @@ /* message info title */ "Received message" = "Empfangene Nachricht"; +/* No comment provided by engineer. */ +"Received messages" = "Empfangene Nachrichten"; + +/* No comment provided by engineer. */ +"Received reply" = "Empfangene Antwort"; + +/* No comment provided by engineer. */ +"Received total" = "Summe aller empfangenen Nachrichten"; + /* No comment provided by engineer. */ "Receiving address will be changed to a different server. Address change will complete after sender comes online." = "Die Empfängeradresse wird auf einen anderen Server geändert. Der Adresswechsel wird abgeschlossen, wenn der Absender wieder online ist."; /* No comment provided by engineer. */ -"Receiving file will be stopped." = "Der Empfang der Datei wird beendet."; +"Receiving file will be stopped." = "Das Herunterladen der Datei wird beendet."; /* No comment provided by engineer. */ "Receiving via" = "Empfangen über"; @@ -2858,12 +4091,30 @@ /* No comment provided by engineer. */ "Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)." = "Aktueller Nachrichtenverlauf und verbesserter [Gruppenverzeichnis-Bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)."; +/* No comment provided by engineer. */ +"Recipient(s) can't see who this message is from." = "Empfänger können nicht sehen, von wem die Nachricht stammt."; + /* No comment provided by engineer. */ "Recipients see updates as you type them." = "Die Empfänger sehen Nachrichtenaktualisierungen, während Sie sie eingeben."; +/* No comment provided by engineer. */ +"Reconnect" = "Neu verbinden"; + /* No comment provided by engineer. */ "Reconnect all connected servers to force message delivery. It uses additional traffic." = "Alle verbundenen Server werden neu verbunden, um die Zustellung der Nachricht zu erzwingen. Dies verursacht zusätzlichen Datenverkehr."; +/* No comment provided by engineer. */ +"Reconnect all servers" = "Alle Server neu verbinden"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "Alle Server neu verbinden?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "Um die Auslieferung von Nachrichten zu erzwingen, wird der Server neu verbunden. Dafür wird weiterer Datenverkehr benötigt."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "Server neu verbinden?"; + /* No comment provided by engineer. */ "Reconnect servers?" = "Die Server neu verbinden?"; @@ -2876,7 +4127,17 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "Reduzierter Batterieverbrauch"; -/* reject incoming call via notification */ +/* No comment provided by engineer. */ +"Register" = "Registrieren"; + +/* token info */ +"Register notification token?" = "Benachrichtigungs-Token registrieren?"; + +/* token status text */ +"Registered" = "Registriert"; + +/* reject incoming call via notification +swipe action */ "Reject" = "Ablehnen"; /* No comment provided by engineer. */ @@ -2885,6 +4146,9 @@ /* No comment provided by engineer. */ "Reject contact request" = "Kontaktanfrage ablehnen"; +/* No comment provided by engineer. */ +"rejected" = "abgelehnt"; + /* call status */ "rejected call" = "Abgelehnter Anruf"; @@ -2897,6 +4161,12 @@ /* No comment provided by engineer. */ "Remove" = "Entfernen"; +/* No comment provided by engineer. */ +"Remove archive?" = "Archiv entfernen?"; + +/* No comment provided by engineer. */ +"Remove image" = "Bild entfernen"; + /* No comment provided by engineer. */ "Remove member" = "Mitglied entfernen"; @@ -2933,24 +4203,81 @@ /* No comment provided by engineer. */ "Repeat connection request?" = "Verbindungsanfrage wiederholen?"; +/* No comment provided by engineer. */ +"Repeat download" = "Herunterladen wiederholen"; + +/* No comment provided by engineer. */ +"Repeat import" = "Import wiederholen"; + /* No comment provided by engineer. */ "Repeat join request?" = "Verbindungsanfrage wiederholen?"; +/* No comment provided by engineer. */ +"Repeat upload" = "Hochladen wiederholen"; + /* chat item action */ "Reply" = "Antwort"; +/* chat item action */ +"Report" = "Melden"; + +/* report reason */ +"Report content: only group moderators will see it." = "Inhalt melden: Nur Gruppenmoderatoren werden es sehen."; + +/* report reason */ +"Report member profile: only group moderators will see it." = "Mitgliederprofil melden: Nur Gruppenmoderatoren werden es sehen."; + +/* report reason */ +"Report other: only group moderators will see it." = "Anderes melden: Nur Gruppenmoderatoren werden es sehen."; + +/* No comment provided by engineer. */ +"Report reason?" = "Grund der Meldung?"; + +/* report reason */ +"Report spam: only group moderators will see it." = "Spam melden: Nur Gruppenmoderatoren werden es sehen."; + +/* report reason */ +"Report violation: only group moderators will see it." = "Verstoß melden: Nur Gruppenmoderatoren werden es sehen."; + +/* report in notification */ +"Report: %@" = "Meldung: %@"; + +/* No comment provided by engineer. */ +"Reporting messages to moderators is prohibited." = "Melden von Nachrichten an Moderatoren ist nicht erlaubt."; + +/* No comment provided by engineer. */ +"Reports" = "Meldungen"; + +/* chat list item title */ +"requested to connect" = "Zur Verbindung aufgefordert"; + /* No comment provided by engineer. */ "Required" = "Erforderlich"; /* No comment provided by engineer. */ "Reset" = "Zurücksetzen"; +/* No comment provided by engineer. */ +"Reset all hints" = "Alle Hinweise zurücksetzen"; + +/* No comment provided by engineer. */ +"Reset all statistics" = "Alle Statistiken zurücksetzen"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "Alle Statistiken zurücksetzen?"; + /* No comment provided by engineer. */ "Reset colors" = "Farben zurücksetzen"; +/* No comment provided by engineer. */ +"Reset to app theme" = "Auf das App-Design zurücksetzen"; + /* No comment provided by engineer. */ "Reset to defaults" = "Auf Voreinstellungen zurücksetzen"; +/* No comment provided by engineer. */ +"Reset to user theme" = "Auf das Benutzer-spezifische Design zurücksetzen"; + /* No comment provided by engineer. */ "Restart the app to create a new chat profile" = "Um ein neues Chat-Profil zu erstellen, starten Sie die App neu"; @@ -2976,7 +4303,7 @@ "Reveal" = "Aufdecken"; /* No comment provided by engineer. */ -"Revert" = "Zurückkehren"; +"Review conditions" = "Nutzungsbedingungen einsehen"; /* No comment provided by engineer. */ "Revoke" = "Widerrufen"; @@ -2993,37 +4320,44 @@ /* No comment provided by engineer. */ "Run chat" = "Chat starten"; -/* chat item action */ +/* No comment provided by engineer. */ +"Safely receive files" = "Dateien sicher herunterladen"; + +/* No comment provided by engineer. */ +"Safer groups" = "Sicherere Gruppen"; + +/* alert button +chat item action */ "Save" = "Speichern"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Speichern (und Kontakte benachrichtigen)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "Speichern und Kontakt benachrichtigen"; /* No comment provided by engineer. */ "Save and notify group members" = "Speichern und Gruppenmitglieder benachrichtigen"; +/* No comment provided by engineer. */ +"Save and reconnect" = "Speichern und neu verbinden"; + /* No comment provided by engineer. */ "Save and update group profile" = "Gruppen-Profil sichern und aktualisieren"; -/* No comment provided by engineer. */ -"Save archive" = "Archiv speichern"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Einstellungen von \"Automatisch akzeptieren\" speichern"; - /* No comment provided by engineer. */ "Save group profile" = "Gruppenprofil speichern"; +/* No comment provided by engineer. */ +"Save list" = "Liste speichern"; + /* No comment provided by engineer. */ "Save passphrase and open chat" = "Passwort speichern und Chat öffnen"; /* No comment provided by engineer. */ "Save passphrase in Keychain" = "Passwort im Schlüsselbund speichern"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "Präferenzen speichern?"; /* No comment provided by engineer. */ @@ -3032,14 +4366,26 @@ /* No comment provided by engineer. */ "Save servers" = "Alle Server speichern"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "Alle Server speichern?"; /* No comment provided by engineer. */ -"Save settings?" = "Einstellungen speichern?"; +"Save welcome message?" = "Begrüßungsmeldung speichern?"; + +/* alert title */ +"Save your profile?" = "Ihr Profil speichern?"; /* No comment provided by engineer. */ -"Save welcome message?" = "Begrüßungsmeldung speichern?"; +"saved" = "abgespeichert"; + +/* No comment provided by engineer. */ +"Saved" = "Abgespeichert"; + +/* No comment provided by engineer. */ +"Saved from" = "Abgespeichert von"; + +/* No comment provided by engineer. */ +"saved from %@" = "abgespeichert von %@"; /* message info title */ "Saved message" = "Gespeicherte Nachricht"; @@ -3047,6 +4393,15 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Gespeicherte WebRTC ICE-Server werden entfernt"; +/* No comment provided by engineer. */ +"Saving %lld messages" = "Es wird/werden %lld Nachricht(en) gesichert"; + +/* No comment provided by engineer. */ +"Scale" = "Skalieren"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Link scannen / einfügen"; + /* No comment provided by engineer. */ "Scan code" = "Code scannen"; @@ -3062,6 +4417,9 @@ /* No comment provided by engineer. */ "Scan server QR code" = "Scannen Sie den QR-Code des Servers"; +/* No comment provided by engineer. */ +"search" = "Suchen"; + /* No comment provided by engineer. */ "Search" = "Suche"; @@ -3069,11 +4427,14 @@ "Search bar accepts invitation links." = "In der Suchleiste werden nun auch Einladungslinks akzeptiert."; /* No comment provided by engineer. */ -"Search or paste SimpleX link" = "Suchen oder fügen Sie den SimpleX-Link ein"; +"Search or paste SimpleX link" = "Suchen oder SimpleX-Link einfügen"; /* network option */ "sec" = "sek"; +/* No comment provided by engineer. */ +"Secondary" = "Zweite Farbe"; + /* time unit */ "seconds" = "Sekunden"; @@ -3083,6 +4444,9 @@ /* server test step */ "Secure queue" = "Sichere Warteschlange"; +/* No comment provided by engineer. */ +"Secured" = "Abgesichert"; + /* No comment provided by engineer. */ "Security assessment" = "Sicherheits-Gutachten"; @@ -3092,9 +4456,18 @@ /* chat item text */ "security code changed" = "Sicherheitscode wurde geändert"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "Auswählen"; +/* No comment provided by engineer. */ +"Select chat profile" = "Chat-Profil auswählen"; + +/* No comment provided by engineer. */ +"Selected %lld" = "%lld ausgewählt"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Diese Nachricht ist wegen der gewählten Chat-Einstellungen nicht erlaubt."; + /* No comment provided by engineer. */ "Self-destruct" = "Selbstzerstörung"; @@ -3119,26 +4492,35 @@ /* No comment provided by engineer. */ "send direct message" = "Direktnachricht senden"; -/* No comment provided by engineer. */ -"Send direct message" = "Direktnachricht senden"; - /* No comment provided by engineer. */ "Send direct message to connect" = "Eine Direktnachricht zum Verbinden senden"; /* No comment provided by engineer. */ "Send disappearing message" = "Verschwindende Nachricht senden"; +/* No comment provided by engineer. */ +"Send errors" = "Fehler beim Senden"; + /* No comment provided by engineer. */ "Send link previews" = "Link-Vorschau senden"; /* No comment provided by engineer. */ "Send live message" = "Live Nachricht senden"; +/* No comment provided by engineer. */ +"Send message to enable calls." = "Nachricht senden, um Anrufe zu aktivieren."; + +/* No comment provided by engineer. */ +"Send messages directly when IP address is protected and your or destination server does not support private routing." = "Nachrichten werden direkt versendet, wenn die IP-Adresse geschützt ist, und Ihr oder der Zielserver kein privates Routing unterstützt."; + +/* No comment provided by engineer. */ +"Send messages directly when your or destination server does not support private routing." = "Nachrichten werden direkt versendet, wenn Ihr oder der Zielserver kein privates Routing unterstützt."; + /* No comment provided by engineer. */ "Send notifications" = "Benachrichtigungen senden"; /* No comment provided by engineer. */ -"Send notifications:" = "Benachrichtigungen senden:"; +"Send private reports" = "Private Meldungen senden"; /* No comment provided by engineer. */ "Send questions and ideas" = "Senden Sie Fragen und Ideen"; @@ -3152,7 +4534,7 @@ /* No comment provided by engineer. */ "Send up to 100 last messages to new members." = "Bis zu 100 der letzten Nachrichten an neue Gruppenmitglieder senden."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Der Absender hat die Dateiübertragung abgebrochen."; /* No comment provided by engineer. */ @@ -3188,15 +4570,57 @@ /* copied message info */ "Sent at: %@" = "Gesendet um: %@"; +/* No comment provided by engineer. */ +"Sent directly" = "Direkt gesendet"; + /* notification */ "Sent file event" = "Datei-Ereignis wurde gesendet"; /* message info title */ "Sent message" = "Gesendete Nachricht"; +/* No comment provided by engineer. */ +"Sent messages" = "Gesendete Nachrichten"; + /* No comment provided by engineer. */ "Sent messages will be deleted after set time." = "Gesendete Nachrichten werden nach der eingestellten Zeit gelöscht."; +/* No comment provided by engineer. */ +"Sent reply" = "Gesendete Antwort"; + +/* No comment provided by engineer. */ +"Sent total" = "Summe aller gesendeten Nachrichten"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Über einen Proxy gesendet"; + +/* No comment provided by engineer. */ +"Server" = "Server"; + +/* alert message */ +"Server added to operator %@." = "Der Server wurde dem Betreiber %@ hinzugefügt."; + +/* No comment provided by engineer. */ +"Server address" = "Server-Adresse"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "Die Server-Adresse ist nicht mit den Netzwerkeinstellungen kompatibel: %@."; + +/* srv error text. */ +"Server address is incompatible with network settings." = "Die Server-Adresse ist nicht mit den Netzwerkeinstellungen kompatibel."; + +/* alert title */ +"Server operator changed." = "Der Server-Betreiber wurde geändert."; + +/* No comment provided by engineer. */ +"Server operators" = "Server-Betreiber"; + +/* alert title */ +"Server protocol changed." = "Das Server-Protokoll wurde geändert."; + +/* queue info */ +"server queue info: %@\n\nlast received msg: %@" = "Server-Warteschlangen-Information: %1$@\n\nZuletzt empfangene Nachricht: %2$@"; + /* server test error */ "Server requires authorization to create queues, check password" = "Um Warteschlangen zu erzeugen benötigt der Server eine Authentifizierung. Bitte überprüfen Sie das Passwort"; @@ -3206,24 +4630,48 @@ /* No comment provided by engineer. */ "Server test failed!" = "Server Test ist fehlgeschlagen!"; +/* No comment provided by engineer. */ +"Server type" = "Server-Typ"; + +/* srv error text */ +"Server version is incompatible with network settings." = "Die Server-Version ist nicht mit den Netzwerkeinstellungen kompatibel."; + +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "Die Server-Version ist nicht mit Ihrer App kompatibel: %@."; + /* No comment provided by engineer. */ "Servers" = "Server"; +/* No comment provided by engineer. */ +"Servers info" = "Server-Informationen"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "Die Serverstatistiken werden zurückgesetzt. Dies kann nicht rückgängig gemacht werden!"; + /* No comment provided by engineer. */ "Session code" = "Sitzungscode"; /* No comment provided by engineer. */ "Set 1 day" = "Einen Tag festlegen"; +/* No comment provided by engineer. */ +"Set chat name…" = "Chat-Name festlegen…"; + /* No comment provided by engineer. */ "Set contact name…" = "Kontaktname festlegen…"; +/* No comment provided by engineer. */ +"Set default theme" = "Default-Design einstellen"; + /* No comment provided by engineer. */ "Set group preferences" = "Gruppen-Präferenzen einstellen"; /* No comment provided by engineer. */ "Set it instead of system authentication." = "Anstelle der System-Authentifizierung festlegen."; +/* No comment provided by engineer. */ +"Set message expiration in chats." = "Verfallsdatum von Nachrichten in Chats festlegen."; + /* profile update event chat item */ "set new contact address" = "Es wurde eine neue Kontaktadresse festgelegt"; @@ -3233,6 +4681,9 @@ /* No comment provided by engineer. */ "Set passcode" = "Zugangscode einstellen"; +/* No comment provided by engineer. */ +"Set passphrase" = "Passwort festlegen"; + /* No comment provided by engineer. */ "Set passphrase to export" = "Passwort für den Export festlegen"; @@ -3245,27 +4696,58 @@ /* No comment provided by engineer. */ "Settings" = "Einstellungen"; -/* chat item action */ +/* alert message */ +"Settings were changed." = "Die Einstellungen wurden geändert."; + +/* No comment provided by engineer. */ +"Shape profile images" = "Form der Profil-Bilder"; + +/* alert action +chat item action */ "Share" = "Teilen"; /* No comment provided by engineer. */ "Share 1-time link" = "Einmal-Link teilen"; +/* No comment provided by engineer. */ +"Share 1-time link with a friend" = "Den Einmal-Einladungslink mit einem Freund teilen"; + /* No comment provided by engineer. */ "Share address" = "Adresse teilen"; /* No comment provided by engineer. */ +"Share address publicly" = "Die Adresse öffentlich teilen"; + +/* alert title */ "Share address with contacts?" = "Die Adresse mit Kontakten teilen?"; +/* No comment provided by engineer. */ +"Share from other apps." = "Aus anderen Apps heraus teilen."; + /* No comment provided by engineer. */ "Share link" = "Link teilen"; +/* No comment provided by engineer. */ +"Share profile" = "Profil teilen"; + +/* No comment provided by engineer. */ +"Share SimpleX address on social media." = "Die SimpleX-Adresse auf sozialen Medien teilen."; + /* No comment provided by engineer. */ "Share this 1-time invite link" = "Teilen Sie diesen Einmal-Einladungslink"; +/* No comment provided by engineer. */ +"Share to SimpleX" = "Mit SimpleX teilen"; + /* No comment provided by engineer. */ "Share with contacts" = "Mit Kontakten teilen"; +/* No comment provided by engineer. */ +"Short link" = "Verkürzter Link"; + +/* No comment provided by engineer. */ +"Show → on messages sent via private routing." = "Bei Nachrichten, die über privates Routing versendet wurden, → anzeigen."; + /* No comment provided by engineer. */ "Show calls in phone history" = "Anrufliste anzeigen"; @@ -3275,18 +4757,42 @@ /* No comment provided by engineer. */ "Show last messages" = "Letzte Nachrichten anzeigen"; +/* No comment provided by engineer. */ +"Show message status" = "Nachrichtenstatus anzeigen"; + +/* No comment provided by engineer. */ +"Show percentage" = "Prozentualen Anteil anzeigen"; + /* No comment provided by engineer. */ "Show preview" = "Vorschau anzeigen"; +/* No comment provided by engineer. */ +"Show QR code" = "QR-Code anzeigen"; + /* No comment provided by engineer. */ "Show:" = "Anzeigen:"; +/* No comment provided by engineer. */ +"SimpleX" = "SimpleX"; + /* No comment provided by engineer. */ "SimpleX address" = "SimpleX-Adresse"; /* No comment provided by engineer. */ "SimpleX Address" = "SimpleX-Adresse"; +/* No comment provided by engineer. */ +"SimpleX address and 1-time links are safe to share via any messenger." = "Die SimpleX-Adresse und Einmal-Links können sicher über beliebige Messenger geteilt werden."; + +/* No comment provided by engineer. */ +"SimpleX address or 1-time link?" = "SimpleX-Adresse oder Einmal-Link?"; + +/* simplex link type */ +"SimpleX channel link" = "SimpleX-Kanal-Link"; + +/* No comment provided by engineer. */ +"SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app." = "SimpleX-Chat und Flux haben vereinbart, die von Flux betriebenen Server in die App aufzunehmen."; + /* No comment provided by engineer. */ "SimpleX Chat security was audited by Trail of Bits." = "Die Sicherheit von SimpleX Chat wurde von Trail of Bits überprüft."; @@ -3299,9 +4805,15 @@ /* simplex link type */ "SimpleX group link" = "SimpleX Gruppen-Link"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "SimpleX-Links"; +/* No comment provided by engineer. */ +"SimpleX links are prohibited." = "In dieser Gruppe sind SimpleX-Links nicht erlaubt."; + +/* No comment provided by engineer. */ +"SimpleX links not allowed" = "SimpleX-Links sind nicht erlaubt"; + /* No comment provided by engineer. */ "SimpleX Lock" = "SimpleX-Sperre"; @@ -3317,9 +4829,15 @@ /* simplex link type */ "SimpleX one-time invitation" = "SimpleX-Einmal-Einladung"; +/* No comment provided by engineer. */ +"SimpleX protocols reviewed by Trail of Bits." = "Die SimpleX-Protokolle wurden von Trail of Bits überprüft."; + /* No comment provided by engineer. */ "Simplified incognito mode" = "Vereinfachter Inkognito-Modus"; +/* No comment provided by engineer. */ +"Size" = "Größe"; + /* No comment provided by engineer. */ "Skip" = "Überspringen"; @@ -3330,14 +4848,42 @@ "Small groups (max 20)" = "Kleine Gruppen (max. 20)"; /* No comment provided by engineer. */ -"SMP servers" = "SMP-Server"; +"SMP server" = "SMP-Server"; + +/* No comment provided by engineer. */ +"SOCKS proxy" = "SOCKS-Proxy"; + +/* blur media */ +"Soft" = "Weich"; + +/* No comment provided by engineer. */ +"Some app settings were not migrated." = "Einige App-Einstellungen wurden nicht migriert."; + +/* No comment provided by engineer. */ +"Some file(s) were not exported:" = "Einzelne Datei(en) wurde(n) nicht exportiert:"; /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "Während des Imports sind einige nicht schwerwiegende Fehler aufgetreten - in der Chat-Konsole finden Sie weitere Einzelheiten."; +/* No comment provided by engineer. */ +"Some non-fatal errors occurred during import:" = "Während des Imports traten ein paar nicht schwerwiegende Fehler auf:"; + +/* alert message */ +"Some servers failed the test:\n%@" = "Einige Server haben den Test nicht bestanden:\n%@"; + /* notification title */ "Somebody" = "Jemand"; +/* blocking reason +report reason */ +"Spam" = "Spam"; + +/* No comment provided by engineer. */ +"Square, circle, or anything in between." = "Quadratisch, kreisförmig oder irgendetwas dazwischen."; + +/* chat item text */ +"standard end-to-end encryption" = "Standard-Ende-zu-Ende-Verschlüsselung"; + /* No comment provided by engineer. */ "Start chat" = "Starten Sie den Chat"; @@ -3347,14 +4893,20 @@ /* No comment provided by engineer. */ "Start migration" = "Starten Sie die Migration"; +/* No comment provided by engineer. */ +"Starting from %@." = "Beginnend mit %@."; + /* No comment provided by engineer. */ "starting…" = "Verbindung wird gestartet…"; +/* No comment provided by engineer. */ +"Statistics" = "Statistiken"; + /* No comment provided by engineer. */ "Stop" = "Beenden"; /* No comment provided by engineer. */ -"Stop chat to enable database actions" = "Chat beenden, um Datenbankaktionen zu erlauben"; +"Stop chat" = "Chat beenden"; /* No comment provided by engineer. */ "Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Beenden Sie den Chat, um die Chat-Datenbank zu exportieren, zu importieren oder zu löschen. Solange der Chat angehalten ist, können Sie keine Nachrichten empfangen oder senden."; @@ -3363,44 +4915,74 @@ "Stop chat?" = "Chat beenden?"; /* cancel file action */ -"Stop file" = "Datei beenden"; +"Stop file" = "Herunterladen beenden"; /* No comment provided by engineer. */ -"Stop receiving file?" = "Den Empfang der Datei beenden?"; +"Stop receiving file?" = "Das Herunterladen der Datei beenden?"; /* No comment provided by engineer. */ -"Stop sending file?" = "Das Senden der Datei beenden?"; +"Stop sending file?" = "Das Hochladen der Datei beenden?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "Teilen beenden"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "Das Teilen der Adresse beenden?"; /* authentication reason */ "Stop SimpleX" = "Stoppen Sie SimpleX"; +/* No comment provided by engineer. */ +"Stopping chat" = "Chat wird beendet"; + +/* No comment provided by engineer. */ +"Storage" = "Ablage"; + /* No comment provided by engineer. */ "strike" = "durchstreichen"; +/* blur media */ +"Strong" = "Hart"; + /* No comment provided by engineer. */ "Submit" = "Bestätigen"; +/* No comment provided by engineer. */ +"Subscribed" = "Abonniert"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Fehler beim Abonnieren"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Nicht beachtete Abonnements"; + /* No comment provided by engineer. */ "Support SimpleX Chat" = "Unterstützung von SimpleX Chat"; +/* No comment provided by engineer. */ +"Switch audio and video during the call." = "Während des Anrufs zwischen Audio und Video wechseln"; + +/* No comment provided by engineer. */ +"Switch chat profile for 1-time invitations." = "Das Chat-Profil für Einmal-Einladungen wechseln"; + /* No comment provided by engineer. */ "System" = "System"; /* No comment provided by engineer. */ "System authentication" = "System-Authentifizierung"; +/* No comment provided by engineer. */ +"Tail" = "Sprechblasen-Format"; + /* No comment provided by engineer. */ "Take picture" = "Machen Sie ein Foto"; /* No comment provided by engineer. */ "Tap button " = "Schaltfläche antippen "; +/* No comment provided by engineer. */ +"Tap Create SimpleX address in the menu to create it later." = "Tippen Sie im Menü auf SimpleX-Adresse erstellen, um sie später zu erstellen."; + /* No comment provided by engineer. */ "Tap to activate profile." = "Zum Aktivieren des Profils tippen."; @@ -3420,11 +5002,14 @@ "Tap to scan" = "Zum Scannen tippen"; /* No comment provided by engineer. */ -"Tap to start a new chat" = "Zum Starten eines neuen Chats tippen"; +"TCP connection" = "TCP-Verbindung"; /* No comment provided by engineer. */ "TCP connection timeout" = "Timeout der TCP-Verbindung"; +/* No comment provided by engineer. */ +"TCP port for messaging" = "TCP-Port für Nachrichtenübermittlung"; + /* No comment provided by engineer. */ "TCP_KEEPCNT" = "TCP_KEEPCNT"; @@ -3434,16 +5019,22 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* file error alert title */ +"Temporary file error" = "Temporärer Datei-Fehler"; + /* server test failure */ "Test failed at step %@." = "Der Test ist beim Schritt %@ fehlgeschlagen."; +/* No comment provided by engineer. */ +"Test notifications" = "Benachrichtigungen testen"; + /* No comment provided by engineer. */ "Test server" = "Teste Server"; /* No comment provided by engineer. */ "Test servers" = "Teste alle Server"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "Tests sind fehlgeschlagen!"; /* No comment provided by engineer. */ @@ -3456,10 +5047,13 @@ "Thanks to the users – contribute via Weblate!" = "Dank der Nutzer - Tragen Sie per Weblate bei!"; /* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "Die erste Plattform ohne Benutzerkennungen – Privat per Design."; +"The app can notify you when you receive messages or contact requests - please open settings to enable." = "Wenn sie Nachrichten oder Kontaktanfragen empfangen, kann Sie die App benachrichtigen - Um dies zu aktivieren, öffnen Sie bitte die Einstellungen."; /* No comment provided by engineer. */ -"The app can notify you when you receive messages or contact requests - please open settings to enable." = "Wenn sie Nachrichten oder Kontaktanfragen empfangen, kann Sie die App benachrichtigen - Um dies zu aktivieren, öffnen Sie bitte die Einstellungen."; +"The app protects your privacy by using different operators in each conversation." = "Durch Verwendung verschiedener Netzwerk-Betreiber für jede Unterhaltung schützt die App Ihre Privatsphäre."; + +/* No comment provided by engineer. */ +"The app will ask to confirm downloads from unknown file servers (except .onion)." = "Die App wird eine Bestätigung bei Downloads von unbekannten Datei-Servern anfordern (außer bei .onion)."; /* No comment provided by engineer. */ "The attempt to change database passphrase was not completed." = "Die Änderung des Datenbank-Passworts konnte nicht abgeschlossen werden."; @@ -3467,6 +5061,9 @@ /* No comment provided by engineer. */ "The code you scanned is not a SimpleX link QR code." = "Der von Ihnen gescannte Code ist kein SimpleX-Link-QR-Code."; +/* No comment provided by engineer. */ +"The connection reached the limit of undelivered messages, your contact may be offline." = "Diese Verbindung hat das Limit der nicht ausgelieferten Nachrichten erreicht. Ihr Kontakt ist möglicherweise offline."; + /* No comment provided by engineer. */ "The connection you accepted will be cancelled!" = "Die von Ihnen akzeptierte Verbindung wird abgebrochen!"; @@ -3479,6 +5076,9 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "Die Verschlüsselung funktioniert und ein neues Verschlüsselungsabkommen ist nicht erforderlich. Es kann zu Verbindungsfehlern kommen!"; +/* No comment provided by engineer. */ +"The future of messaging" = "Die nächste Generation von privatem Messaging"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "Der Hash der vorherigen Nachricht unterscheidet sich."; @@ -3492,13 +5092,22 @@ "The message will be marked as moderated for all members." = "Diese Nachricht wird für alle Mitglieder als moderiert gekennzeichnet."; /* No comment provided by engineer. */ -"The next generation of private messaging" = "Die nächste Generation von privatem Messaging"; +"The messages will be deleted for all members." = "Die Nachrichten werden für alle Gruppenmitglieder gelöscht."; + +/* No comment provided by engineer. */ +"The messages will be marked as moderated for all members." = "Die Nachrichten werden für alle Mitglieder als moderiert gekennzeichnet werden."; /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "Die alte Datenbank wurde während der Migration nicht entfernt. Sie kann gelöscht werden."; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "Das Profil wird nur mit Ihren Kontakten geteilt."; +"Your profile is stored on your device and only shared with your contacts." = "Das Profil wird nur mit Ihren Kontakten geteilt."; + +/* No comment provided by engineer. */ +"The same conditions will apply to operator **%@**." = "Dieselben Nutzungsbedingungen gelten auch für den Betreiber **%@**."; + +/* No comment provided by engineer. */ +"The second preset operator in the app!" = "Der zweite voreingestellte Netzwerk-Betreiber in der App!"; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "Wir haben das zweite Häkchen vermisst! ✅"; @@ -3507,13 +5116,22 @@ "The sender will NOT be notified" = "Der Absender wird NICHT benachrichtigt"; /* No comment provided by engineer. */ -"The servers for new connections of your current chat profile **%@**." = "Mögliche Server für neue Verbindungen von Ihrem aktuellen Chat-Profil **%@**."; +"The servers for new connections of your current chat profile **%@**." = "Nachrichten-Server für neue Verbindungen über Ihr aktuelles Chat-Profil **%@**."; + +/* No comment provided by engineer. */ +"The servers for new files of your current chat profile **%@**." = "Medien- und Datei-Server für neue Daten über Ihr aktuelles Chat-Profil **%@**."; /* No comment provided by engineer. */ "The text you pasted is not a SimpleX link." = "Der von Ihnen eingefügte Text ist kein SimpleX-Link."; /* No comment provided by engineer. */ -"Theme" = "Design"; +"The uploaded database archive will be permanently removed from the servers." = "Das hochgeladene Datenbank-Archiv wird dauerhaft von den Servern entfernt."; + +/* No comment provided by engineer. */ +"Themes" = "Design"; + +/* No comment provided by engineer. */ +"These conditions will also apply for: **%@**." = "Diese Nutzungsbedingungen gelten auch für: **%@**."; /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Diese Einstellungen betreffen Ihr aktuelles Profil **%@**."; @@ -3522,13 +5140,22 @@ "They can be overridden in contact and group settings." = "Sie können in den Kontakteinstellungen überschrieben werden."; /* No comment provided by engineer. */ -"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Diese Aktion kann nicht rückgängig gemacht werden! Alle empfangenen und gesendeten Dateien und Medien werden gelöscht. Bilder mit niedriger Auflösung bleiben erhalten."; +"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Es werden alle herunter- und hochgeladenen Dateien und Medien gelöscht. Bilder mit niedriger Auflösung bleiben erhalten. Diese Aktion kann nicht rückgängig gemacht werden!"; /* No comment provided by engineer. */ -"This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Diese Aktion kann nicht rückgängig gemacht werden! Alle empfangenen und gesendeten Nachrichten, die über den ausgewählten Zeitraum hinaus gehen, werden gelöscht. Dieser Vorgang kann mehrere Minuten dauern."; +"This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Es werden alle empfangenen und gesendeten Nachrichten, die über den ausgewählten Zeitraum hinaus gehen, gelöscht. Dieser Vorgang kann mehrere Minuten dauern. Diese Aktion kann nicht rückgängig gemacht werden!"; + +/* alert message */ +"This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted." = "Die älteren als die ausgewählten gesendeten und empfangenen Nachrichten in diesem Chat werden gelöscht. Diese Aktion kann nicht rückgängig gemacht werden!"; /* No comment provided by engineer. */ -"This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Diese Aktion kann nicht rückgängig gemacht werden! Ihr Profil und Ihre Kontakte, Nachrichten und Dateien gehen unwiderruflich verloren."; +"This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Ihr Profil und Ihre Kontakte, Nachrichten und Dateien gehen unwiderruflich verloren. Diese Aktion kann nicht rückgängig gemacht werden!"; + +/* E2EE info chat item */ +"This chat is protected by end-to-end encryption." = "Dieser Chat ist durch Ende-zu-Ende-Verschlüsselung geschützt."; + +/* E2EE info chat item */ +"This chat is protected by quantum resistant end-to-end encryption." = "Dieser Chat ist durch Quantum-resistente Ende-zu-Ende-Verschlüsselung geschützt."; /* notification title */ "this contact" = "Dieser Kontakt"; @@ -3551,11 +5178,23 @@ /* No comment provided by engineer. */ "This is your own SimpleX address!" = "Das ist Ihre eigene SimpleX-Adresse!"; +/* No comment provided by engineer. */ +"This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link." = "Für diesen Link wird eine neuere App-Version benötigt. Bitte aktualisieren Sie die App oder bitten Sie Ihren Kontakt einen kompatiblen Link zu senden."; + +/* No comment provided by engineer. */ +"This link was used with another mobile device, please create a new link on the desktop." = "Dieser Link wurde schon mit einem anderen Mobiltelefon genutzt. Bitte erstellen sie einen neuen Link in der Desktop-App."; + +/* No comment provided by engineer. */ +"This message was deleted or not received yet." = "Diese Nachricht wurde gelöscht oder bisher noch nicht empfangen."; + /* No comment provided by engineer. */ "This setting applies to messages in your current chat profile **%@**." = "Diese Einstellung gilt für Nachrichten in Ihrem aktuellen Chat-Profil **%@**."; /* No comment provided by engineer. */ -"To ask any questions and to receive updates:" = "Um Fragen zu stellen und Aktualisierungen zu erhalten:"; +"Title" = "Bezeichnung"; + +/* No comment provided by engineer. */ +"To ask any questions and to receive updates:" = "Um Fragen zu stellen und aktuelle Informationen zu erhalten:"; /* No comment provided by engineer. */ "To connect, your contact can scan QR code or use the link in the app." = "Um eine Verbindung herzustellen, kann Ihr Kontakt den QR-Code scannen oder den Link in der App verwenden."; @@ -3567,7 +5206,7 @@ "To make a new connection" = "Um eine Verbindung mit einem neuen Kontakt zu erstellen"; /* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Zum Schutz Ihrer Privatsphäre verwendet SimpleX an Stelle von Benutzerkennungen, die von allen anderen Plattformen verwendet werden, Kennungen für Nachrichtenwarteschlangen, die für jeden Ihrer Kontakte individuell sind."; +"To protect against your link being replaced, you can compare contact security codes." = "Zum Schutz vor dem Austausch Ihres Links können Sie die Sicherheitscodes Ihrer Kontakte vergleichen."; /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "Bild- und Sprachdateinamen enthalten UTC, um Informationen zur Zeitzone zu schützen."; @@ -3575,24 +5214,60 @@ /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Um Ihre Informationen zu schützen, schalten Sie die SimpleX-Sperre ein.\nSie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funktion aktiviert wird."; +/* No comment provided by engineer. */ +"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Zum Schutz Ihrer IP-Adresse, wird für die Nachrichten-Auslieferung privates Routing über Ihre konfigurierten SMP-Server genutzt."; + +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "Zum Schutz Ihrer Privatsphäre verwendet SimpleX an Stelle von Benutzerkennungen, die von allen anderen Plattformen verwendet werden, Kennungen für Nachrichtenwarteschlangen, die für jeden Ihrer Kontakte individuell sind."; + +/* No comment provided by engineer. */ +"To receive" = "Für den Empfang"; + +/* No comment provided by engineer. */ +"To record speech please grant permission to use Microphone." = "Bitte erteilen Sie für Sprach-Aufnahmen die Genehmigung das Mikrofon zu nutzen."; + +/* No comment provided by engineer. */ +"To record video please grant permission to use Camera." = "Bitte erteilen Sie für Video-Aufnahmen die Genehmigung die Kamera zu nutzen."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Bitte erlauben Sie die Nutzung des Mikrofons, um Sprachnachrichten aufnehmen zu können."; /* No comment provided by engineer. */ "To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Geben Sie ein vollständiges Passwort in das Suchfeld auf der Seite **Ihre Chat-Profile** ein, um Ihr verborgenes Profil zu sehen."; +/* No comment provided by engineer. */ +"To send" = "Für das Senden"; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Um sofortige Push-Benachrichtigungen zu unterstützen, muss die Chat-Datenbank migriert werden."; +/* No comment provided by engineer. */ +"To use the servers of **%@**, accept conditions of use." = "Um die Server von **%@** zu nutzen, müssen Sie dessen Nutzungsbedingungen akzeptieren."; + /* No comment provided by engineer. */ "To verify end-to-end encryption with your contact compare (or scan) the code on your devices." = "Um die Ende-zu-Ende-Verschlüsselung mit Ihrem Kontakt zu überprüfen, müssen Sie den Sicherheitscode in Ihren Apps vergleichen oder scannen."; +/* No comment provided by engineer. */ +"Toggle chat list:" = "Chat-Liste umschalten:"; + /* No comment provided by engineer. */ "Toggle incognito when connecting." = "Inkognito beim Verbinden einschalten."; +/* token status */ +"Token status: %@." = "Token-Status: %@."; + +/* No comment provided by engineer. */ +"Toolbar opacity" = "Deckkraft der Symbolleiste"; + +/* No comment provided by engineer. */ +"Total" = "Summe aller Abonnements"; + /* No comment provided by engineer. */ "Transport isolation" = "Transport-Isolation"; +/* No comment provided by engineer. */ +"Transport sessions" = "Transport-Sitzungen"; + /* No comment provided by engineer. */ "Trying to connect to the server used to receive messages from this contact (error: %@)." = "Beim Versuch die Verbindung mit dem Server aufzunehmen, der für den Empfang von Nachrichten mit diesem Kontakt genutzt wird, ist ein Fehler aufgetreten (Fehler: %@)."; @@ -3629,13 +5304,13 @@ /* rcv group event chat item */ "unblocked %@" = "%@ wurde freigegeben"; -/* item status description */ -"Unexpected error: %@" = "Unerwarteter Fehler: %@"; +/* No comment provided by engineer. */ +"Undelivered messages" = "Nicht ausgelieferte Nachrichten"; /* No comment provided by engineer. */ "Unexpected migration state" = "Unerwarteter Migrationsstatus"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "Fav. entf."; /* No comment provided by engineer. */ @@ -3662,6 +5337,12 @@ /* No comment provided by engineer. */ "Unknown error" = "Unbekannter Fehler"; +/* No comment provided by engineer. */ +"unknown servers" = "Unbekannte Relais"; + +/* alert title */ +"Unknown servers!" = "Unbekannte Server!"; + /* No comment provided by engineer. */ "unknown status" = "unbekannter Gruppenmitglieds-Status"; @@ -3683,21 +5364,24 @@ /* authentication reason */ "Unlock app" = "App entsperren"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "Stummschaltung aufheben"; /* No comment provided by engineer. */ +"unprotected" = "Ungeschützt"; + +/* swipe action */ "Unread" = "Ungelesen"; +/* No comment provided by engineer. */ +"Unsupported connection link" = "Verbindungs-Link wird nicht unterstützt"; + /* No comment provided by engineer. */ "Up to 100 last messages are sent to new members." = "Bis zu 100 der letzten Nachrichten werden an neue Mitglieder gesendet."; /* No comment provided by engineer. */ "Update" = "Aktualisieren"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = "Einstellung für .onion-Hosts aktualisieren?"; - /* No comment provided by engineer. */ "Update database passphrase" = "Datenbank-Passwort aktualisieren"; @@ -3705,7 +5389,10 @@ "Update network settings?" = "Netzwerkeinstellungen aktualisieren?"; /* No comment provided by engineer. */ -"Update transport isolation mode?" = "Transport-Isolations-Modus aktualisieren?"; +"Update settings?" = "Einstellungen aktualisieren?"; + +/* No comment provided by engineer. */ +"Updated conditions" = "Aktualisierte Nutzungsbedingungen"; /* rcv group event chat item */ "updated group profile" = "Aktualisiertes Gruppenprofil"; @@ -3717,22 +5404,43 @@ "Updating settings will re-connect the client to all servers." = "Die Aktualisierung der Einstellungen wird den Client wieder mit allen Servern verbinden."; /* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "Die Aktualisierung dieser Einstellung wird den Client wieder mit allen Servern verbinden."; +"Upgrade and open chat" = "Aktualisieren und den Chat öffnen"; /* No comment provided by engineer. */ -"Upgrade and open chat" = "Aktualisieren und den Chat öffnen"; +"Upload errors" = "Fehler beim Hochladen"; + +/* No comment provided by engineer. */ +"Upload failed" = "Hochladen fehlgeschlagen"; /* server test step */ "Upload file" = "Datei hochladen"; +/* No comment provided by engineer. */ +"Uploaded" = "Hochgeladen"; + +/* No comment provided by engineer. */ +"Uploaded files" = "Hochgeladene Dateien"; + +/* No comment provided by engineer. */ +"Uploading archive" = "Archiv wird hochgeladen"; + /* No comment provided by engineer. */ "Use .onion hosts" = "Verwende .onion-Hosts"; +/* No comment provided by engineer. */ +"Use %@" = "Verwende %@"; + /* No comment provided by engineer. */ "Use chat" = "Verwenden Sie Chat"; /* No comment provided by engineer. */ -"Use current profile" = "Nutzen Sie das aktuelle Profil"; +"Use current profile" = "Aktuelles Profil nutzen"; + +/* No comment provided by engineer. */ +"Use for files" = "Für Dateien verwenden"; + +/* No comment provided by engineer. */ +"Use for messages" = "Für Nachrichten verwenden"; /* No comment provided by engineer. */ "Use for new connections" = "Für neue Verbindungen nutzen"; @@ -3744,22 +5452,52 @@ "Use iOS call interface" = "iOS Anrufschnittstelle nutzen"; /* No comment provided by engineer. */ -"Use new incognito profile" = "Nutzen Sie das neue Inkognito-Profil"; +"Use new incognito profile" = "Neues Inkognito-Profil nutzen"; /* No comment provided by engineer. */ "Use only local notifications?" = "Nur lokale Benachrichtigungen nutzen?"; +/* No comment provided by engineer. */ +"Use private routing with unknown servers when IP address is not protected." = "Sie nutzen privates Routing mit unbekannten Servern, wenn Ihre IP-Adresse nicht geschützt ist."; + +/* No comment provided by engineer. */ +"Use private routing with unknown servers." = "Sie nutzen privates Routing mit unbekannten Servern."; + /* No comment provided by engineer. */ "Use server" = "Server nutzen"; +/* No comment provided by engineer. */ +"Use servers" = "Verwende Server"; + +/* No comment provided by engineer. */ +"Use short links (BETA)" = "Kurze Links verwenden (BETA)"; + /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Verwenden Sie SimpleX-Chat-Server?"; /* No comment provided by engineer. */ -"User profile" = "Benutzerprofil"; +"Use SOCKS proxy" = "SOCKS-Proxy nutzen"; /* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = "Für die Nutzung von .onion-Hosts sind kompatible VPN-Anbieter erforderlich."; +"Use TCP port %@ when no port is specified." = "Solange kein Port konfiguriert ist, wird TCP-Port %@ genutzt."; + +/* No comment provided by engineer. */ +"Use TCP port 443 for preset servers only." = "TCP-Port 443 nur für voreingestellte Server verwenden."; + +/* No comment provided by engineer. */ +"Use the app while in the call." = "Die App kann während eines Anrufs genutzt werden."; + +/* No comment provided by engineer. */ +"Use the app with one hand." = "Die App mit einer Hand bedienen."; + +/* No comment provided by engineer. */ +"Use web port" = "Web-Port nutzen"; + +/* No comment provided by engineer. */ +"User selection" = "Benutzer-Auswahl"; + +/* No comment provided by engineer. */ +"Username" = "Benutzername"; /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Verwendung von SimpleX-Chat-Servern."; @@ -3782,6 +5520,12 @@ /* No comment provided by engineer. */ "Verify connections" = "Verbindungen überprüfen"; +/* No comment provided by engineer. */ +"Verify database passphrase" = "Überprüfen Sie das Datenbank-Passwort"; + +/* No comment provided by engineer. */ +"Verify passphrase" = "Überprüfen Sie das Passwort"; + /* No comment provided by engineer. */ "Verify security code" = "Sicherheitscode überprüfen"; @@ -3803,6 +5547,9 @@ /* No comment provided by engineer. */ "Via secure quantum resistant protocol." = "Über ein sicheres quantenbeständiges Protokoll."; +/* No comment provided by engineer. */ +"video" = "Video"; + /* No comment provided by engineer. */ "Video call" = "Videoanruf"; @@ -3810,17 +5557,23 @@ "video call (not e2e encrypted)" = "Videoanruf (nicht E2E verschlüsselt)"; /* No comment provided by engineer. */ -"Video will be received when your contact completes uploading it." = "Das Video wird empfangen, sobald Ihr Kontakt das Hochladen beendet hat."; +"Video will be received when your contact completes uploading it." = "Das Video wird heruntergeladen, sobald Ihr Kontakt das Hochladen beendet hat."; /* No comment provided by engineer. */ -"Video will be received when your contact is online, please wait or check later!" = "Das Video wird empfangen, wenn Ihr Kontakt online ist. Bitte warten oder überprüfen Sie es später!"; +"Video will be received when your contact is online, please wait or check later!" = "Das Video wird heruntergeladen, sobald Ihr Kontakt online ist. Bitte warten oder überprüfen Sie es später!"; /* No comment provided by engineer. */ "Videos and files up to 1gb" = "Videos und Dateien bis zu 1GB"; +/* No comment provided by engineer. */ +"View conditions" = "Nutzungsbedingungen anschauen"; + /* No comment provided by engineer. */ "View security code" = "Schauen Sie sich den Sicherheitscode an"; +/* No comment provided by engineer. */ +"View updated conditions" = "Aktualisierte Nutzungsbedingungen anschauen"; + /* chat feature */ "Visible history" = "Sichtbarer Nachrichtenverlauf"; @@ -3834,7 +5587,10 @@ "Voice messages are prohibited in this chat." = "In diesem Chat sind Sprachnachrichten nicht erlaubt."; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "In dieser Gruppe sind Sprachnachrichten nicht erlaubt."; +"Voice messages are prohibited." = "In dieser Gruppe sind Sprachnachrichten nicht erlaubt."; + +/* No comment provided by engineer. */ +"Voice messages not allowed" = "Sprachnachrichten sind nicht erlaubt"; /* No comment provided by engineer. */ "Voice messages prohibited!" = "Sprachnachrichten sind nicht erlaubt!"; @@ -3857,9 +5613,18 @@ /* No comment provided by engineer. */ "Waiting for video" = "Auf das Video warten"; +/* No comment provided by engineer. */ +"Wallpaper accent" = "Wallpaper-Akzent"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Wallpaper-Hintergrund"; + /* No comment provided by engineer. */ "wants to connect to you!" = "möchte sich mit Ihnen verbinden!"; +/* No comment provided by engineer. */ +"Warning: starting chat on multiple devices is not supported and will cause message delivery failures" = "Warnung: Das Starten des Chats auf mehreren Geräten wird nicht unterstützt und wird zu Fehlern bei der Nachrichtenübermittlung führen"; + /* No comment provided by engineer. */ "Warning: you may lose some data!" = "Warnung: Sie könnten einige Daten verlieren!"; @@ -3875,6 +5640,9 @@ /* No comment provided by engineer. */ "Welcome message" = "Begrüßungsmeldung"; +/* No comment provided by engineer. */ +"Welcome message is too long" = "Die Begrüßungsmeldung ist zu lang"; + /* No comment provided by engineer. */ "What's new" = "Was ist neu"; @@ -3882,11 +5650,26 @@ "When available" = "Wenn verfügbar"; /* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "Wenn Personen eine Verbindung anfordern, können Sie diese annehmen oder ablehnen."; +"When connecting audio and video calls." = "Bei der Verbindung über Audio- und Video-Anrufe."; + +/* No comment provided by engineer. */ +"when IP hidden" = "Wenn die IP-Adresse versteckt ist"; + +/* No comment provided by engineer. */ +"When more than one operator is enabled, none of them has metadata to learn who communicates with whom." = "Wenn mehrere Netzwerk-Betreiber aktiviert sind, hat keiner von ihnen Metadaten, um zu erfahren, wer mit wem kommuniziert."; /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Wenn Sie ein Inkognito-Profil mit Jemandem teilen, wird dieses Profil auch für die Gruppen verwendet, für die Sie von diesem Kontakt eingeladen werden."; +/* No comment provided by engineer. */ +"WiFi" = "WiFi"; + +/* No comment provided by engineer. */ +"Will be enabled in direct chats!" = "Wird in direkten Chats automatisch aktiviert!"; + +/* No comment provided by engineer. */ +"Wired ethernet" = "Kabelgebundenes Netzwerk"; + /* No comment provided by engineer. */ "With encrypted files and media." = "Mit verschlüsselten Dateien und Medien."; @@ -3896,20 +5679,35 @@ /* No comment provided by engineer. */ "With reduced battery usage." = "Mit reduziertem Akkuverbrauch."; +/* No comment provided by engineer. */ +"Without Tor or VPN, your IP address will be visible to file servers." = "Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für Datei-Server sichtbar sein."; + +/* alert message */ +"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für diese XFTP-Relais sichtbar sein: %@."; + /* No comment provided by engineer. */ "Wrong database passphrase" = "Falsches Datenbank-Passwort"; +/* snd error text */ +"Wrong key or unknown connection - most likely this connection is deleted." = "Falscher Schlüssel oder unbekannte Verbindung - höchstwahrscheinlich ist diese Verbindung gelöscht worden."; + +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Falscher Schlüssel oder unbekannte Daten-Paketadresse der Datei - höchstwahrscheinlich wurde die Datei gelöscht."; + /* No comment provided by engineer. */ "Wrong passphrase!" = "Falsches Passwort!"; /* No comment provided by engineer. */ -"XFTP servers" = "XFTP-Server"; +"XFTP server" = "XFTP-Server"; /* pref value */ "yes" = "Ja"; /* No comment provided by engineer. */ -"You" = "Profil"; +"you" = "Profil"; + +/* No comment provided by engineer. */ +"You **must not** use the same database on two devices." = "Sie dürfen die selbe Datenbank **nicht** auf zwei Geräten nutzen."; /* No comment provided by engineer. */ "You accepted connection" = "Sie haben die Verbindung akzeptiert"; @@ -3923,6 +5721,9 @@ /* No comment provided by engineer. */ "You are already connected to %@." = "Sie sind bereits mit %@ verbunden."; +/* No comment provided by engineer. */ +"You are already connected with %@." = "Sie sind bereits mit %@ verbunden."; + /* No comment provided by engineer. */ "You are already connecting to %@." = "Sie sind bereits mit %@ verbunden."; @@ -3953,6 +5754,9 @@ /* No comment provided by engineer. */ "You are invited to group" = "Sie sind zu der Gruppe eingeladen"; +/* No comment provided by engineer. */ +"You are not connected to these servers. Private routing is used to deliver messages to them." = "Sie sind nicht mit diesen Servern verbunden. Zur Auslieferung von Nachrichten an diese Server wird privates Routing genutzt."; + /* No comment provided by engineer. */ "you are observer" = "Sie sind Beobachter"; @@ -3962,6 +5766,12 @@ /* No comment provided by engineer. */ "You can accept calls from lock screen, without device and app authentication." = "Sie können Anrufe ohne Geräte- und App-Authentifizierung vom Sperrbildschirm aus annehmen."; +/* No comment provided by engineer. */ +"You can change it in Appearance settings." = "Kann von Ihnen in den Erscheinungsbild-Einstellungen geändert werden."; + +/* No comment provided by engineer. */ +"You can configure servers via settings." = "Sie können die Server über die Einstellungen konfigurieren."; + /* No comment provided by engineer. */ "You can create it later" = "Sie können dies später erstellen"; @@ -3971,6 +5781,9 @@ /* No comment provided by engineer. */ "You can enable them later via app Privacy & Security settings." = "Sie können diese später in den Datenschutz & Sicherheits-Einstellungen der App aktivieren."; +/* No comment provided by engineer. */ +"You can give another try." = "Sie können es nochmal probieren."; + /* No comment provided by engineer. */ "You can hide or mute a user profile - swipe it to the right." = "Sie können ein Benutzerprofil verbergen oder stummschalten - wischen Sie es nach rechts."; @@ -3978,7 +5791,13 @@ "You can make it visible to your SimpleX contacts via Settings." = "Sie können sie über Einstellungen für Ihre SimpleX-Kontakte sichtbar machen."; /* notification body */ -"You can now send messages to %@" = "Sie können nun Nachrichten an %@ versenden"; +"You can now chat with %@" = "Sie können nun Nachrichten an %@ versenden"; + +/* No comment provided by engineer. */ +"You can send messages to %@ from Archived contacts." = "Sie können aus den archivierten Kontakten heraus Nachrichten an %@ versenden."; + +/* No comment provided by engineer. */ +"You can set connection name, to remember who the link was shared with." = "Sie können einen Verbindungsnamen festlegen, um sich zu merken, mit wem der Link geteilt wurde."; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "Über die Geräte-Einstellungen können Sie die Benachrichtigungsvorschau im Sperrbildschirm erlauben."; @@ -3990,10 +5809,10 @@ "You can share this address with your contacts to let them connect with **%@**." = "Sie können diese Adresse mit Ihren Kontakten teilen, um sie mit **%@** verbinden zu lassen."; /* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "Sie können Ihre Adresse als Link oder als QR-Code teilen – Jede Person kann sich darüber mit Ihnen verbinden."; +"You can start chat via app Settings / Database or by restarting the app" = "Sie können den Chat über die App-Einstellungen / Datenbank oder durch Neustart der App starten"; /* No comment provided by engineer. */ -"You can start chat via app Settings / Database or by restarting the app" = "Sie können den Chat über die App-Einstellungen / Datenbank oder durch Neustart der App starten"; +"You can still view conversation with %@ in the list of chats." = "Sie können in der Chat-Liste weiterhin die Unterhaltung mit %@ einsehen."; /* No comment provided by engineer. */ "You can turn on SimpleX Lock via Settings." = "Sie können die SimpleX-Sperre über die Einstellungen aktivieren."; @@ -4001,7 +5820,7 @@ /* No comment provided by engineer. */ "You can use markdown to format messages:" = "Um Nachrichteninhalte zu formatieren, können Sie Markdowns verwenden:"; -/* No comment provided by engineer. */ +/* alert message */ "You can view invitation link again in connection details." = "Den Einladungslink können Sie in den Details der Verbindung nochmals sehen."; /* No comment provided by engineer. */ @@ -4020,10 +5839,10 @@ "you changed role of %@ to %@" = "Sie haben die Rolle von %1$@ auf %2$@ geändert"; /* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "Sie können selbst festlegen, über welche Server Sie Ihre Nachrichten **empfangen** und an Ihre Kontakte **senden** wollen."; +"You could not be verified; please try again." = "Sie konnten nicht überprüft werden; bitte versuchen Sie es erneut."; /* No comment provided by engineer. */ -"You could not be verified; please try again." = "Sie konnten nicht überprüft werden; bitte versuchen Sie es erneut."; +"You decide who can connect." = "Sie entscheiden, wer sich mit Ihnen verbinden kann."; /* No comment provided by engineer. */ "You have already requested connection via this address!" = "Sie haben über diese Adresse bereits eine Verbindung beantragt!"; @@ -4031,9 +5850,6 @@ /* No comment provided by engineer. */ "You have already requested connection!\nRepeat connection request?" = "Sie haben bereits ein Verbindungsanfrage beantragt!\nVerbindungsanfrage wiederholen?"; -/* No comment provided by engineer. */ -"You have no chats" = "Sie haben keine Chats"; - /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "Sie müssen das Passwort jedes Mal eingeben, wenn die App startet. Es wird nicht auf dem Gerät gespeichert."; @@ -4049,9 +5865,18 @@ /* snd group event chat item */ "you left" = "Sie haben verlassen"; +/* No comment provided by engineer. */ +"You may migrate the exported database." = "Sie können die exportierte Datenbank migrieren."; + +/* No comment provided by engineer. */ +"You may save the exported archive." = "Sie können das exportierte Archiv speichern."; + /* No comment provided by engineer. */ "You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." = "Sie dürfen die neueste Version Ihrer Chat-Datenbank NUR auf einem Gerät verwenden, andernfalls erhalten Sie möglicherweise keine Nachrichten mehr von einigen Ihrer Kontakte."; +/* No comment provided by engineer. */ +"You need to allow your contact to call to be able to call them." = "Sie müssen Ihrem Kontakt Anrufe zu Ihnen erlauben, bevor Sie ihn selbst anrufen können."; + /* No comment provided by engineer. */ "You need to allow your contact to send voice messages to be able to send them." = "Sie müssen Ihrem Kontakt das Senden von Sprachnachrichten erlauben, um diese senden zu können."; @@ -4070,6 +5895,9 @@ /* chat list item description */ "you shared one-time link incognito" = "Sie haben Inkognito einen Einmal-Link geteilt"; +/* token info */ +"You should receive notifications." = "Sie sollten Benachrichtigungen erhalten."; + /* snd group event chat item */ "you unblocked %@" = "Sie haben %@ freigegeben"; @@ -4094,6 +5922,9 @@ /* No comment provided by engineer. */ "You will still receive calls and notifications from muted profiles when they are active." = "Sie können Anrufe und Benachrichtigungen auch von stummgeschalteten Profilen empfangen, solange diese aktiv sind."; +/* No comment provided by engineer. */ +"You will stop receiving messages from this chat. Chat history will be preserved." = "Sie werden von diesem Chat keine Nachrichten mehr erhalten. Der Nachrichtenverlauf bleibt erhalten."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Sie werden von dieser Gruppe keine Nachrichten mehr erhalten. Der Nachrichtenverlauf wird beibehalten."; @@ -4109,9 +5940,6 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Sie verwenden ein Inkognito-Profil für diese Gruppe. Um zu verhindern, dass Sie Ihr Hauptprofil teilen, ist in diesem Fall das Einladen von Kontakten nicht erlaubt"; -/* No comment provided by engineer. */ -"Your %@ servers" = "Ihre %@-Server"; - /* No comment provided by engineer. */ "Your calls" = "Anrufe"; @@ -4121,11 +5949,14 @@ /* No comment provided by engineer. */ "Your chat database is not encrypted - set passphrase to encrypt it." = "Ihre Chat-Datenbank ist nicht verschlüsselt. Bitte legen Sie ein Passwort fest, um sie zu schützen."; +/* alert title */ +"Your chat preferences" = "Ihre Chat-Präferenzen"; + /* No comment provided by engineer. */ "Your chat profiles" = "Ihre Chat-Profile"; /* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Damit die Verbindung hergestellt werden kann, muss Ihr Kontakt online sein.\nSie können diese Verbindung abbrechen und den Kontakt entfernen (und es später nochmals mit einem neuen Link versuchen)."; +"Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." = "Ihre Verbindung wurde auf %@ verschoben. Während Sie auf das Profil weitergeleitet wurden trat aber ein unerwarteter Fehler auf."; /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "Ihr Kontakt hat eine Datei gesendet, die größer ist als die derzeit unterstützte maximale Größe (%@)."; @@ -4134,7 +5965,10 @@ "Your contacts can allow full message deletion." = "Ihre Kontakte können die unwiederbringliche Löschung von Nachrichten erlauben."; /* No comment provided by engineer. */ -"Your contacts will remain connected." = "Ihre Kontakte bleiben verbunden."; +"Your contacts will remain connected." = "Ihre Kontakte bleiben weiterhin verbunden."; + +/* No comment provided by engineer. */ +"Your credentials may be sent unencrypted." = "Ihre Anmeldeinformationen können unverschlüsselt versendet werden."; /* No comment provided by engineer. */ "Your current chat database will be DELETED and REPLACED with the imported one." = "Ihre aktuelle Chat-Datenbank wird GELÖSCHT und durch die Importierte ERSETZT."; @@ -4149,7 +5983,7 @@ "Your preferences" = "Ihre Präferenzen"; /* No comment provided by engineer. */ -"Your privacy" = "Ihre Privatsphäre"; +"Your privacy" = "Privatsphäre"; /* No comment provided by engineer. */ "Your profile" = "Mein Profil"; @@ -4158,7 +5992,10 @@ "Your profile **%@** will be shared." = "Ihr Profil **%@** wird geteilt."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt.\nSimpleX-Server können Ihr Profil nicht einsehen."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt. SimpleX-Server können Ihr Profil nicht einsehen."; + +/* alert message */ +"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Ihr Profil wurde geändert. Wenn Sie es speichern, wird das aktualisierte Profil an alle Ihre Kontakte gesendet."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Ihr Profil, Ihre Kontakte und zugestellten Nachrichten werden auf Ihrem Gerät gespeichert."; @@ -4167,10 +6004,10 @@ "Your random profile" = "Ihr Zufallsprofil"; /* No comment provided by engineer. */ -"Your server" = "Ihr Server"; +"Your server address" = "Ihre Serveradresse"; /* No comment provided by engineer. */ -"Your server address" = "Ihre Serveradresse"; +"Your servers" = "Ihre Server"; /* No comment provided by engineer. */ "Your settings" = "Einstellungen"; @@ -4178,9 +6015,3 @@ /* No comment provided by engineer. */ "Your SimpleX address" = "Ihre SimpleX-Adresse"; -/* No comment provided by engineer. */ -"Your SMP servers" = "Ihre SMP-Server"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "Ihre XFTP-Server"; - diff --git a/apps/ios/de.lproj/SimpleX--iOS--InfoPlist.strings b/apps/ios/de.lproj/SimpleX--iOS--InfoPlist.strings index 5fe2ef2d09..e0554c9fb6 100644 --- a/apps/ios/de.lproj/SimpleX--iOS--InfoPlist.strings +++ b/apps/ios/de.lproj/SimpleX--iOS--InfoPlist.strings @@ -2,7 +2,7 @@ "CFBundleName" = "SimpleX"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "SimpleX benötigt Zugriff auf die Kamera, um QR Codes für die Verbindung mit anderen Nutzern zu scannen und Videoanrufe durchzuführen."; +"NSCameraUsageDescription" = "SimpleX benötigt Zugriff auf die Kamera, um QR Codes für die Verbindung mit anderen Benutzern zu scannen und Videoanrufe durchzuführen."; /* Privacy - Face ID Usage Description */ "NSFaceIDUsageDescription" = "Face ID wird von SimpleX für die lokale Authentifizierung genutzt"; @@ -14,5 +14,5 @@ "NSMicrophoneUsageDescription" = "SimpleX benötigt Zugriff auf das Mikrofon, um Audio- und Videoanrufe und die Aufnahme von Sprachnachrichten zu ermöglichen."; /* Privacy - Photo Library Additions Usage Description */ -"NSPhotoLibraryAddUsageDescription" = "SimpleX benötigt Zugriff auf das Fotoalbum, um selbst gemachte oder empfangene Bilder zu speichern"; +"NSPhotoLibraryAddUsageDescription" = "SimpleX benötigt Zugriff auf das Fotoalbum, um selbst gemachte oder heruntergeladene Bilder zu speichern"; diff --git a/apps/ios/en.lproj/Localizable.strings b/apps/ios/en.lproj/Localizable.strings index cf485752ea..cb83427195 100644 --- a/apps/ios/en.lproj/Localizable.strings +++ b/apps/ios/en.lproj/Localizable.strings @@ -1,9 +1,6 @@ /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact."; - /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -27,4 +24,3 @@ /* No comment provided by engineer. */ "No group!" = "Group not found!"; - diff --git a/apps/ios/es.lproj/Localizable.strings b/apps/ios/es.lproj/Localizable.strings index ad252b0a88..28ba0f0642 100644 --- a/apps/ios/es.lproj/Localizable.strings +++ b/apps/ios/es.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (puede copiarse)"; @@ -31,30 +16,15 @@ /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- mensajes de voz de hasta 5 minutos.\n- tiempo personalizado para mensajes temporales.\n- historial de edición."; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 coloreado!"; -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - /* No comment provided by engineer. */ "(new)" = "(nuevo)"; /* No comment provided by engineer. */ "(this device v%@)" = "(este dispositivo v%@)"; -/* No comment provided by engineer. */ -")" = ")"; - /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Contribuye](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -65,10 +35,7 @@ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Estrella en GitHub](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add contact**: to create a new invitation link, or connect via a link you received." = "**Añadir contacto**: crea un enlace de invitación nuevo o usa un enlace recibido."; - -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Añadir nuevo contacto**: para crear tu código QR o enlace de un uso para tu contacto."; +"**Create 1-time link**: to create and share a new invitation link." = "**Añadir contacto**: crea un enlace de invitación nuevo."; /* No comment provided by engineer. */ "**Create group**: to create a new group." = "**Crear grupo**: crea un grupo nuevo."; @@ -80,20 +47,29 @@ "**e2e encrypted** video call" = "Videollamada con **cifrado de extremo a extremo**"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Más privado**: comprueba los mensajes nuevos cada 20 minutos. El token del dispositivo se comparte con el servidor de SimpleX Chat, pero no cuántos contactos o mensajes tienes."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Más privado**: comprueba los mensajes nuevos cada 20 minutos. El token del dispositivo se comparte con el servidor de SimpleX Chat, pero no cuántos contactos o mensajes tienes."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Más privado**: no se usa el servidor de notificaciones de SimpleX Chat, los mensajes se comprueban periódicamente en segundo plano (dependiendo de la frecuencia con la que utilices la aplicación)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**Más privado**: no se usa el servidor de notificaciones de SimpleX Chat, los mensajes se comprueban periódicamente en segundo plano (dependiendo de la frecuencia con la que utilices la aplicación)."; /* No comment provided by engineer. */ -"**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Atención**: NO podrás recuperar o cambiar la contraseña si la pierdes."; +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Recuarda**: usar la misma base de datos en dos dispositivos hará que falle el descifrado de mensajes como protección de seguridad."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Recomendado**: el token del dispositivo y las notificaciones se envían al servidor de notificaciones de SimpleX Chat, pero no el contenido del mensaje, su tamaño o su procedencia."; +"**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Atención**: Si la pierdes NO podrás recuperar o cambiar la contraseña."; + +/* No comment provided by engineer. */ +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Recomendado**: el token del dispositivo y las notificaciones se envían al servidor de notificaciones de SimpleX Chat, pero no el contenido del mensaje, su tamaño o su procedencia."; + +/* No comment provided by engineer. */ +"**Scan / Paste link**: to connect via a link you received." = "**Escanear / Pegar enlace**: para conectar mediante un enlace recibido."; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Advertencia**: Las notificaciones automáticas instantáneas requieren una contraseña guardada en Keychain."; +/* No comment provided by engineer. */ +"**Warning**: the archive will be removed." = "**Atención**: el archivo será eliminado."; + /* No comment provided by engineer. */ "*bold*" = "\\*bold*"; @@ -136,6 +112,9 @@ /* No comment provided by engineer. */ "%@ connected" = "%@ conectado"; +/* No comment provided by engineer. */ +"%@ downloaded" = "%@ descargado"; + /* notification title */ "%@ is connected!" = "%@ ¡está conectado!"; @@ -146,11 +125,20 @@ "%@ is verified" = "%@ está verificado"; /* No comment provided by engineer. */ -"%@ servers" = "Servidores %@"; +"%@ server" = "%@ servidor"; + +/* No comment provided by engineer. */ +"%@ servers" = "%@ servidores"; + +/* No comment provided by engineer. */ +"%@ uploaded" = "%@ subido"; /* notification title */ "%@ wants to connect!" = "¡ %@ quiere contactar!"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + /* No comment provided by engineer. */ "%@, %@ and %lld members" = "%@, %@ y %lld miembro(s) más"; @@ -161,25 +149,43 @@ "%@:" = "%@:"; /* time interval */ -"%d days" = "%d días"; +"%d days" = "%d día(s)"; + +/* forward confirmation reason */ +"%d file(s) are still being downloaded." = "%d archivo(s) se está(n) descargando todavía."; + +/* forward confirmation reason */ +"%d file(s) failed to download." = "La descarga ha fallado para %d archivo(s)."; + +/* forward confirmation reason */ +"%d file(s) were deleted." = "%d archivo(s) ha(n) sido eliminado(s)."; + +/* forward confirmation reason */ +"%d file(s) were not downloaded." = "%d archivo(s) no se ha(n) descargado."; /* time interval */ -"%d hours" = "%d horas"; +"%d hours" = "%d hora(s)"; + +/* alert title */ +"%d messages not forwarded" = "%d mensaje(s) no enviado(s)"; /* time interval */ -"%d min" = "%d minutos"; +"%d min" = "%d minuto(s)"; /* time interval */ -"%d months" = "%d meses"; +"%d months" = "%d mes(es)"; /* time interval */ -"%d sec" = "%d segundos"; +"%d sec" = "%d segundo(s)"; + +/* delete after time */ +"%d seconds(s)" = "%d segundos"; /* integrity error chat item */ -"%d skipped message(s)" = "%d mensaje(s) saltado(s"; +"%d skipped message(s)" = "%d mensaje(s) omitido(s)"; /* time interval */ -"%d weeks" = "%d semanas"; +"%d weeks" = "%d semana(s)"; /* No comment provided by engineer. */ "%lld" = "%lld"; @@ -217,9 +223,6 @@ /* No comment provided by engineer. */ "%lld new interface languages" = "%lld idiomas de interfaz nuevos"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld segundo(s)"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld segundos"; @@ -265,7 +268,8 @@ /* No comment provided by engineer. */ "0s" = "0s"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "un dia"; /* time interval */ @@ -274,12 +278,23 @@ /* No comment provided by engineer. */ "1 minute" = "1 minuto"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "un mes"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "una semana"; +/* delete after time */ +"1 year" = "1 año"; + +/* No comment provided by engineer. */ +"1-time link" = "Enlace de un uso"; + +/* No comment provided by engineer. */ +"1-time link can be used *with one contact only* - share in person or via any messenger." = "Los enlaces de un uso pueden ser usados *solamente con un contacto* - compártelos en persona o mediante cualquier aplicación de mensajería."; + /* No comment provided by engineer. */ "5 minutes" = "5 minutos"; @@ -296,13 +311,13 @@ "A new contact" = "Contacto nuevo"; /* No comment provided by engineer. */ -"A new random profile will be shared." = "Se compartirá un perfil nuevo aleatorio."; +"A new random profile will be shared." = "Compartirás un perfil nuevo aleatorio."; /* No comment provided by engineer. */ "A separate TCP connection will be used **for each chat profile you have in the app**." = "Se usará una conexión TCP independiente **por cada perfil que tengas en la aplicación**."; /* No comment provided by engineer. */ -"A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "Se usará una conexión TCP independiente **por cada contacto y miembro de grupo**.\n**Atención**: si tienes muchas conexiones, tu consumo de batería y tráfico pueden ser sustancialmente mayores y algunas conexiones pueden fallar."; +"A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "Se usará una conexión TCP independiente **por cada contacto y miembro de grupo**.\n**Atención**: si tienes muchas conexiones, tu consumo de batería y tráfico pueden aumentar bastante y algunas conexiones pueden fallar."; /* No comment provided by engineer. */ "Abort" = "Cancelar"; @@ -314,10 +329,7 @@ "Abort changing address?" = "¿Cancelar el cambio de servidor?"; /* No comment provided by engineer. */ -"About SimpleX" = "Acerca de SimpleX"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "Acerca de la dirección SimpleX"; +"About operators" = "Acerca de los operadores"; /* No comment provided by engineer. */ "About SimpleX Chat" = "Sobre SimpleX Chat"; @@ -326,105 +338,200 @@ "above, then choose:" = "y después elige:"; /* No comment provided by engineer. */ -"Accent color" = "Color"; +"Accent" = "Color"; /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "Aceptar"; +/* No comment provided by engineer. */ +"Accept conditions" = "Aceptar condiciones"; + /* No comment provided by engineer. */ "Accept connection request?" = "¿Aceptar solicitud de conexión?"; /* notification body */ "Accept contact request from %@?" = "¿Aceptar solicitud de contacto de %@?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "Aceptar incógnito"; /* call status */ "accepted call" = "llamada aceptada"; +/* No comment provided by engineer. */ +"Accepted conditions" = "Condiciones aceptadas"; + +/* chat list item title */ +"accepted invitation" = "invitación aceptada"; + +/* No comment provided by engineer. */ +"Acknowledged" = "Confirmaciones"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Errores de confirmación"; + +/* token status text */ +"Active" = "Activo"; + +/* No comment provided by engineer. */ +"Active connections" = "Conexiones activas"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Añade la dirección a tu perfil para que tus contactos puedan compartirla con otros. La actualización del perfil se enviará a tus contactos."; /* No comment provided by engineer. */ -"Add contact" = "Añadir contacto"; +"Add friends" = "Añadir amigos"; /* No comment provided by engineer. */ -"Add preset servers" = "Añadir servidores predefinidos"; +"Add list" = "Añadir lista"; /* No comment provided by engineer. */ "Add profile" = "Añadir perfil"; /* No comment provided by engineer. */ -"Add server…" = "Añadir servidor…"; +"Add server" = "Añadir servidor"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "Añadir servidores mediante el escaneo de códigos QR."; +/* No comment provided by engineer. */ +"Add team members" = "Añadir miembros del equipo"; + /* No comment provided by engineer. */ "Add to another device" = "Añadir a otro dispositivo"; +/* No comment provided by engineer. */ +"Add to list" = "Añadir a la lista"; + /* No comment provided by engineer. */ "Add welcome message" = "Añadir mensaje de bienvenida"; +/* No comment provided by engineer. */ +"Add your team members to the conversations." = "Añade a miembros de tu equipo a las conversaciones."; + +/* No comment provided by engineer. */ +"Added media & file servers" = "Servidores de archivos y multimedia añadidos"; + +/* No comment provided by engineer. */ +"Added message servers" = "Servidores de mensajes añadidos"; + +/* No comment provided by engineer. */ +"Additional accent" = "Acento adicional"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Color adicional 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Secundario adicional"; + /* No comment provided by engineer. */ "Address" = "Dirección"; /* No comment provided by engineer. */ "Address change will be aborted. Old receiving address will be used." = "El cambio de dirección se cancelará. Se usará la antigua dirección de recepción."; +/* No comment provided by engineer. */ +"Address or 1-time link?" = "¿Dirección o enlace de un uso?"; + +/* No comment provided by engineer. */ +"Address settings" = "Configurar dirección"; + /* member role */ "admin" = "administrador"; +/* feature role */ +"admins" = "administradores"; + +/* No comment provided by engineer. */ +"Admins can block a member for all." = "Los administradores pueden bloquear a un miembro para los demás."; + /* No comment provided by engineer. */ "Admins can create the links to join groups." = "Los administradores pueden crear enlaces para unirse a grupos."; /* No comment provided by engineer. */ "Advanced network settings" = "Configuración avanzada de red"; +/* No comment provided by engineer. */ +"Advanced settings" = "Configuración avanzada"; + /* chat item text */ "agreeing encryption for %@…" = "acordando cifrado para %@…"; /* chat item text */ "agreeing encryption…" = "acordando cifrado…"; +/* No comment provided by engineer. */ +"All" = "Todo"; + /* No comment provided by engineer. */ "All app data is deleted." = "Todos los datos de la aplicación se eliminarán."; /* No comment provided by engineer. */ -"All chats and messages will be deleted - this cannot be undone!" = "Se eliminarán todos los chats y mensajes. ¡No podrá deshacerse!"; +"All chats and messages will be deleted - this cannot be undone!" = "Se eliminarán todos los chats y mensajes. ¡No puede deshacerse!"; + +/* alert message */ +"All chats will be removed from the list %@, and the list deleted." = "Todos los chats se quitarán de la lista %@ y esta será eliminada."; /* No comment provided by engineer. */ "All data is erased when it is entered." = "Al introducirlo todos los datos son eliminados."; +/* No comment provided by engineer. */ +"All data is kept private on your device." = "Todos los datos son privados y están en tu dispositivo."; + /* No comment provided by engineer. */ "All group members will remain connected." = "Todos los miembros del grupo permanecerán conectados."; -/* No comment provided by engineer. */ -"All messages will be deleted - this cannot be undone!" = "Todos los mensajes serán borrados. ¡No podrá deshacerse!"; +/* feature role */ +"all members" = "todos los miembros"; /* No comment provided by engineer. */ -"All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Se eliminarán todos los mensajes SOLO para tí. ¡No podrá deshacerse!"; +"All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages." = "Todos los mensajes y archivos son enviados **cifrados de extremo a extremo** y con seguridad de cifrado postcuántico en mensajes directos."; + +/* No comment provided by engineer. */ +"All messages will be deleted - this cannot be undone!" = "Todos los mensajes serán eliminados. ¡No puede deshacerse!"; + +/* No comment provided by engineer. */ +"All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Se eliminarán todos los mensajes SOLO para tí. ¡No puede deshacerse!"; /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "¡Los mensajes nuevos de %@ estarán ocultos!"; +/* profile dropdown */ +"All profiles" = "Todos los perfiles"; + +/* No comment provided by engineer. */ +"All reports will be archived for you." = "Todos los informes serán archivados para ti."; + +/* No comment provided by engineer. */ +"All servers" = "Todos los servidores"; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Todos tus contactos permanecerán conectados."; /* No comment provided by engineer. */ "All your contacts will remain connected. Profile update will be sent to your contacts." = "Todos tus contactos permanecerán conectados. La actualización del perfil se enviará a tus contactos."; +/* No comment provided by engineer. */ +"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Todos tus contactos, conversaciones y archivos serán cifrados, divididos y subidos de forma segura a los servidores XFTP configurados."; + /* No comment provided by engineer. */ "Allow" = "Se permite"; /* No comment provided by engineer. */ "Allow calls only if your contact allows them." = "Se permiten las llamadas pero sólo si tu contacto también las permite."; +/* No comment provided by engineer. */ +"Allow calls?" = "¿Permitir llamadas?"; + /* No comment provided by engineer. */ "Allow disappearing messages only if your contact allows it to you." = "Se permiten los mensajes temporales pero sólo si tu contacto también los permite para tí."; +/* No comment provided by engineer. */ +"Allow downgrade" = "Permitir versión anterior"; + /* No comment provided by engineer. */ "Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Se permite la eliminación irreversible de mensajes pero sólo si tu contacto también la permite para tí. (24 horas)"; @@ -440,12 +547,21 @@ /* No comment provided by engineer. */ "Allow sending disappearing messages." = "Permites el envío de mensajes temporales."; +/* No comment provided by engineer. */ +"Allow sharing" = "Permitir compartir"; + /* No comment provided by engineer. */ "Allow to irreversibly delete sent messages. (24 hours)" = "Se permite la eliminación irreversible de mensajes. (24 horas)"; +/* No comment provided by engineer. */ +"Allow to report messsages to moderators." = "Permitir informar de mensajes a los moderadores."; + /* No comment provided by engineer. */ "Allow to send files and media." = "Se permite enviar archivos y multimedia."; +/* No comment provided by engineer. */ +"Allow to send SimpleX links." = "Se permite enviar enlaces SimpleX."; + /* No comment provided by engineer. */ "Allow to send voice messages." = "Permites enviar mensajes de voz."; @@ -482,6 +598,9 @@ /* pref value */ "always" = "siempre"; +/* No comment provided by engineer. */ +"Always use private routing." = "Usar siempre enrutamiento privado."; + /* No comment provided by engineer. */ "Always use relay" = "Usar siempre retransmisor"; @@ -491,17 +610,29 @@ /* No comment provided by engineer. */ "and %lld other events" = "y %lld evento(s) más"; +/* report reason */ +"Another reason" = "Otro motivo"; + /* No comment provided by engineer. */ "Answer call" = "Responder llamada"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "Cualquiera puede alojar servidores."; + /* No comment provided by engineer. */ "App build: %@" = "Compilación app: %@"; +/* No comment provided by engineer. */ +"App data migration" = "Migrar datos de la aplicación"; + /* No comment provided by engineer. */ "App encrypts new local files (except videos)." = "Cifrado de los nuevos archivos locales (excepto vídeos)."; /* No comment provided by engineer. */ -"App icon" = "Icono aplicación"; +"App group:" = "Grupo app:"; + +/* No comment provided by engineer. */ +"App icon" = "Icono de la aplicación"; /* No comment provided by engineer. */ "App passcode" = "Código de acceso de la aplicación"; @@ -509,6 +640,9 @@ /* No comment provided by engineer. */ "App passcode is replaced with self-destruct passcode." = "El código de acceso será reemplazado por código de autodestrucción."; +/* No comment provided by engineer. */ +"App session" = "por sesión"; + /* No comment provided by engineer. */ "App version" = "Versión de la aplicación"; @@ -518,9 +652,51 @@ /* No comment provided by engineer. */ "Appearance" = "Apariencia"; +/* No comment provided by engineer. */ +"Apply" = "Aplicar"; + +/* No comment provided by engineer. */ +"Apply to" = "Aplicar a"; + +/* No comment provided by engineer. */ +"Archive" = "Archivar"; + +/* No comment provided by engineer. */ +"Archive %lld reports?" = "¿Archivar %lld informes?"; + +/* No comment provided by engineer. */ +"Archive all reports?" = "¿Archivar todos los informes?"; + +/* No comment provided by engineer. */ +"Archive and upload" = "Archivar y subir"; + +/* No comment provided by engineer. */ +"Archive contacts to chat later." = "Archiva contactos para charlar más tarde."; + +/* No comment provided by engineer. */ +"Archive report" = "Archivar informe"; + +/* No comment provided by engineer. */ +"Archive report?" = "¿Archivar informe?"; + +/* swipe action */ +"Archive reports" = "Archivar informes"; + +/* No comment provided by engineer. */ +"Archived contacts" = "Contactos archivados"; + +/* No comment provided by engineer. */ +"archived report" = "informes archivados"; + +/* No comment provided by engineer. */ +"Archiving database" = "Archivando base de datos"; + /* No comment provided by engineer. */ "Attach" = "Adjuntar"; +/* No comment provided by engineer. */ +"attempts" = "intentos"; + /* No comment provided by engineer. */ "Audio & video calls" = "Llamadas y videollamadas"; @@ -560,9 +736,15 @@ /* No comment provided by engineer. */ "Auto-accept images" = "Aceptar imágenes automáticamente"; +/* alert title */ +"Auto-accept settings" = "Auto aceptar configuración"; + /* No comment provided by engineer. */ "Back" = "Volver"; +/* No comment provided by engineer. */ +"Background" = "Fondo"; + /* No comment provided by engineer. */ "Bad desktop address" = "Dirección ordenador incorrecta"; @@ -578,12 +760,39 @@ /* No comment provided by engineer. */ "Bad message ID" = "ID de mensaje incorrecto"; +/* No comment provided by engineer. */ +"Better calls" = "Llamadas mejoradas"; + /* No comment provided by engineer. */ "Better groups" = "Grupos mejorados"; +/* No comment provided by engineer. */ +"Better groups performance" = "Rendimiento de grupos mejorado"; + +/* No comment provided by engineer. */ +"Better message dates." = "Sistema de fechas mejorado."; + /* No comment provided by engineer. */ "Better messages" = "Mensajes mejorados"; +/* No comment provided by engineer. */ +"Better networking" = "Uso de red mejorado"; + +/* No comment provided by engineer. */ +"Better notifications" = "Notificaciones mejoradas"; + +/* No comment provided by engineer. */ +"Better privacy and security" = "Privacidad y seguridad mejoradas"; + +/* No comment provided by engineer. */ +"Better security ✅" = "Seguridad mejorada ✅"; + +/* No comment provided by engineer. */ +"Better user experience" = "Experiencia de usuario mejorada"; + +/* No comment provided by engineer. */ +"Black" = "Negro"; + /* No comment provided by engineer. */ "Block" = "Bloquear"; @@ -606,13 +815,20 @@ "blocked" = "bloqueado"; /* rcv group event chat item */ -"blocked %@" = "%@ bloqueado"; +"blocked %@" = "ha bloqueado a %@"; -/* marked deleted chat item preview text */ -"blocked by admin" = "bloqueado por el administrador"; +/* blocked chat item +marked deleted chat item preview text */ +"blocked by admin" = "bloqueado por administrador"; /* No comment provided by engineer. */ -"Blocked by admin" = "Bloqueado por el administrador"; +"Blocked by admin" = "Bloqueado por administrador"; + +/* No comment provided by engineer. */ +"Blur for better privacy." = "Difumina para mayor privacidad."; + +/* No comment provided by engineer. */ +"Blur media" = "Difuminar multimedia"; /* No comment provided by engineer. */ "bold" = "negrita"; @@ -636,7 +852,22 @@ "Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Búlgaro, Finlandés, Tailandés y Ucraniano - gracias a los usuarios y [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"; /* No comment provided by engineer. */ -"By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Mediante perfil (por defecto) o [por conexión](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)."; +"Business address" = "Dirección empresarial"; + +/* No comment provided by engineer. */ +"Business chats" = "Chats empresariales"; + +/* No comment provided by engineer. */ +"Businesses" = "Empresas"; + +/* No comment provided by engineer. */ +"By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Mediante perfil (predeterminado) o [por conexión](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)."; + +/* No comment provided by engineer. */ +"By using SimpleX Chat you agree to:\n- send only legal content in public groups.\n- respect other users – no spam." = "Al usar SimpleX Chat, aceptas:\n- enviar únicamente contenido legal en los grupos públicos.\n- respetar a los demás usuarios – spam prohibido."; + +/* No comment provided by engineer. */ +"call" = "llamada"; /* No comment provided by engineer. */ "Call already ended!" = "¡La llamada ha terminado!"; @@ -653,9 +884,18 @@ /* No comment provided by engineer. */ "Calls" = "Llamadas"; +/* No comment provided by engineer. */ +"Calls prohibited!" = "¡Llamadas no permitidas!"; + /* No comment provided by engineer. */ "Camera not available" = "Cámara no disponible"; +/* No comment provided by engineer. */ +"Can't call contact" = "No se puede llamar al contacto"; + +/* No comment provided by engineer. */ +"Can't call member" = "No se puede llamar al miembro"; + /* No comment provided by engineer. */ "Can't invite contact!" = "¡No se puede invitar el contacto!"; @@ -663,8 +903,15 @@ "Can't invite contacts!" = "¡No se pueden invitar contactos!"; /* No comment provided by engineer. */ +"Can't message member" = "No se pueden enviar mensajes al miembro"; + +/* alert action +alert button */ "Cancel" = "Cancelar"; +/* No comment provided by engineer. */ +"Cancel migration" = "Cancelar migración"; + /* feature offered item */ "cancelled %@" = "cancelado %@"; @@ -672,11 +919,26 @@ "Cannot access keychain to save database password" = "Keychain inaccesible para guardar la contraseña de la base de datos"; /* No comment provided by engineer. */ +"Cannot forward message" = "No se puede reenviar el mensaje"; + +/* alert title */ "Cannot receive file" = "No se puede recibir el archivo"; +/* snd error text */ +"Capacity exceeded - recipient did not receive previously sent messages." = "Capacidad excedida - el destinatario no ha recibido los mensajes previos."; + +/* No comment provided by engineer. */ +"Cellular" = "Móvil"; + /* No comment provided by engineer. */ "Change" = "Cambiar"; +/* alert title */ +"Change automatic message deletion?" = "¿Modificar la eliminación automática de mensajes?"; + +/* authentication reason */ +"Change chat profiles" = "Cambiar perfil de usuario"; + /* No comment provided by engineer. */ "Change database passphrase?" = "¿Cambiar contraseña de la base de datos?"; @@ -702,7 +964,7 @@ "Change self-destruct mode" = "Cambiar el modo de autodestrucción"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "Cambiar código autodestrucción"; /* chat item text */ @@ -721,41 +983,83 @@ "changing address…" = "cambiando de servidor…"; /* No comment provided by engineer. */ -"Chat archive" = "Archivo del chat"; +"Chat" = "Chat"; + +/* No comment provided by engineer. */ +"Chat already exists" = "El chat ya existe"; + +/* No comment provided by engineer. */ +"Chat already exists!" = "¡El chat ya existe!"; + +/* No comment provided by engineer. */ +"Chat colors" = "Colores del chat"; /* No comment provided by engineer. */ "Chat console" = "Consola de Chat"; /* No comment provided by engineer. */ -"Chat database" = "Base de datos del chat"; +"Chat database" = "Base de datos de SimpleX"; /* No comment provided by engineer. */ "Chat database deleted" = "Base de datos eliminada"; +/* No comment provided by engineer. */ +"Chat database exported" = "Base de datos exportada"; + /* No comment provided by engineer. */ "Chat database imported" = "Base de datos importada"; /* No comment provided by engineer. */ -"Chat is running" = "Chat está en ejecución"; +"Chat is running" = "SimpleX está en ejecución"; /* No comment provided by engineer. */ -"Chat is stopped" = "Chat está detenido"; +"Chat is stopped" = "SimpleX está parado"; /* No comment provided by engineer. */ -"Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "Chat está detenido. Si estás usando esta base de datos en otro dispositivo, deberías transferirla de vuelta antes de iniciarlo."; +"Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "SimpleX está parado. Si has usado esta base de datos en otro dispositivo, debes transferirla de vuelta antes de iniciar SimpleX."; + +/* No comment provided by engineer. */ +"Chat list" = "Lista de chats"; + +/* No comment provided by engineer. */ +"Chat migrated!" = "¡Chat migrado!"; /* No comment provided by engineer. */ "Chat preferences" = "Preferencias de Chat"; +/* alert message */ +"Chat preferences were changed." = "Las preferencias del chat han sido modificadas."; + +/* No comment provided by engineer. */ +"Chat profile" = "Perfil de usuario"; + +/* No comment provided by engineer. */ +"Chat theme" = "Tema de chat"; + +/* No comment provided by engineer. */ +"Chat will be deleted for all members - this cannot be undone!" = "El chat será eliminado para todos los miembros. ¡No puede deshacerse!"; + +/* No comment provided by engineer. */ +"Chat will be deleted for you - this cannot be undone!" = "El chat será eliminado para tí. ¡No puede deshacerse!"; + /* No comment provided by engineer. */ "Chats" = "Chats"; /* No comment provided by engineer. */ +"Check messages every 20 min." = "Comprobar mensajes cada 20 min."; + +/* No comment provided by engineer. */ +"Check messages when allowed." = "Comprobar mensajes cuando se permita."; + +/* alert title */ "Check server address and try again." = "Comprueba la dirección del servidor e inténtalo de nuevo."; /* No comment provided by engineer. */ "Chinese and Spanish interface" = "Interfaz en chino y español"; +/* No comment provided by engineer. */ +"Choose _Migrate from another device_ on the new device and scan QR code." = "En el nuevo dispositivo selecciona _Migrar desde otro dispositivo_ y escanéa el código QR."; + /* No comment provided by engineer. */ "Choose file" = "Elije archivo"; @@ -763,6 +1067,15 @@ "Choose from library" = "Elige de la biblioteca"; /* No comment provided by engineer. */ +"Chunks deleted" = "Bloques eliminados"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "Bloques descargados"; + +/* No comment provided by engineer. */ +"Chunks uploaded" = "Bloques subidos"; + +/* swipe action */ "Clear" = "Vaciar"; /* No comment provided by engineer. */ @@ -772,16 +1085,28 @@ "Clear conversation?" = "¿Vaciar conversación?"; /* No comment provided by engineer. */ -"Clear private notes?" = "¿Borrar notas privadas?"; +"Clear group?" = "¿Vaciar grupo?"; + +/* No comment provided by engineer. */ +"Clear or delete group?" = "¿Vaciar o eliminar grupo?"; + +/* No comment provided by engineer. */ +"Clear private notes?" = "¿Eliminar notas privadas?"; /* No comment provided by engineer. */ "Clear verification" = "Eliminar verificación"; /* No comment provided by engineer. */ -"colored" = "coloreado"; +"Color chats with the new themes." = "Colorea los chats con los nuevos temas."; /* No comment provided by engineer. */ -"Colors" = "Colores"; +"Color mode" = "Modo de color"; + +/* No comment provided by engineer. */ +"colored" = "coloreado"; + +/* report reason */ +"Community guidelines violation" = "Violación de las normas de la comunidad"; /* server test step */ "Compare file" = "Comparar archivo"; @@ -792,15 +1117,51 @@ /* No comment provided by engineer. */ "complete" = "completado"; +/* No comment provided by engineer. */ +"Completed" = "Completadas"; + +/* No comment provided by engineer. */ +"Conditions accepted on: %@." = "Condiciones aceptadas el: %@."; + +/* No comment provided by engineer. */ +"Conditions are accepted for the operator(s): **%@**." = "Las condiciones se han aceptado para el(los) operador(s): **%@**."; + +/* No comment provided by engineer. */ +"Conditions are already accepted for these operator(s): **%@**." = "Las condiciones ya se han aceptado para el/los siguiente(s) operador(s): **%@**."; + +/* No comment provided by engineer. */ +"Conditions of use" = "Condiciones de uso"; + +/* No comment provided by engineer. */ +"Conditions will be accepted for the operator(s): **%@**." = "Las condiciones serán aceptadas para el/los operador(es): **%@**."; + +/* No comment provided by engineer. */ +"Conditions will be accepted on: %@." = "Las condiciones serán aceptadas el: %@."; + +/* No comment provided by engineer. */ +"Conditions will be automatically accepted for enabled operators on: %@." = "Las condiciones serán aceptadas automáticamente para los operadores habilitados el: %@."; + /* No comment provided by engineer. */ "Configure ICE servers" = "Configure servidores ICE"; +/* No comment provided by engineer. */ +"Configure server operators" = "Configurar operadores de servidores"; + /* No comment provided by engineer. */ "Confirm" = "Confirmar"; +/* No comment provided by engineer. */ +"Confirm contact deletion?" = "¿Confirmas la eliminación del contacto?"; + /* No comment provided by engineer. */ "Confirm database upgrades" = "Confirmar actualizaciones de la bases de datos"; +/* No comment provided by engineer. */ +"Confirm files from unknown servers." = "Confirma archivos de servidores desconocidos."; + +/* No comment provided by engineer. */ +"Confirm network settings" = "Confirmar configuración de red"; + /* No comment provided by engineer. */ "Confirm new passphrase…" = "Confirme nueva contraseña…"; @@ -810,6 +1171,15 @@ /* No comment provided by engineer. */ "Confirm password" = "Confirmar contraseña"; +/* No comment provided by engineer. */ +"Confirm that you remember database passphrase to migrate it." = "Para migrar la base de datos confirma que recuerdas la frase de contraseña."; + +/* No comment provided by engineer. */ +"Confirm upload" = "Confirmar subida"; + +/* token status text */ +"Confirmed" = "Confirmado"; + /* server test step */ "Connect" = "Conectar"; @@ -825,6 +1195,9 @@ /* No comment provided by engineer. */ "connect to SimpleX Chat developers." = "contacta con los desarrolladores de SimpleX Chat."; +/* No comment provided by engineer. */ +"Connect to your friends faster." = "Conecta más rápido con tus amigos."; + /* No comment provided by engineer. */ "Connect to yourself?" = "¿Conectarte a tí mismo?"; @@ -849,17 +1222,26 @@ /* No comment provided by engineer. */ "connected" = "conectado"; +/* No comment provided by engineer. */ +"Connected" = "Conectadas"; + /* No comment provided by engineer. */ "Connected desktop" = "Ordenador conectado"; /* rcv group event chat item */ "connected directly" = "conectado directamente"; +/* No comment provided by engineer. */ +"Connected servers" = "Servidores conectados"; + /* No comment provided by engineer. */ "Connected to desktop" = "Conectado con ordenador"; /* No comment provided by engineer. */ -"connecting" = "conectando"; +"connecting" = "conectando..."; + +/* No comment provided by engineer. */ +"Connecting" = "Conectando"; /* No comment provided by engineer. */ "connecting (accepted)" = "conectando (aceptado)"; @@ -882,36 +1264,66 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "Conectando con el servidor... (error: %@)"; +/* No comment provided by engineer. */ +"Connecting to contact, please wait or check later!" = "Conectando con el contacto, por favor espera o revisa más tarde."; + /* No comment provided by engineer. */ "Connecting to desktop" = "Conectando con ordenador"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "conectando…"; /* No comment provided by engineer. */ "Connection" = "Conexión"; +/* No comment provided by engineer. */ +"Connection and servers status." = "Estado de tu conexión y servidores."; + +/* No comment provided by engineer. */ +"Connection blocked" = "Conexión bloqueada"; + /* No comment provided by engineer. */ "Connection error" = "Error conexión"; /* No comment provided by engineer. */ -"Connection error (AUTH)" = "Error conexión (Autenticación)"; +"Connection error (AUTH)" = "Error de conexión (Autenticación)"; /* chat list item title (it should not be shown */ "connection established" = "conexión establecida"; +/* No comment provided by engineer. */ +"Connection is blocked by server operator:\n%@" = "Conexión bloqueada por el operador del servidor:\n%@"; + +/* No comment provided by engineer. */ +"Connection not ready." = "Conexión no establecida."; + +/* No comment provided by engineer. */ +"Connection notifications" = "Notificaciones de conexión"; + /* No comment provided by engineer. */ "Connection request sent!" = "¡Solicitud de conexión enviada!"; +/* No comment provided by engineer. */ +"Connection requires encryption renegotiation." = "La conexión requiere renegociar el cifrado."; + +/* No comment provided by engineer. */ +"Connection security" = "Seguridad de conexión"; + /* No comment provided by engineer. */ "Connection terminated" = "Conexión finalizada"; /* No comment provided by engineer. */ -"Connection timeout" = "Tiempo de conexión expirado"; +"Connection timeout" = "Tiempo de conexión agotado"; + +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "La conexión con el escritorio (desktop) se ha parado"; /* connection information */ "connection:%@" = "conexión: % @"; +/* No comment provided by engineer. */ +"Connections" = "Conexiones"; + /* profile update event chat item */ "contact %@ changed to %@" = "el contacto %1$@ ha cambiado a %2$@"; @@ -921,6 +1333,9 @@ /* No comment provided by engineer. */ "Contact already exists" = "El contácto ya existe"; +/* No comment provided by engineer. */ +"Contact deleted!" = "¡Contacto eliminado!"; + /* No comment provided by engineer. */ "contact has e2e encryption" = "el contacto dispone de cifrado de extremo a extremo"; @@ -934,7 +1349,7 @@ "Contact is connected" = "El contacto está en línea"; /* No comment provided by engineer. */ -"Contact is not connected yet!" = "¡El contacto aun no se ha conectado!"; +"Contact is deleted." = "El contacto está eliminado."; /* No comment provided by engineer. */ "Contact name" = "Contacto"; @@ -942,21 +1357,36 @@ /* No comment provided by engineer. */ "Contact preferences" = "Preferencias de contacto"; +/* No comment provided by engineer. */ +"Contact will be deleted - this cannot be undone!" = "El contacto será eliminado. ¡No puede deshacerse!"; + /* No comment provided by engineer. */ "Contacts" = "Contactos"; /* No comment provided by engineer. */ "Contacts can mark messages for deletion; you will be able to view them." = "Tus contactos sólo pueden marcar los mensajes para eliminar. Tu podrás verlos."; +/* blocking reason */ +"Content violates conditions of use" = "El contenido viola las condiciones de uso"; + /* No comment provided by engineer. */ "Continue" = "Continuar"; -/* chat item action */ +/* No comment provided by engineer. */ +"Conversation deleted!" = "¡Conversación eliminada!"; + +/* No comment provided by engineer. */ "Copy" = "Copiar"; +/* No comment provided by engineer. */ +"Copy error" = "Copiar error"; + /* No comment provided by engineer. */ "Core version: v%@" = "Versión Core: v%@"; +/* No comment provided by engineer. */ +"Corner" = "Esquina"; + /* No comment provided by engineer. */ "Correct name to %@?" = "¿Corregir el nombre a %@?"; @@ -964,10 +1394,10 @@ "Create" = "Crear"; /* No comment provided by engineer. */ -"Create a group using a random profile." = "Crear grupo usando perfil aleatorio."; +"Create 1-time link" = "Crear enlace de un uso"; /* No comment provided by engineer. */ -"Create an address to let people connect with you." = "Crea una dirección para que otras personas puedan conectar contigo."; +"Create a group using a random profile." = "Crear grupo usando perfil aleatorio."; /* server test step */ "Create file" = "Crear archivo"; @@ -981,6 +1411,9 @@ /* No comment provided by engineer. */ "Create link" = "Crear enlace"; +/* No comment provided by engineer. */ +"Create list" = "Crear lista"; + /* No comment provided by engineer. */ "Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" = "Crea perfil nuevo en la [aplicación para PC](https://simplex.Descargas/de chat/). 💻"; @@ -999,6 +1432,9 @@ /* No comment provided by engineer. */ "Create your profile" = "Crea tu perfil"; +/* No comment provided by engineer. */ +"Created" = "Creadas"; + /* No comment provided by engineer. */ "Created at" = "Creado"; @@ -1006,7 +1442,7 @@ "Created at: %@" = "Creado: %@"; /* No comment provided by engineer. */ -"Created on %@" = "Creado en %@"; +"Creating archive link" = "Creando enlace al archivo"; /* No comment provided by engineer. */ "Creating link…" = "Creando enlace…"; @@ -1014,12 +1450,18 @@ /* No comment provided by engineer. */ "creator" = "creador"; +/* No comment provided by engineer. */ +"Current conditions text couldn't be loaded, you can review conditions via this link:" = "El texto con las condiciones actuales no se ha podido cargar, puedes revisar las condiciones en el siguiente enlace:"; + /* No comment provided by engineer. */ "Current Passcode" = "Código de Acceso"; /* No comment provided by engineer. */ "Current passphrase…" = "Contraseña actual…"; +/* No comment provided by engineer. */ +"Current profile" = "Perfil actual"; + /* No comment provided by engineer. */ "Currently maximum supported file size is %@." = "El tamaño máximo de archivo admitido es %@."; @@ -1029,9 +1471,18 @@ /* No comment provided by engineer. */ "Custom time" = "Tiempo personalizado"; +/* No comment provided by engineer. */ +"Customizable message shape." = "Forma personalizable de los mensajes."; + +/* No comment provided by engineer. */ +"Customize theme" = "Personalizar tema"; + /* No comment provided by engineer. */ "Dark" = "Oscuro"; +/* No comment provided by engineer. */ +"Dark mode colors" = "Colores en modo oscuro"; + /* No comment provided by engineer. */ "Database downgrade" = "Degradación de la base de datos"; @@ -1069,7 +1520,7 @@ "Database passphrase & export" = "Base de datos y contraseña"; /* No comment provided by engineer. */ -"Database passphrase is different from saved in the keychain." = "La contraseña es distinta a la almacenada en Keychain."; +"Database passphrase is different from saved in the keychain." = "La contraseña es diferente a la almacenada en Keychain."; /* No comment provided by engineer. */ "Database passphrase is required to open chat." = "Para abrir la aplicación se requiere la contraseña de la base de datos."; @@ -1092,26 +1543,37 @@ /* time unit */ "days" = "días"; +/* No comment provided by engineer. */ +"Debug delivery" = "Informe debug"; + /* No comment provided by engineer. */ "Decentralized" = "Descentralizada"; /* message decrypt error item */ "Decryption error" = "Error descifrado"; -/* pref value */ -"default (%@)" = "por defecto (%@)"; +/* No comment provided by engineer. */ +"decryption errors" = "errores de descifrado"; + +/* delete after time +pref value */ +"default (%@)" = "predeterminado (%@)"; /* No comment provided by engineer. */ -"default (no)" = "por defecto (no)"; +"default (no)" = "predeterminado (no)"; /* No comment provided by engineer. */ -"default (yes)" = "por defecto (sí)"; +"default (yes)" = "predeterminado (sí)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "Eliminar"; /* No comment provided by engineer. */ -"Delete %lld messages?" = "¿Elimina %lld mensajes?"; +"Delete %lld messages of members?" = "¿Eliminar %lld mensajes de miembros?"; + +/* No comment provided by engineer. */ +"Delete %lld messages?" = "¿Eliminar %lld mensajes?"; /* No comment provided by engineer. */ "Delete address" = "Eliminar dirección"; @@ -1129,16 +1591,19 @@ "Delete and notify contact" = "Eliminar y notificar contacto"; /* No comment provided by engineer. */ -"Delete archive" = "Eliminar archivo"; +"Delete chat" = "Eliminar chat"; /* No comment provided by engineer. */ -"Delete chat archive?" = "¿Eliminar archivo del chat?"; +"Delete chat messages from your device." = "Elimina los mensajes del dispositivo."; /* No comment provided by engineer. */ "Delete chat profile" = "Eliminar perfil"; /* No comment provided by engineer. */ -"Delete chat profile?" = "¿Eliminar el perfil?"; +"Delete chat profile?" = "¿Eliminar perfil?"; + +/* No comment provided by engineer. */ +"Delete chat?" = "¿Eliminar chat?"; /* No comment provided by engineer. */ "Delete connection" = "Eliminar conexión"; @@ -1147,19 +1612,19 @@ "Delete contact" = "Eliminar contacto"; /* No comment provided by engineer. */ -"Delete Contact" = "Eliminar contacto"; - -/* No comment provided by engineer. */ -"Delete contact?\nThis cannot be undone!" = "¿Eliminar contacto?\n¡No podrá deshacerse!"; +"Delete contact?" = "¿Eliminar contacto?"; /* No comment provided by engineer. */ "Delete database" = "Eliminar base de datos"; +/* No comment provided by engineer. */ +"Delete database from this device" = "Eliminar base de datos de este dispositivo"; + /* server test step */ "Delete file" = "Eliminar archivo"; /* No comment provided by engineer. */ -"Delete files and media?" = "Eliminar archivos y multimedia?"; +"Delete files and media?" = "¿Eliminar archivos y multimedia?"; /* No comment provided by engineer. */ "Delete files for all chat profiles" = "Eliminar archivos de todos los perfiles"; @@ -1185,14 +1650,17 @@ /* No comment provided by engineer. */ "Delete link?" = "¿Eliminar enlace?"; +/* alert title */ +"Delete list?" = "¿Eliminar lista?"; + /* No comment provided by engineer. */ "Delete member message?" = "¿Eliminar el mensaje de miembro?"; /* No comment provided by engineer. */ "Delete message?" = "¿Eliminar mensaje?"; -/* No comment provided by engineer. */ -"Delete messages" = "Eliminar mensaje"; +/* alert button */ +"Delete messages" = "Activar"; /* No comment provided by engineer. */ "Delete messages after" = "Eliminar en"; @@ -1204,7 +1672,7 @@ "Delete old database?" = "¿Eliminar base de datos antigua?"; /* No comment provided by engineer. */ -"Delete pending connection" = "Eliminar conexión pendiente"; +"Delete or moderate up to 200 messages." = "Elimina o modera hasta 200 mensajes a la vez."; /* No comment provided by engineer. */ "Delete pending connection?" = "¿Eliminar conexión pendiente?"; @@ -1215,12 +1683,24 @@ /* server test step */ "Delete queue" = "Eliminar cola"; +/* No comment provided by engineer. */ +"Delete report" = "Eliminar informe"; + +/* No comment provided by engineer. */ +"Delete up to 20 messages at once." = "Elimina hasta 20 mensajes a la vez."; + /* No comment provided by engineer. */ "Delete user profile?" = "¿Eliminar perfil de usuario?"; +/* No comment provided by engineer. */ +"Delete without notification" = "Elimina sin notificar"; + /* deleted chat item */ "deleted" = "eliminado"; +/* No comment provided by engineer. */ +"Deleted" = "Eliminadas"; + /* No comment provided by engineer. */ "Deleted at" = "Eliminado"; @@ -1231,7 +1711,13 @@ "deleted contact" = "contacto eliminado"; /* rcv group event chat item */ -"deleted group" = "grupo eliminado"; +"deleted group" = "ha eliminado el grupo"; + +/* No comment provided by engineer. */ +"Deletion errors" = "Errores de eliminación"; + +/* No comment provided by engineer. */ +"Delivered even when Apple drops them." = "Entregados incluso cuando Apple los descarta."; /* No comment provided by engineer. */ "Delivery" = "Entrega"; @@ -1254,9 +1740,27 @@ /* No comment provided by engineer. */ "Desktop devices" = "Ordenadores"; +/* No comment provided by engineer. */ +"Destination server address of %@ is incompatible with forwarding server %@ settings." = "La dirección del servidor de destino de %@ es incompatible con la configuración del servidor de reenvío %@."; + +/* snd error text */ +"Destination server error: %@" = "Error del servidor de destino: %@"; + +/* No comment provided by engineer. */ +"Destination server version of %@ is incompatible with forwarding server %@." = "La versión del servidor de destino de %@ es incompatible con el servidor de reenvío %@."; + +/* No comment provided by engineer. */ +"Detailed statistics" = "Estadísticas detalladas"; + +/* No comment provided by engineer. */ +"Details" = "Detalles"; + /* No comment provided by engineer. */ "Develop" = "Desarrollo"; +/* No comment provided by engineer. */ +"Developer options" = "Opciones desarrollador"; + /* No comment provided by engineer. */ "Developer tools" = "Herramientas desarrollo"; @@ -1282,11 +1786,20 @@ "Direct messages" = "Mensajes directos"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "Los mensajes directos entre miembros del grupo no están permitidos."; +"Direct messages between members are prohibited in this chat." = "Mensajes directos no permitidos entre miembros de este chat."; + +/* No comment provided by engineer. */ +"Direct messages between members are prohibited." = "Los mensajes directos entre miembros del grupo no están permitidos."; /* No comment provided by engineer. */ "Disable (keep overrides)" = "Desactivar (conservando anulaciones)"; +/* alert title */ +"Disable automatic message deletion?" = "¿Desactivar la eliminación automática de mensajes?"; + +/* alert button */ +"Disable delete messages" = "Desactivar"; + /* No comment provided by engineer. */ "Disable for all" = "Desactivar para todos"; @@ -1296,6 +1809,9 @@ /* No comment provided by engineer. */ "disabled" = "desactivado"; +/* No comment provided by engineer. */ +"Disabled" = "Desactivado"; + /* No comment provided by engineer. */ "Disappearing message" = "Mensaje temporal"; @@ -1306,7 +1822,7 @@ "Disappearing messages are prohibited in this chat." = "Los mensajes temporales no están permitidos en este chat."; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "Los mensajes temporales no están permitidos en este grupo."; +"Disappearing messages are prohibited." = "Los mensajes temporales no están permitidos en este grupo."; /* No comment provided by engineer. */ "Disappears at" = "Desaparecerá"; @@ -1318,7 +1834,7 @@ "Disconnect" = "Desconectar"; /* No comment provided by engineer. */ -"Disconnect desktop?" = "¿Desconectar ordenador?"; +"Disconnect desktop?" = "¿Desconectar del ordenador?"; /* No comment provided by engineer. */ "Discover and join groups" = "Descubre y únete a grupos"; @@ -1330,11 +1846,23 @@ "Do it later" = "Hacer más tarde"; /* No comment provided by engineer. */ -"Do not send history to new members." = "No enviar historial a miembros nuevos."; +"Do not send history to new members." = "No se envía el historial a los miembros nuevos."; + +/* No comment provided by engineer. */ +"Do NOT send messages directly, even if your or destination server does not support private routing." = "NO enviar mensajes directamente incluso si tu servidor o el de destino no soportan enrutamiento privado."; + +/* No comment provided by engineer. */ +"Do not use credentials with proxy." = "No uses credenciales con proxy."; + +/* No comment provided by engineer. */ +"Do NOT use private routing." = "NO usar enrutamiento privado."; /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "NO uses SimpleX para llamadas de emergencia."; +/* No comment provided by engineer. */ +"Documents:" = "Documentos:"; + /* No comment provided by engineer. */ "Don't create address" = "No crear dirección SimpleX"; @@ -1342,26 +1870,63 @@ "Don't enable" = "No activar"; /* No comment provided by engineer. */ -"Don't show again" = "No mostrar de nuevo"; +"Don't miss important messages." = "No pierdas los mensajes importantes."; + +/* No comment provided by engineer. */ +"Don't show again" = "No volver a mostrar"; + +/* No comment provided by engineer. */ +"Done" = "Hecho"; /* No comment provided by engineer. */ "Downgrade and open chat" = "Degradar y abrir Chat"; +/* alert button +chat item action */ +"Download" = "Descargar"; + +/* No comment provided by engineer. */ +"Download errors" = "Errores en la descarga"; + +/* No comment provided by engineer. */ +"Download failed" = "Descarga fallida"; + /* server test step */ "Download file" = "Descargar archivo"; +/* alert action */ +"Download files" = "Descargar archivos"; + +/* No comment provided by engineer. */ +"Downloaded" = "Descargado"; + +/* No comment provided by engineer. */ +"Downloaded files" = "Archivos descargados"; + +/* No comment provided by engineer. */ +"Downloading archive" = "Descargando archivo"; + +/* No comment provided by engineer. */ +"Downloading link details" = "Descargando detalles del enlace"; + /* No comment provided by engineer. */ "Duplicate display name!" = "¡Nombre mostrado duplicado!"; /* integrity error chat item */ "duplicate message" = "mensaje duplicado"; +/* No comment provided by engineer. */ +"duplicates" = "duplicados"; + /* No comment provided by engineer. */ "Duration" = "Duración"; /* No comment provided by engineer. */ "e2e encrypted" = "cifrado de extremo a extremo"; +/* No comment provided by engineer. */ +"E2E encrypted notifications." = "Notificaciones cifradas E2E."; + /* chat item action */ "Edit" = "Editar"; @@ -1374,15 +1939,21 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "Activar (conservar anulaciones)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "¿Activar eliminación automática de mensajes?"; /* No comment provided by engineer. */ "Enable camera access" = "Permitir acceso a la cámara"; +/* No comment provided by engineer. */ +"Enable Flux in Network & servers settings for better metadata privacy." = "Habilitar Flux en la configuración de Red y servidores para mejorar la privacidad de los metadatos."; + /* No comment provided by engineer. */ "Enable for all" = "Activar para todos"; +/* No comment provided by engineer. */ +"Enable in direct chats (BETA)!" = "¡Activar en chats directos (BETA)!"; + /* No comment provided by engineer. */ "Enable instant notifications?" = "¿Activar notificación instantánea?"; @@ -1410,6 +1981,12 @@ /* enabled status */ "enabled" = "activado"; +/* No comment provided by engineer. */ +"Enabled" = "Activado"; + +/* No comment provided by engineer. */ +"Enabled for" = "Activado para"; + /* enabled status */ "enabled for contact" = "activado para el contacto"; @@ -1423,7 +2000,7 @@ "Encrypt database?" = "¿Cifrar base de datos?"; /* No comment provided by engineer. */ -"Encrypt local files" = "Cifra archivos locales"; +"Encrypt local files" = "Cifrar archivos locales"; /* No comment provided by engineer. */ "Encrypt stored files & media" = "Cifra archivos almacenados y multimedia"; @@ -1482,6 +2059,9 @@ /* chat item text */ "encryption re-negotiation required for %@" = "se requiere renegociar el cifrado para %@"; +/* No comment provided by engineer. */ +"Encryption renegotiation in progress." = "Renegociación de cifrado en curso."; + /* No comment provided by engineer. */ "ended" = "finalizado"; @@ -1497,6 +2077,9 @@ /* No comment provided by engineer. */ "Enter Passcode" = "Introduce Código"; +/* No comment provided by engineer. */ +"Enter passphrase" = "Introduce la frase de contraseña"; + /* No comment provided by engineer. */ "Enter passphrase…" = "Introduce la contraseña…"; @@ -1504,7 +2087,7 @@ "Enter password above to show!" = "¡Introduce la contraseña arriba para mostrar!"; /* No comment provided by engineer. */ -"Enter server manually" = "Introduce el servidor manualmente"; +"Enter server manually" = "Añadir manualmente"; /* No comment provided by engineer. */ "Enter this device name…" = "Nombre de este dispositivo…"; @@ -1527,24 +2110,39 @@ /* No comment provided by engineer. */ "Error aborting address change" = "Error al cancelar cambio de dirección"; +/* alert title */ +"Error accepting conditions" = "Error al aceptar las condiciones"; + /* No comment provided by engineer. */ "Error accepting contact request" = "Error al aceptar solicitud del contacto"; -/* No comment provided by engineer. */ -"Error accessing database file" = "Error al acceder al archivo de la base de datos"; - /* No comment provided by engineer. */ "Error adding member(s)" = "Error al añadir miembro(s)"; +/* alert title */ +"Error adding server" = "Error al añadir servidor"; + /* No comment provided by engineer. */ "Error changing address" = "Error al cambiar servidor"; +/* No comment provided by engineer. */ +"Error changing connection profile" = "Error al cambiar el perfil de conexión"; + /* No comment provided by engineer. */ "Error changing role" = "Error al cambiar rol"; /* No comment provided by engineer. */ "Error changing setting" = "Error cambiando configuración"; +/* No comment provided by engineer. */ +"Error changing to incognito!" = "¡Error al cambiar a incógnito!"; + +/* No comment provided by engineer. */ +"Error checking token status" = "Error al verificar el estado del token"; + +/* No comment provided by engineer. */ +"Error connecting to forwarding server %@. Please try later." = "Error al conectar con el servidor de reenvío %@. Por favor, inténtalo más tarde."; + /* No comment provided by engineer. */ "Error creating address" = "Error al crear dirección"; @@ -1554,6 +2152,9 @@ /* No comment provided by engineer. */ "Error creating group link" = "Error al crear enlace de grupo"; +/* alert title */ +"Error creating list" = "Error al crear lista"; + /* No comment provided by engineer. */ "Error creating member contact" = "Error al establecer contacto con el miembro"; @@ -1563,6 +2164,9 @@ /* No comment provided by engineer. */ "Error creating profile!" = "¡Error al crear perfil!"; +/* No comment provided by engineer. */ +"Error creating report" = "Error al crear informe"; + /* No comment provided by engineer. */ "Error decrypting file" = "Error al descifrar el archivo"; @@ -1575,9 +2179,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "Error al eliminar conexión"; -/* No comment provided by engineer. */ -"Error deleting contact" = "Error al eliminar contacto"; - /* No comment provided by engineer. */ "Error deleting database" = "Error al eliminar base de datos"; @@ -1590,6 +2191,9 @@ /* No comment provided by engineer. */ "Error deleting user profile" = "Error al eliminar perfil"; +/* No comment provided by engineer. */ +"Error downloading the archive" = "Error al descargar el archivo"; + /* No comment provided by engineer. */ "Error enabling delivery receipts!" = "¡Error al activar confirmaciones de entrega!"; @@ -1602,26 +2206,47 @@ /* No comment provided by engineer. */ "Error exporting chat database" = "Error al exportar base de datos"; +/* No comment provided by engineer. */ +"Error exporting theme: %@" = "Error al exportar tema: %@"; + /* No comment provided by engineer. */ "Error importing chat database" = "Error al importar base de datos"; /* No comment provided by engineer. */ "Error joining group" = "Error al unirte al grupo"; +/* alert title */ +"Error loading servers" = "Error al cargar servidores"; + /* No comment provided by engineer. */ -"Error loading %@ servers" = "Error al cargar servidores %@"; +"Error migrating settings" = "Error al migrar la configuración"; /* No comment provided by engineer. */ "Error opening chat" = "Error al abrir chat"; -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Error al recibir archivo"; /* No comment provided by engineer. */ -"Error removing member" = "Error al eliminar miembro"; +"Error reconnecting server" = "Error al reconectar con el servidor"; /* No comment provided by engineer. */ -"Error saving %@ servers" = "Error al guardar servidores %@"; +"Error reconnecting servers" = "Error al reconectar con los servidores"; + +/* alert title */ +"Error registering for notifications" = "Error al registrarse para notificaciones"; + +/* No comment provided by engineer. */ +"Error removing member" = "Error al expulsar miembro"; + +/* alert title */ +"Error reordering lists" = "Error al reorganizar listas"; + +/* No comment provided by engineer. */ +"Error resetting statistics" = "Error al restablecer las estadísticas"; + +/* alert title */ +"Error saving chat list" = "Error al guardar listas"; /* No comment provided by engineer. */ "Error saving group profile" = "Error al guardar perfil de grupo"; @@ -1635,6 +2260,12 @@ /* No comment provided by engineer. */ "Error saving passphrase to keychain" = "Error al guardar contraseña en Keychain"; +/* alert title */ +"Error saving servers" = "Error al guardar servidores"; + +/* when migrating */ +"Error saving settings" = "Error al guardar ajustes"; + /* No comment provided by engineer. */ "Error saving user password" = "Error al guardar contraseña de usuario"; @@ -1657,20 +2288,29 @@ "Error starting chat" = "Error al iniciar Chat"; /* No comment provided by engineer. */ -"Error stopping chat" = "Error al detener Chat"; +"Error stopping chat" = "Error al parar SimpleX"; /* No comment provided by engineer. */ +"Error switching profile" = "Error al cambiar perfil"; + +/* alertTitle */ "Error switching profile!" = "¡Error al cambiar perfil!"; /* No comment provided by engineer. */ "Error synchronizing connection" = "Error al sincronizar conexión"; +/* No comment provided by engineer. */ +"Error testing server connection" = "Error al testar la conexión al servidor"; + /* No comment provided by engineer. */ "Error updating group link" = "Error al actualizar enlace de grupo"; /* No comment provided by engineer. */ "Error updating message" = "Error al actualizar mensaje"; +/* alert title */ +"Error updating server" = "Error al actualizar el servidor"; + /* No comment provided by engineer. */ "Error updating settings" = "Error al actualizar configuración"; @@ -1678,9 +2318,17 @@ "Error updating user privacy" = "Error al actualizar privacidad de usuario"; /* No comment provided by engineer. */ -"Error: " = "Error: "; +"Error uploading the archive" = "Error al subir el archivo"; /* No comment provided by engineer. */ +"Error verifying passphrase:" = "Error al verificar la frase de contraseña:"; + +/* No comment provided by engineer. */ +"Error: " = "Error: "; + +/* alert message +file error text +snd error text */ "Error: %@" = "Error: %@"; /* No comment provided by engineer. */ @@ -1690,10 +2338,13 @@ "Error: URL is invalid" = "Error: la URL no es válida"; /* No comment provided by engineer. */ -"Even when disabled in the conversation." = "Incluso si está desactivado para la conversación."; +"Errors" = "Errores"; + +/* servers error */ +"Errors in servers configuration." = "Error en la configuración del servidor."; /* No comment provided by engineer. */ -"event happened" = "evento ocurrido"; +"Even when disabled in the conversation." = "Incluso si está desactivado para la conversación."; /* No comment provided by engineer. */ "Exit without saving" = "Salir sin guardar"; @@ -1701,15 +2352,27 @@ /* chat item action */ "Expand" = "Expandir"; +/* No comment provided by engineer. */ +"expired" = "expirados"; + +/* token status text */ +"Expired" = "Expirado"; + /* No comment provided by engineer. */ "Export database" = "Exportar base de datos"; /* No comment provided by engineer. */ "Export error:" = "Error al exportar:"; +/* No comment provided by engineer. */ +"Export theme" = "Exportar tema"; + /* No comment provided by engineer. */ "Exported database archive." = "Archivo de base de datos exportado."; +/* No comment provided by engineer. */ +"Exported file doesn't exist" = "El archivo exportado no existe"; + /* No comment provided by engineer. */ "Exporting database archive…" = "Exportando base de datos…"; @@ -1719,24 +2382,57 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "¡Rápido y sin necesidad de esperar a que el remitente esté en línea!"; +/* No comment provided by engineer. */ +"Faster deletion of groups." = "Eliminación más rápida de grupos."; + /* No comment provided by engineer. */ "Faster joining and more reliable messages." = "Mensajería más segura y conexión más rápida."; /* No comment provided by engineer. */ +"Faster sending messages." = "Envío más rápido de mensajes."; + +/* swipe action */ "Favorite" = "Favoritos"; +/* No comment provided by engineer. */ +"Favorites" = "Favoritos"; + +/* file error alert title */ +"File error" = "Error de archivo"; + +/* alert message */ +"File errors:\n%@" = "Error(es) de archivo\n%@"; + +/* file error text */ +"File is blocked by server operator:\n%@." = "Archivo bloqueado por el operador del servidor\n%@."; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "Archivo no encontrado, probablemente haya sido eliminado o cancelado."; + +/* file error text */ +"File server error: %@" = "Error del servidor de archivos: %@"; + +/* No comment provided by engineer. */ +"File status" = "Estado del archivo"; + +/* copied message info */ +"File status: %@" = "Estado del archivo: %@"; + /* No comment provided by engineer. */ "File will be deleted from servers." = "El archivo será eliminado de los servidores."; /* No comment provided by engineer. */ -"File will be received when your contact completes uploading it." = "El archivo se recibirá cuando tu contacto termine de subirlo."; +"File will be received when your contact completes uploading it." = "El archivo se recibirá cuando el contacto termine de subirlo."; /* No comment provided by engineer. */ -"File will be received when your contact is online, please wait or check later!" = "El archivo se recibirá cuando tu contacto esté en línea, ¡por favor espera o compruébalo más tarde!"; +"File will be received when your contact is online, please wait or check later!" = "El archivo se recibirá cuando el contacto esté en línea, ¡por favor espera o revisa más tarde!"; /* No comment provided by engineer. */ "File: %@" = "Archivo: %@"; +/* No comment provided by engineer. */ +"Files" = "Archivos"; + /* No comment provided by engineer. */ "Files & media" = "Archivos y multimedia"; @@ -1744,7 +2440,10 @@ "Files and media" = "Archivos y multimedia"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "No se permiten archivos y multimedia en este grupo."; +"Files and media are prohibited." = "Los archivos y multimedia no están permitidos en este grupo."; + +/* No comment provided by engineer. */ +"Files and media not allowed" = "Archivos y multimedia no permitidos"; /* No comment provided by engineer. */ "Files and media prohibited!" = "¡Archivos y multimedia no permitidos!"; @@ -1752,6 +2451,12 @@ /* No comment provided by engineer. */ "Filter unread and favorite chats." = "Filtra chats no leídos y favoritos."; +/* No comment provided by engineer. */ +"Finalize migration" = "Finalizar migración"; + +/* No comment provided by engineer. */ +"Finalize migration on another device." = "Finalizar la migración en otro dispositivo."; + /* No comment provided by engineer. */ "Finally, we have them! 🚀" = "¡Por fin los tenemos! 🚀"; @@ -1776,9 +2481,72 @@ /* No comment provided by engineer. */ "Fix not supported by group member" = "Corrección no compatible con miembro del grupo"; +/* No comment provided by engineer. */ +"For all moderators" = "Para todos los moderadores"; + +/* servers error */ +"For chat profile %@:" = "Para el perfil de chat %@:"; + /* No comment provided by engineer. */ "For console" = "Para consola"; +/* No comment provided by engineer. */ +"For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server." = "Si por ejemplo tu contacto recibe los mensajes a través de un servidor de SimpleX Chat, tu aplicación los entregará a través de un servidor de Flux."; + +/* No comment provided by engineer. */ +"For me" = "para mí"; + +/* No comment provided by engineer. */ +"For private routing" = "Para enrutamiento privado"; + +/* No comment provided by engineer. */ +"For social media" = "Para redes sociales"; + +/* chat item action */ +"Forward" = "Reenviar"; + +/* alert title */ +"Forward %d message(s)?" = "¿Reenviar %d mensaje(s)?"; + +/* No comment provided by engineer. */ +"Forward and save messages" = "Reenviar y guardar mensajes"; + +/* alert action */ +"Forward messages" = "Reenviar mensajes"; + +/* alert message */ +"Forward messages without files?" = "¿Reenviar mensajes sin los archivos?"; + +/* No comment provided by engineer. */ +"Forward up to 20 messages at once." = "Desplazamiento de hasta 20 mensajes."; + +/* No comment provided by engineer. */ +"forwarded" = "reenviado"; + +/* No comment provided by engineer. */ +"Forwarded" = "Reenviado"; + +/* No comment provided by engineer. */ +"Forwarded from" = "Reenviado por"; + +/* No comment provided by engineer. */ +"Forwarding %lld messages" = "Reenviando %lld mensajes"; + +/* No comment provided by engineer. */ +"Forwarding server %@ failed to connect to destination server %@. Please try later." = "El servidor de reenvío %@ no ha podido conectarse al servidor de destino %@. Por favor, intentalo más tarde."; + +/* No comment provided by engineer. */ +"Forwarding server address is incompatible with network settings: %@." = "La dirección del servidor de reenvío es incompatible con la configuración de red: %@."; + +/* No comment provided by engineer. */ +"Forwarding server version is incompatible with network settings: %@." = "La versión del servidor de reenvío es incompatible con la configuración de red: %@."; + +/* snd error text */ +"Forwarding server: %@\nDestination server error: %@" = "Servidor de reenvío: %1$@\nError del servidor de destino: %2$@"; + +/* snd error text */ +"Forwarding server: %@\nError: %@" = "Servidor de reenvío: %1$@\nError: %2$@"; + /* No comment provided by engineer. */ "Found desktop" = "Ordenador encontrado"; @@ -1792,20 +2560,26 @@ "Full name (optional)" = "Nombre completo (opcional)"; /* No comment provided by engineer. */ -"Full name:" = "Nombre completo:"; +"Fully decentralized – visible only to members." = "Totalmente descentralizado. Visible sólo para los miembros."; /* No comment provided by engineer. */ -"Fully decentralized – visible only to members." = "Completamente descentralizado: sólo visible a los miembros."; - -/* No comment provided by engineer. */ -"Fully re-implemented - work in background!" = "Completamente reimplementado: ¡funciona en segundo plano!"; +"Fully re-implemented - work in background!" = "Totalmente revisado. ¡Funciona en segundo plano!"; /* No comment provided by engineer. */ "Further reduced battery usage" = "Reducción consumo de batería"; +/* No comment provided by engineer. */ +"Get notified when mentioned." = "Las menciones ahora se notifican."; + /* No comment provided by engineer. */ "GIFs and stickers" = "GIFs y stickers"; +/* message preview */ +"Good afternoon!" = "¡Buenas tardes!"; + +/* message preview */ +"Good morning!" = "¡Buenos días!"; + /* No comment provided by engineer. */ "Group" = "Grupo"; @@ -1842,24 +2616,6 @@ /* No comment provided by engineer. */ "Group links" = "Enlaces de grupo"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "Los miembros pueden añadir reacciones a los mensajes."; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "Los miembros del grupo pueden eliminar mensajes de forma irreversible. (24 horas)"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "Los miembros del grupo pueden enviar mensajes directos."; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "Los miembros del grupo pueden enviar mensajes temporales."; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "Los miembros del grupo pueden enviar archivos y multimedia."; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "Los miembros del grupo pueden enviar mensajes de voz."; - /* notification */ "Group message:" = "Mensaje de grupo:"; @@ -1882,14 +2638,20 @@ "Group welcome message" = "Mensaje de bienvenida en grupos"; /* No comment provided by engineer. */ -"Group will be deleted for all members - this cannot be undone!" = "El grupo será eliminado para todos los miembros. ¡No podrá deshacerse!"; +"Group will be deleted for all members - this cannot be undone!" = "El grupo será eliminado para todos los miembros. ¡No puede deshacerse!"; /* No comment provided by engineer. */ -"Group will be deleted for you - this cannot be undone!" = "El grupo será eliminado para tí. ¡No podrá deshacerse!"; +"Group will be deleted for you - this cannot be undone!" = "El grupo será eliminado para tí. ¡No puede deshacerse!"; + +/* No comment provided by engineer. */ +"Groups" = "Grupos"; /* No comment provided by engineer. */ "Help" = "Ayuda"; +/* No comment provided by engineer. */ +"Help admins moderating their groups." = "Ayuda a los admins a moderar sus grupos."; + /* No comment provided by engineer. */ "Hidden" = "Oculto"; @@ -1921,6 +2683,12 @@ "hours" = "horas"; /* No comment provided by engineer. */ +"How it affects privacy" = "Cómo afecta a la privacidad"; + +/* No comment provided by engineer. */ +"How it helps privacy" = "Cómo ayuda a la privacidad"; + +/* alert button */ "How it works" = "Cómo funciona"; /* No comment provided by engineer. */ @@ -1935,11 +2703,14 @@ /* No comment provided by engineer. */ "How to use your servers" = "Cómo usar los servidores"; +/* No comment provided by engineer. */ +"Hungarian interface" = "Interfaz en húngaro"; + /* No comment provided by engineer. */ "ICE servers (one per line)" = "Servidores ICE (uno por línea)"; /* No comment provided by engineer. */ -"If you can't meet in person, show QR code in a video call, or share the link." = "Si no puedes reunirte en persona, muestra el código QR por videollamada, o comparte el enlace."; +"If you can't meet in person, show QR code in a video call, or share the link." = "Si no puedes reunirte en persona, muestra el código QR por videollamada o comparte el enlace."; /* No comment provided by engineer. */ "If you enter this passcode when opening the app, all app data will be irreversibly removed!" = "¡Si introduces este código al abrir la aplicación, todos los datos de la misma se eliminarán de forma irreversible!"; @@ -1954,16 +2725,16 @@ "Ignore" = "Ignorar"; /* No comment provided by engineer. */ -"Image will be received when your contact completes uploading it." = "La imagen se recibirá cuando tu contacto termine de subirla."; +"Image will be received when your contact completes uploading it." = "La imagen se recibirá cuando el contacto termine de subirla."; /* No comment provided by engineer. */ -"Image will be received when your contact is online, please wait or check later!" = "La imagen se recibirá cuando tu contacto esté en línea, ¡por favor espera o compruébalo más tarde!"; +"Image will be received when your contact is online, please wait or check later!" = "La imagen se recibirá cuando el contacto esté en línea, ¡por favor espera o revisa más tarde!"; /* No comment provided by engineer. */ "Immediately" = "Inmediatamente"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "Inmune a spam y abuso"; +"Immune to spam" = "Inmune a spam y abuso"; /* No comment provided by engineer. */ "Import" = "Importar"; @@ -1974,6 +2745,18 @@ /* No comment provided by engineer. */ "Import database" = "Importar base de datos"; +/* No comment provided by engineer. */ +"Import failed" = "Error de importación"; + +/* No comment provided by engineer. */ +"Import theme" = "Importar tema"; + +/* No comment provided by engineer. */ +"Importing archive" = "Importando archivo"; + +/* No comment provided by engineer. */ +"Improved delivery, reduced traffic usage.\nMore improvements are coming soon!" = "Reducción del tráfico y entrega mejorada.\n¡Pronto habrá nuevas mejoras!"; + /* No comment provided by engineer. */ "Improved message delivery" = "Entrega de mensajes mejorada"; @@ -1983,9 +2766,24 @@ /* No comment provided by engineer. */ "Improved server configuration" = "Configuración del servidor mejorada"; +/* No comment provided by engineer. */ +"In order to continue, chat should be stopped." = "Para continuar, SimpleX debe estar parado."; + /* No comment provided by engineer. */ "In reply to" = "En respuesta a"; +/* No comment provided by engineer. */ +"In-call sounds" = "Sonido de llamada"; + +/* No comment provided by engineer. */ +"inactive" = "inactivo"; + +/* report reason */ +"Inappropriate content" = "Contenido inapropiado"; + +/* report reason */ +"Inappropriate profile" = "Perfil inapropiado"; + /* No comment provided by engineer. */ "Incognito" = "Incógnito"; @@ -2040,14 +2838,32 @@ /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Instalar terminal para [SimpleX Chat](https://github.com/simplex-chat/simplex-chat)"; +/* No comment provided by engineer. */ +"Instant" = "Al instante"; + /* No comment provided by engineer. */ "Instant push notifications will be hidden!\n" = "¡Las notificaciones automáticas estarán ocultas!\n"; /* No comment provided by engineer. */ -"Instantly" = "Al instante"; +"Interface" = "Interfaz"; /* No comment provided by engineer. */ -"Interface" = "Interfaz"; +"Interface colors" = "Colores del interfaz"; + +/* token status text */ +"Invalid" = "No válido"; + +/* token status text */ +"Invalid (bad token)" = "No válido (token incorrecto)"; + +/* token status text */ +"Invalid (expired)" = "No válido (expirado)"; + +/* token status text */ +"Invalid (unregistered)" = "No válido (no registrado)"; + +/* token status text */ +"Invalid (wrong topic)" = "No válido (tópico incorrecto)"; /* invalid chat data */ "invalid chat" = "chat no válido"; @@ -2067,6 +2883,9 @@ /* No comment provided by engineer. */ "Invalid link" = "Enlace no válido"; +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Confirmación de migración no válida"; + /* No comment provided by engineer. */ "Invalid name!" = "¡Nombre no válido!"; @@ -2076,7 +2895,7 @@ /* No comment provided by engineer. */ "Invalid response" = "Respuesta no válida"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "¡Dirección de servidor no válida!"; /* item status text */ @@ -2088,12 +2907,18 @@ /* group name */ "invitation to group %@" = "invitación al grupo %@"; +/* No comment provided by engineer. */ +"invite" = "Invitar"; + /* No comment provided by engineer. */ "Invite friends" = "Invitar amigos"; /* No comment provided by engineer. */ "Invite members" = "Invitar miembros"; +/* No comment provided by engineer. */ +"Invite to chat" = "Invitar al chat"; + /* No comment provided by engineer. */ "Invite to group" = "Invitar al grupo"; @@ -2115,6 +2940,9 @@ /* No comment provided by engineer. */ "iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "iOS Keychain se usará para almacenar la contraseña de forma segura después de reiniciar la aplicación o cambiar la contraseña. Esto permitirá recibir notificaciones automáticas."; +/* No comment provided by engineer. */ +"IP address" = "Dirección IP"; + /* No comment provided by engineer. */ "Irreversible message deletion" = "Eliminación irreversible del mensaje"; @@ -2122,7 +2950,7 @@ "Irreversible message deletion is prohibited in this chat." = "La eliminación irreversible de mensajes no está permitida en este chat."; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "La eliminación irreversible de mensajes no está permitida en este grupo."; +"Irreversible message deletion is prohibited." = "La eliminación irreversible de mensajes no está permitida en este grupo."; /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "Permite tener varias conexiones anónimas sin datos compartidos entre estas dentro del mismo perfil."; @@ -2131,7 +2959,10 @@ "It can happen when you or your connection used the old database backup." = "Puede ocurrir cuando tu o tu contacto estáis usando una copia de seguridad antigua de la base de datos."; /* No comment provided by engineer. */ -"It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Esto puede suceder cuando:\n1. Los mensajes caducan en el cliente saliente tras 2 días o en el servidor tras 30 días.\n2. El descifrado ha fallado porque tu o tu contacto estáis usando una copia de seguridad antigua de la base de datos.\n3. La conexión ha sido comprometida."; +"It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Esto puede suceder cuando:\n1. Los mensajes caducan tras 2 días en el cliente saliente o tras 30 días en el servidor.\n2. El descifrado ha fallado porque tu o tu contacto estáis usando una copia de seguridad antigua de la base de datos.\n3. La conexión ha sido comprometida."; + +/* No comment provided by engineer. */ +"It protects your IP address and connections." = "Protege tu dirección IP y tus conexiones."; /* No comment provided by engineer. */ "It seems like you are already connected via this link. If it is not the case, there was an error (%@)." = "Parece que ya estás conectado mediante este enlace. Si no es así ha habido un error (%@)."; @@ -2145,7 +2976,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "Interfáz en japonés"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "Unirte"; /* No comment provided by engineer. */ @@ -2172,13 +3003,16 @@ /* No comment provided by engineer. */ "Joining group" = "Entrando al grupo"; -/* No comment provided by engineer. */ +/* alert action */ "Keep" = "Guardar"; +/* No comment provided by engineer. */ +"Keep conversation" = "Conservar conversación"; + /* No comment provided by engineer. */ "Keep the app open to use it from desktop" = "Mantén la aplicación abierta para usarla desde el ordenador"; -/* No comment provided by engineer. */ +/* alert title */ "Keep unused invitation?" = "¿Guardar invitación no usada?"; /* No comment provided by engineer. */ @@ -2196,9 +3030,15 @@ /* No comment provided by engineer. */ "Learn more" = "Más información"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "Salir"; +/* No comment provided by engineer. */ +"Leave chat" = "Salir del chat"; + +/* No comment provided by engineer. */ +"Leave chat?" = "¿Salir del chat?"; + /* No comment provided by engineer. */ "Leave group" = "Salir del grupo"; @@ -2226,6 +3066,15 @@ /* No comment provided by engineer. */ "Linked desktops" = "Ordenadores enlazados"; +/* swipe action */ +"List" = "Lista"; + +/* No comment provided by engineer. */ +"List name and emoji should be different for all lists." = "El nombre y el emoji deben ser diferentes en todas las listas."; + +/* No comment provided by engineer. */ +"List name..." = "Nombre de la lista..."; + /* No comment provided by engineer. */ "LIVE" = "EN VIVO"; @@ -2235,14 +3084,11 @@ /* No comment provided by engineer. */ "Live messages" = "Mensajes en vivo"; -/* No comment provided by engineer. */ -"Local" = "Local"; - /* No comment provided by engineer. */ "Local name" = "Nombre local"; /* No comment provided by engineer. */ -"Local profile data only" = "Sólo datos del perfil local"; +"Local profile data only" = "Eliminar sólo el perfil"; /* No comment provided by engineer. */ "Lock after" = "Bloquear en"; @@ -2250,24 +3096,15 @@ /* No comment provided by engineer. */ "Lock mode" = "Modo bloqueo"; -/* No comment provided by engineer. */ -"Make a private connection" = "Establecer una conexión privada"; - /* No comment provided by engineer. */ "Make one message disappear" = "Escribir un mensaje temporal"; /* No comment provided by engineer. */ "Make profile private!" = "¡Hacer perfil privado!"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Asegúrate de que las direcciones del servidor %@ tienen el formato correcto, están separadas por líneas y no duplicadas (%@)."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Asegúrate de que las direcciones del servidor WebRTC ICE tienen el formato correcto, están separadas por líneas y no duplicadas."; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Muchos se preguntarán: *si SimpleX no tiene identificadores de usuario, ¿cómo puede entregar los mensajes?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "Marcar como eliminado para todos"; @@ -2286,6 +3123,12 @@ /* No comment provided by engineer. */ "Max 30 seconds, received instantly." = "Máximo 30 segundos, recibido al instante."; +/* No comment provided by engineer. */ +"Media & file servers" = "Servidores de archivos y multimedia"; + +/* blur media */ +"Medium" = "Medio"; + /* member role */ "member" = "miembro"; @@ -2298,6 +3141,15 @@ /* rcv group event chat item */ "member connected" = "conectado"; +/* item status text */ +"Member inactive" = "Miembro inactivo"; + +/* chat feature */ +"Member reports" = "Informes de miembros"; + +/* No comment provided by engineer. */ +"Member role will be changed to \"%@\". All chat members will be notified." = "El rol del miembro cambiará a \"%@\" y todos serán notificados."; + /* No comment provided by engineer. */ "Member role will be changed to \"%@\". All group members will be notified." = "El rol del miembro cambiará a \"%@\" y se notificará al grupo."; @@ -2305,7 +3157,43 @@ "Member role will be changed to \"%@\". The member will receive a new invitation." = "El rol del miembro cambiará a \"%@\" y recibirá una invitación nueva."; /* No comment provided by engineer. */ -"Member will be removed from group - this cannot be undone!" = "El miembro será expulsado del grupo. ¡No podrá deshacerse!"; +"Member will be removed from chat - this cannot be undone!" = "El miembro será eliminado del chat. ¡No puede deshacerse!"; + +/* No comment provided by engineer. */ +"Member will be removed from group - this cannot be undone!" = "El miembro será expulsado del grupo. ¡No puede deshacerse!"; + +/* No comment provided by engineer. */ +"Members can add message reactions." = "Los miembros pueden añadir reacciones a los mensajes."; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "Los miembros del grupo pueden eliminar mensajes de forma irreversible. (24 horas)"; + +/* No comment provided by engineer. */ +"Members can report messsages to moderators." = "Los miembros pueden informar de mensajes a los moderadores."; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "Los miembros del grupo pueden enviar mensajes directos."; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "Los miembros del grupo pueden enviar mensajes temporales."; + +/* No comment provided by engineer. */ +"Members can send files and media." = "Los miembros del grupo pueden enviar archivos y multimedia."; + +/* No comment provided by engineer. */ +"Members can send SimpleX links." = "Los miembros del grupo pueden enviar enlaces SimpleX."; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "Los miembros del grupo pueden enviar mensajes de voz."; + +/* No comment provided by engineer. */ +"Mention members 👋" = "Menciona a miembros 👋"; + +/* No comment provided by engineer. */ +"Menus" = "Menus"; + +/* No comment provided by engineer. */ +"message" = "mensaje"; /* item status text */ "Message delivery error" = "Error en la entrega del mensaje"; @@ -2313,9 +3201,21 @@ /* No comment provided by engineer. */ "Message delivery receipts!" = "¡Confirmación de entrega de mensajes!"; +/* item status text */ +"Message delivery warning" = "Aviso de entrega de mensaje"; + /* No comment provided by engineer. */ "Message draft" = "Borrador de mensaje"; +/* item status text */ +"Message forwarded" = "Mensaje reenviado"; + +/* item status description */ +"Message may be delivered later if member becomes active." = "El mensaje podría ser entregado más tarde si el miembro vuelve a estar activo."; + +/* No comment provided by engineer. */ +"Message queue info" = "Información cola de mensajes"; + /* chat feature */ "Message reactions" = "Reacciones a mensajes"; @@ -2323,14 +3223,35 @@ "Message reactions are prohibited in this chat." = "Las reacciones a los mensajes no están permitidas en este chat."; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "Las reacciones a los mensajes no están permitidas en este grupo."; +"Message reactions are prohibited." = "Las reacciones a los mensajes no están permitidas en este grupo."; /* notification */ "message received" = "mensaje recibido"; +/* No comment provided by engineer. */ +"Message reception" = "Recepción de mensaje"; + +/* No comment provided by engineer. */ +"Message servers" = "Servidores de mensajes"; + +/* No comment provided by engineer. */ +"Message shape" = "Forma del mensaje"; + +/* No comment provided by engineer. */ +"Message source remains private." = "El autor del mensaje se mantiene privado."; + +/* No comment provided by engineer. */ +"Message status" = "Estado del mensaje"; + +/* copied message info */ +"Message status: %@" = "Estado del mensaje: %@"; + /* No comment provided by engineer. */ "Message text" = "Contacto y texto"; +/* No comment provided by engineer. */ +"Message too large" = "Mensaje demasiado largo"; + /* No comment provided by engineer. */ "Messages" = "Mensajes"; @@ -2340,9 +3261,48 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "¡Los mensajes de %@ serán mostrados!"; +/* alert message */ +"Messages in this chat will never be deleted." = "Los mensajes de esta conversación nunca se eliminan."; + +/* No comment provided by engineer. */ +"Messages received" = "Mensajes recibidos"; + +/* No comment provided by engineer. */ +"Messages sent" = "Mensajes enviados"; + +/* alert message */ +"Messages were deleted after you selected them." = "Los mensajes han sido eliminados después de seleccionarlos."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Los mensajes, archivos y llamadas están protegidos mediante **cifrado de extremo a extremo** con secreto perfecto hacía adelante, repudio y recuperación tras ataque."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Los mensajes, archivos y llamadas están protegidos mediante **cifrado de extremo a extremo resistente a tecnología cuántica** con secreto perfecto hacía adelante, repudio y recuperación tras ataque."; + +/* No comment provided by engineer. */ +"Migrate device" = "Migrar dispositivo"; + +/* No comment provided by engineer. */ +"Migrate from another device" = "Migrar desde otro dispositivo"; + +/* No comment provided by engineer. */ +"Migrate here" = "Migrar aquí"; + +/* No comment provided by engineer. */ +"Migrate to another device" = "Migrar a otro dispositivo"; + +/* No comment provided by engineer. */ +"Migrate to another device via QR code." = "Migrar a otro dispositivo mediante código QR."; + +/* No comment provided by engineer. */ +"Migrating" = "Migrando"; + /* No comment provided by engineer. */ "Migrating database archive…" = "Migrando base de datos…"; +/* No comment provided by engineer. */ +"Migration complete" = "Migración completada"; + /* No comment provided by engineer. */ "Migration error:" = "Error de migración:"; @@ -2353,7 +3313,7 @@ "Migration is completed" = "Migración completada"; /* No comment provided by engineer. */ -"Migrations: %@" = "Migraciones: %@"; +"Migrations:" = "Migraciones:"; /* time unit */ "minutes" = "minutos"; @@ -2376,63 +3336,99 @@ /* marked deleted chat item preview text */ "moderated by %@" = "moderado por %@"; +/* member role */ +"moderator" = "moderador"; + /* time unit */ "months" = "meses"; +/* swipe action */ +"More" = "Más"; + /* No comment provided by engineer. */ "More improvements are coming soon!" = "¡Pronto habrá más mejoras!"; +/* No comment provided by engineer. */ +"More reliable network connection." = "Conexión de red más fiable."; + +/* No comment provided by engineer. */ +"More reliable notifications" = "Notificaciones más fiables"; + /* item status description */ "Most likely this connection is deleted." = "Probablemente la conexión ha sido eliminada."; -/* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "Lo más probable es que este contacto haya eliminado la conexión contigo."; - /* No comment provided by engineer. */ "Multiple chat profiles" = "Múltiples perfiles"; -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "Silenciar"; +/* notification label action */ +"Mute all" = "Silenciar todo"; + /* No comment provided by engineer. */ "Muted when inactive!" = "¡Silenciado cuando está inactivo!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "Nombre"; /* No comment provided by engineer. */ "Network & servers" = "Servidores y Redes"; +/* No comment provided by engineer. */ +"Network connection" = "Conexión de red"; + +/* No comment provided by engineer. */ +"Network decentralization" = "Descentralización de la red"; + +/* snd error text */ +"Network issues - message expired after many attempts to send it." = "Problema en la red - el mensaje ha expirado tras muchos intentos de envío."; + +/* No comment provided by engineer. */ +"Network management" = "Gestión de la red"; + +/* No comment provided by engineer. */ +"Network operator" = "Operador de red"; + /* No comment provided by engineer. */ "Network settings" = "Configuración de red"; /* No comment provided by engineer. */ "Network status" = "Estado de la red"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "nunca"; +/* token status text */ +"New" = "Nuevo"; + /* No comment provided by engineer. */ "New chat" = "Nuevo chat"; +/* No comment provided by engineer. */ +"New chat experience 🎉" = "Nueva experiencia de chat 🎉"; + /* notification */ "New contact request" = "Nueva solicitud de contacto"; /* notification */ "New contact:" = "Contacto nuevo:"; -/* No comment provided by engineer. */ -"New database archive" = "Nuevo archivo de bases de datos"; - /* No comment provided by engineer. */ "New desktop app!" = "Nueva aplicación para PC!"; /* No comment provided by engineer. */ "New display name" = "Nuevo nombre mostrado"; +/* notification */ +"New events" = "Eventos nuevos"; + /* No comment provided by engineer. */ "New in %@" = "Nuevo en %@"; +/* No comment provided by engineer. */ +"New media options" = "Nuevas opciones multimedia"; + /* No comment provided by engineer. */ "New member role" = "Nuevo rol de miembro"; @@ -2448,6 +3444,15 @@ /* No comment provided by engineer. */ "New passphrase…" = "Contraseña nueva…"; +/* No comment provided by engineer. */ +"New server" = "Servidor nuevo"; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used every time you start the app." = "Se usarán credenciales SOCKS nuevas cada vez que inicies la aplicación."; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used for each server." = "Se usarán credenciales SOCKS nuevas para cada servidor."; + /* pref value */ "no" = "no"; @@ -2457,6 +3462,15 @@ /* Authentication unavailable */ "No app password" = "Sin contraseña de la aplicación"; +/* No comment provided by engineer. */ +"No chats" = "Sin chats"; + +/* No comment provided by engineer. */ +"No chats found" = "Ningún chat encontrado"; + +/* No comment provided by engineer. */ +"No chats in list %@" = "Sin chats en la lista %@"; + /* No comment provided by engineer. */ "No contacts selected" = "Ningún contacto seleccionado"; @@ -2469,6 +3483,9 @@ /* No comment provided by engineer. */ "No device token!" = "¡Sin dispositivo token!"; +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Aún no hay conexión directa con este miembro, el mensaje es reenviado por el administrador."; + /* No comment provided by engineer. */ "no e2e encryption" = "sin cifrar"; @@ -2481,24 +3498,87 @@ /* No comment provided by engineer. */ "No history" = "Sin historial"; +/* No comment provided by engineer. */ +"No info, try to reload" = "No hay información, intenta recargar"; + +/* servers error */ +"No media & file servers." = "Sin servidores para archivos y multimedia."; + +/* No comment provided by engineer. */ +"No message" = "Ningún mensaje"; + +/* servers error */ +"No message servers." = "Sin servidores para mensajes."; + +/* No comment provided by engineer. */ +"No network connection" = "Sin conexión de red"; + +/* No comment provided by engineer. */ +"No permission to record speech" = "Sin permiso para grabación de voz"; + +/* No comment provided by engineer. */ +"No permission to record video" = "Sin permiso para grabación de vídeo"; + /* No comment provided by engineer. */ "No permission to record voice message" = "Sin permiso para grabar mensajes de voz"; +/* No comment provided by engineer. */ +"No push server" = "Sin servidores push"; + /* No comment provided by engineer. */ "No received or sent files" = "Sin archivos recibidos o enviados"; +/* servers error */ +"No servers for private message routing." = "Sin servidores para enrutamiento privado."; + +/* servers error */ +"No servers to receive files." = "Sin servidores para recibir archivos."; + +/* servers error */ +"No servers to receive messages." = "Sin servidores para recibir mensajes."; + +/* servers error */ +"No servers to send files." = "Sin servidores para enviar archivos."; + /* copied message info in history */ "no text" = "sin texto"; +/* alert title */ +"No token!" = "¡Sin token!"; + +/* No comment provided by engineer. */ +"No unread chats" = "Ningún chat sin leer"; + +/* No comment provided by engineer. */ +"No user identifiers." = "Sin identificadores de usuario."; + /* No comment provided by engineer. */ "Not compatible!" = "¡No compatible!"; +/* No comment provided by engineer. */ +"Notes" = "Notas"; + +/* No comment provided by engineer. */ +"Nothing selected" = "Nada seleccionado"; + +/* alert title */ +"Nothing to forward!" = "¡Nada para reenviar!"; + /* No comment provided by engineer. */ "Notifications" = "Notificaciones"; /* No comment provided by engineer. */ "Notifications are disabled!" = "¡Las notificaciones están desactivadas!"; +/* alert title */ +"Notifications error" = "Error en notificaciones"; + +/* No comment provided by engineer. */ +"Notifications privacy" = "Privacidad en las notificaciones"; + +/* alert title */ +"Notifications status" = "Estado notificaciones"; + /* No comment provided by engineer. */ "Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Ahora los administradores pueden:\n- eliminar mensajes de los miembros.\n- desactivar el rol miembro (a rol \"observador\")"; @@ -2506,11 +3586,11 @@ "observer" = "observador"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "desactivado"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Desactivado"; /* feature offered item */ @@ -2519,7 +3599,7 @@ /* feature offered item */ "offered %@: %@" = "ofrecido %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ @@ -2528,26 +3608,29 @@ /* No comment provided by engineer. */ "Old database" = "Base de datos antigua"; -/* No comment provided by engineer. */ -"Old database archive" = "Archivo de bases de datos antiguas"; - /* group pref value */ "on" = "Activado"; /* No comment provided by engineer. */ -"One-time invitation link" = "Enlace único de invitación de un uso"; +"One-time invitation link" = "Enlace de invitación de un solo uso"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "Se requieren hosts .onion para la conexión. Requiere activación de la VPN."; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "Se **requieren** hosts .onion para la conexión.\nRequiere activación de la VPN."; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "Se usarán hosts .onion si están disponibles. Requiere activación de la VPN."; +"Onion hosts will be used when available.\nRequires compatible VPN." = "Se usarán hosts .onion si están disponibles.\nRequiere activación de la VPN."; /* No comment provided by engineer. */ "Onion hosts will not be used." = "No se usarán hosts .onion."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Sólo los dispositivos cliente almacenan perfiles de usuario, contactos, grupos y mensajes enviados con **cifrado de extremo a extremo de 2 capas**."; +"Only chat owners can change preferences." = "Sólo los propietarios del chat pueden cambiar las preferencias."; + +/* No comment provided by engineer. */ +"Only client devices store user profiles, contacts, groups, and messages." = "Sólo los dispositivos cliente almacenan perfiles de usuario, contactos, grupos y mensajes enviados con **cifrado de extremo a extremo de 2 capas**."; + +/* No comment provided by engineer. */ +"Only delete conversation" = "Eliminar sólo la conversación"; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Sólo los propietarios pueden modificar las preferencias del grupo."; @@ -2558,6 +3641,12 @@ /* No comment provided by engineer. */ "Only group owners can enable voice messages." = "Sólo los propietarios del grupo pueden activar los mensajes de voz."; +/* No comment provided by engineer. */ +"Only sender and moderators see it" = "Solo el remitente y el moderador pueden verlo"; + +/* No comment provided by engineer. */ +"Only you and moderators see it" = "Solo tú y los moderadores podéis verlo"; + /* No comment provided by engineer. */ "Only you can add message reactions." = "Sólo tú puedes añadir reacciones a los mensajes."; @@ -2588,39 +3677,78 @@ /* No comment provided by engineer. */ "Only your contact can send voice messages." = "Sólo tu contacto puede enviar mensajes de voz."; -/* No comment provided by engineer. */ +/* alert action */ "Open" = "Abrir"; +/* No comment provided by engineer. */ +"Open changes" = "Abrir cambios"; + /* No comment provided by engineer. */ "Open chat" = "Abrir chat"; /* authentication reason */ "Open chat console" = "Abrir consola de Chat"; +/* No comment provided by engineer. */ +"Open conditions" = "Abrir condiciones"; + /* No comment provided by engineer. */ "Open group" = "Grupo abierto"; +/* authentication reason */ +"Open migration to another device" = "Abrir menú migración a otro dispositivo"; + /* No comment provided by engineer. */ "Open Settings" = "Abrir Configuración"; -/* authentication reason */ -"Open user profiles" = "Abrir perfil de usuario"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "Protocolo y código abiertos: cualquiera puede usar los servidores."; - /* No comment provided by engineer. */ "Opening app…" = "Iniciando aplicación…"; /* No comment provided by engineer. */ -"Or scan QR code" = "O escanear código QR"; +"Operator" = "Operador"; + +/* alert title */ +"Operator server" = "Servidor del operador"; /* No comment provided by engineer. */ -"Or show this code" = "O mostrar este código"; +"Or import archive file" = "O importa desde un archivo"; + +/* No comment provided by engineer. */ +"Or paste archive link" = "O pegar enlace del archivo"; + +/* No comment provided by engineer. */ +"Or scan QR code" = "O escanea el código QR"; + +/* No comment provided by engineer. */ +"Or securely share this file link" = "O comparte de forma segura este enlace al archivo"; + +/* No comment provided by engineer. */ +"Or show this code" = "O muestra el código QR"; + +/* No comment provided by engineer. */ +"Or to share privately" = "O para compartir en privado"; + +/* No comment provided by engineer. */ +"Organize chats into lists" = "Organiza tus chats en listas"; + +/* No comment provided by engineer. */ +"other" = "otros"; + +/* No comment provided by engineer. */ +"Other" = "Otro"; + +/* No comment provided by engineer. */ +"other errors" = "otros errores"; + +/* alert message */ +"Other file errors:\n%@" = "Otro(s) error(es) de archivo.\n%@"; /* member role */ "owner" = "propietario"; +/* feature role */ +"owners" = "propietarios"; + /* No comment provided by engineer. */ "Passcode" = "Código de acceso"; @@ -2636,6 +3764,9 @@ /* No comment provided by engineer. */ "Passcode set!" = "¡Código de acceso guardado!"; +/* No comment provided by engineer. */ +"Password" = "Contraseña"; + /* No comment provided by engineer. */ "Password to show" = "Contraseña para hacerlo visible"; @@ -2652,29 +3783,47 @@ "Paste link to connect!" = "Pegar enlace para conectar!"; /* No comment provided by engineer. */ -"Paste the link you received" = "Pegar el enlace recibido"; +"Paste the link you received" = "Pega el enlace recibido"; /* No comment provided by engineer. */ "peer-to-peer" = "p2p"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "Las personas pueden conectarse contigo solo mediante los enlaces que compartes."; +"pending" = "pendiente"; /* No comment provided by engineer. */ -"Periodically" = "Periódico"; +"Pending" = "Pendientes"; + +/* No comment provided by engineer. */ +"pending approval" = "pendiente de aprobación"; + +/* No comment provided by engineer. */ +"Periodic" = "Periódicamente"; /* message decrypt error item */ "Permanent decryption error" = "Error permanente descifrado"; +/* No comment provided by engineer. */ +"Picture-in-picture calls" = "Llamadas picture-in-picture"; + /* No comment provided by engineer. */ "PING count" = "Contador PING"; /* No comment provided by engineer. */ "PING interval" = "Intervalo PING"; +/* No comment provided by engineer. */ +"Play from the chat list." = "Reproduce desde la lista de chats."; + +/* No comment provided by engineer. */ +"Please ask your contact to enable calls." = "Por favor, pide a tu contacto que active las llamadas."; + /* No comment provided by engineer. */ "Please ask your contact to enable sending voice messages." = "Solicita que tu contacto habilite el envío de mensajes de voz."; +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Comprueba que el móvil y el ordenador están conectados a la misma red local y que el cortafuegos del ordenador permite la conexión.\nPor favor, comparte cualquier otro problema con los desarrolladores."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Comprueba que has usado el enlace correcto o pide a tu contacto que te envíe otro."; @@ -2684,6 +3833,9 @@ /* No comment provided by engineer. */ "Please check yours and your contact preferences." = "Comprueba tus preferencias y las de tu contacto."; +/* No comment provided by engineer. */ +"Please confirm that network settings are correct for this device." = "Por favor, confirma que la configuración de red es correcta para este dispositivo."; + /* No comment provided by engineer. */ "Please contact developers.\nError: %@" = "Por favor, contacta con los desarrolladores.\nError: %@"; @@ -2711,52 +3863,91 @@ /* No comment provided by engineer. */ "Please store passphrase securely, you will NOT be able to change it if you lose it." = "Guarda la contraseña de forma segura, NO podrás cambiarla si la pierdes."; +/* token info */ +"Please try to disable and re-enable notfications." = "Por favor, intenta desactivar y reactivar las notificaciones."; + +/* token info */ +"Please wait for token activation to complete." = "Por favor, espera a que el token de activación se complete."; + +/* token info */ +"Please wait for token to be registered." = "Por favor, espera a que el token se registre."; + /* No comment provided by engineer. */ "Polish interface" = "Interfaz en polaco"; +/* No comment provided by engineer. */ +"Port" = "Puerto"; + /* server test error */ -"Possibly, certificate fingerprint in server address is incorrect" = "Posiblemente la huella digital del certificado en la dirección del servidor es incorrecta"; +"Possibly, certificate fingerprint in server address is incorrect" = "Posiblemente la huella del certificado en la dirección del servidor es incorrecta"; /* No comment provided by engineer. */ "Preserve the last message draft, with attachments." = "Conserva el último borrador del mensaje con los datos adjuntos."; /* No comment provided by engineer. */ -"Preset server" = "Servidor predefinido"; +"Preset server address" = "Dirección predefinida del servidor"; /* No comment provided by engineer. */ -"Preset server address" = "Dirección del servidor predefinida"; +"Preset servers" = "Servidores predefinidos"; /* No comment provided by engineer. */ "Preview" = "Vista previa"; /* No comment provided by engineer. */ -"Privacy & security" = "Privacidad y Seguridad"; +"Previously connected servers" = "Servidores conectados previamente"; + +/* No comment provided by engineer. */ +"Privacy & security" = "Seguridad y Privacidad"; + +/* No comment provided by engineer. */ +"Privacy for your customers." = "Privacidad para tus clientes."; + +/* No comment provided by engineer. */ +"Privacy policy and conditions of use." = "Política de privacidad y condiciones de uso."; /* No comment provided by engineer. */ "Privacy redefined" = "Privacidad redefinida"; +/* No comment provided by engineer. */ +"Private chats, groups and your contacts are not accessible to server operators." = "Los chats privados, los grupos y tus contactos no son accesibles para los operadores de servidores."; + /* No comment provided by engineer. */ "Private filenames" = "Nombres de archivos privados"; +/* No comment provided by engineer. */ +"Private media file names." = "Nombres privados en archivos de media."; + +/* No comment provided by engineer. */ +"Private message routing" = "Enrutamiento privado de mensajes"; + +/* No comment provided by engineer. */ +"Private message routing 🚀" = "Enrutamiento privado de mensajes 🚀"; + /* name of notes to self */ "Private notes" = "Notas privadas"; /* No comment provided by engineer. */ -"Profile and server connections" = "Perfil y conexiones de servidor"; +"Private routing" = "Enrutamiento privado"; + +/* No comment provided by engineer. */ +"Private routing error" = "Error de enrutamiento privado"; + +/* No comment provided by engineer. */ +"Profile and server connections" = "Eliminar perfil y conexiones"; /* No comment provided by engineer. */ "Profile image" = "Imagen del perfil"; /* No comment provided by engineer. */ -"Profile name" = "Nombre del perfil"; - -/* No comment provided by engineer. */ -"Profile name:" = "Nombre del perfil:"; +"Profile images" = "Forma de los perfiles"; /* No comment provided by engineer. */ "Profile password" = "Contraseña del perfil"; /* No comment provided by engineer. */ +"Profile theme" = "Tema del perfil"; + +/* alert message */ "Profile update will be sent to your contacts." = "La actualización del perfil se enviará a tus contactos."; /* No comment provided by engineer. */ @@ -2771,6 +3962,9 @@ /* No comment provided by engineer. */ "Prohibit messages reactions." = "No se permiten reacciones a los mensajes."; +/* No comment provided by engineer. */ +"Prohibit reporting messages to moderators." = "No se permite informar de mensajes a los moderadores."; + /* No comment provided by engineer. */ "Prohibit sending direct messages to members." = "No se permiten mensajes directos entre miembros."; @@ -2780,54 +3974,84 @@ /* No comment provided by engineer. */ "Prohibit sending files and media." = "No permitir el envío de archivos y multimedia."; +/* No comment provided by engineer. */ +"Prohibit sending SimpleX links." = "No se permite enviar enlaces SimpleX."; + /* No comment provided by engineer. */ "Prohibit sending voice messages." = "No se permiten mensajes de voz."; /* No comment provided by engineer. */ -"Protect app screen" = "Proteger la pantalla de la aplicación"; +"Protect app screen" = "Proteger la pantalla"; + +/* No comment provided by engineer. */ +"Protect IP address" = "Proteger dirección IP"; /* No comment provided by engineer. */ "Protect your chat profiles with a password!" = "¡Protege tus perfiles con contraseña!"; /* No comment provided by engineer. */ -"Protocol timeout" = "Tiempo de espera del protocolo"; +"Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." = "Protege tu dirección IP de los servidores de retransmisión elegidos por tus contactos.\nActívalo en ajustes de *Servidores y Redes*."; /* No comment provided by engineer. */ -"Protocol timeout per KB" = "Límite de espera del protocolo por KB"; +"Protocol timeout" = "Timeout protocolo"; /* No comment provided by engineer. */ -"Push notifications" = "Notificaciones automáticas"; +"Protocol timeout per KB" = "Timeout protocolo por KB"; + +/* No comment provided by engineer. */ +"Proxied" = "Como proxy"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Servidores con proxy"; + +/* No comment provided by engineer. */ +"Proxy requires password" = "El proxy requiere contraseña"; + +/* No comment provided by engineer. */ +"Push notifications" = "Notificaciones push"; + +/* No comment provided by engineer. */ +"Push server" = "Servidor push"; + +/* chat item text */ +"quantum resistant e2e encryption" = "cifrado e2e resistente a tecnología cuántica"; + +/* No comment provided by engineer. */ +"Quantum resistant encryption" = "Cifrado resistente a tecnología cuántica"; /* No comment provided by engineer. */ "Rate the app" = "Valora la aplicación"; +/* No comment provided by engineer. */ +"Reachable chat toolbar" = "Barra de menú accesible"; + /* chat item menu */ "React…" = "Reacciona…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "Leer"; /* No comment provided by engineer. */ "Read more" = "Saber más"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Saber más en el [Manual del Usuario](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Conoce más en la [Guía del Usuario](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Saber más en [Guía de Usuario](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "Conoce más en el [Manual del Usuario](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)."; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Saber más en el [Manual del Usuario](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Conoce más en el [Manual del Usuario](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; /* No comment provided by engineer. */ -"Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Saber más en nuestro [repositorio GitHub](https://github.com/simplex-chat/simplex-chat#readme)."; - -/* No comment provided by engineer. */ -"Read more in our GitHub repository." = "Saber más en nuestro repositorio GitHub."; +"Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Conoce más en nuestro [repositorio GitHub](https://github.com/simplex-chat/simplex-chat#readme)."; /* No comment provided by engineer. */ "Receipts are disabled" = "Las confirmaciones están desactivadas"; +/* No comment provided by engineer. */ +"Receive errors" = "Errores de recepción"; + /* No comment provided by engineer. */ "received answer…" = "respuesta recibida…"; @@ -2846,6 +4070,15 @@ /* message info title */ "Received message" = "Mensaje entrante"; +/* No comment provided by engineer. */ +"Received messages" = "Mensajes recibidos"; + +/* No comment provided by engineer. */ +"Received reply" = "Respuesta recibida"; + +/* No comment provided by engineer. */ +"Received total" = "Total recibidos"; + /* No comment provided by engineer. */ "Receiving address will be changed to a different server. Address change will complete after sender comes online." = "La dirección de recepción pasará a otro servidor. El cambio se completará cuando el remitente esté en línea."; @@ -2858,12 +4091,30 @@ /* No comment provided by engineer. */ "Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)." = "Historial reciente y [bot del directorio](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) mejorados."; +/* No comment provided by engineer. */ +"Recipient(s) can't see who this message is from." = "Los destinatarios no ven de quién procede este mensaje."; + /* No comment provided by engineer. */ "Recipients see updates as you type them." = "Los destinatarios ven actualizarse mientras escribes."; +/* No comment provided by engineer. */ +"Reconnect" = "Reconectar"; + /* No comment provided by engineer. */ "Reconnect all connected servers to force message delivery. It uses additional traffic." = "Reconectar todos los servidores conectados para forzar la entrega del mensaje. Se usa tráfico adicional."; +/* No comment provided by engineer. */ +"Reconnect all servers" = "Reconectar todos los servidores"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "¿Reconectar todos los servidores?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "Reconectar con el servidor para forzar la entrega de mensajes. Se usa tráfico adicional."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "¿Reconectar servidor?"; + /* No comment provided by engineer. */ "Reconnect servers?" = "¿Reconectar servidores?"; @@ -2876,7 +4127,17 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "Reducción del uso de batería"; -/* reject incoming call via notification */ +/* No comment provided by engineer. */ +"Register" = "Registrar"; + +/* token info */ +"Register notification token?" = "¿Registrar el token de notificaciones?"; + +/* token status text */ +"Registered" = "Registrado"; + +/* reject incoming call via notification +swipe action */ "Reject" = "Rechazar"; /* No comment provided by engineer. */ @@ -2885,11 +4146,14 @@ /* No comment provided by engineer. */ "Reject contact request" = "Rechazar solicitud de contacto"; +/* No comment provided by engineer. */ +"rejected" = "rechazado"; + /* call status */ "rejected call" = "llamada rechazada"; /* No comment provided by engineer. */ -"Relay server is only used if necessary. Another party can observe your IP address." = "El retransmisor sólo se usa en caso de necesidad. Un tercero podría ver tu IP."; +"Relay server is only used if necessary. Another party can observe your IP address." = "El servidor de retransmisión sólo se usa en caso de necesidad. Un tercero podría ver tu IP."; /* No comment provided by engineer. */ "Relay server protects your IP address, but it can observe the duration of the call." = "El servidor de retransmisión protege tu IP pero puede ver la duración de la llamada."; @@ -2897,6 +4161,12 @@ /* No comment provided by engineer. */ "Remove" = "Eliminar"; +/* No comment provided by engineer. */ +"Remove archive?" = "¿Eliminar archivo?"; + +/* No comment provided by engineer. */ +"Remove image" = "Eliminar imagen"; + /* No comment provided by engineer. */ "Remove member" = "Expulsar miembro"; @@ -2916,7 +4186,7 @@ "removed contact address" = "dirección de contacto eliminada"; /* profile update event chat item */ -"removed profile picture" = "imagen de perfil eliminada"; +"removed profile picture" = "ha eliminado la imagen del perfil"; /* rcv group event chat item */ "removed you" = "te ha expulsado"; @@ -2933,23 +4203,80 @@ /* No comment provided by engineer. */ "Repeat connection request?" = "¿Repetir solicitud de conexión?"; +/* No comment provided by engineer. */ +"Repeat download" = "Repetir descarga"; + +/* No comment provided by engineer. */ +"Repeat import" = "Repetir importación"; + /* No comment provided by engineer. */ "Repeat join request?" = "¿Repetir solicitud de admisión?"; +/* No comment provided by engineer. */ +"Repeat upload" = "Repetir subida"; + /* chat item action */ "Reply" = "Responder"; +/* chat item action */ +"Report" = "Informe"; + +/* report reason */ +"Report content: only group moderators will see it." = "Informar de contenido: sólo los moderadores del grupo lo verán."; + +/* report reason */ +"Report member profile: only group moderators will see it." = "Informar del perfil de un miembro: sólo los moderadores del grupo lo verán."; + +/* report reason */ +"Report other: only group moderators will see it." = "Informar de otros: sólo los moderadores del grupo lo verán."; + +/* No comment provided by engineer. */ +"Report reason?" = "¿Motivo del informe?"; + +/* report reason */ +"Report spam: only group moderators will see it." = "Informar de spam: sólo los moderadores del grupo lo verán."; + +/* report reason */ +"Report violation: only group moderators will see it." = "Informar de violación: sólo los moderadores del grupo lo verán."; + +/* report in notification */ +"Report: %@" = "Informe: %@"; + +/* No comment provided by engineer. */ +"Reporting messages to moderators is prohibited." = "No se permite informar de mensajes a los moderadores."; + +/* No comment provided by engineer. */ +"Reports" = "Informes"; + +/* chat list item title */ +"requested to connect" = "solicitado para conectar"; + /* No comment provided by engineer. */ "Required" = "Obligatorio"; /* No comment provided by engineer. */ "Reset" = "Restablecer"; +/* No comment provided by engineer. */ +"Reset all hints" = "Reiniciar todas las pistas"; + +/* No comment provided by engineer. */ +"Reset all statistics" = "Restablecer todas las estadísticas"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "¿Restablecer todas las estadísticas?"; + /* No comment provided by engineer. */ "Reset colors" = "Restablecer colores"; /* No comment provided by engineer. */ -"Reset to defaults" = "Restablecer valores por defecto"; +"Reset to app theme" = "Restablecer al tema de la aplicación"; + +/* No comment provided by engineer. */ +"Reset to defaults" = "Restablecer valores predetarminados"; + +/* No comment provided by engineer. */ +"Reset to user theme" = "Restablecer al tema del usuario"; /* No comment provided by engineer. */ "Restart the app to create a new chat profile" = "Reinicia la aplicación para crear un perfil nuevo"; @@ -2976,7 +4303,7 @@ "Reveal" = "Revelar"; /* No comment provided by engineer. */ -"Revert" = "Revertir"; +"Review conditions" = "Revisar condiciones"; /* No comment provided by engineer. */ "Revoke" = "Revocar"; @@ -2991,39 +4318,46 @@ "Role" = "Rol"; /* No comment provided by engineer. */ -"Run chat" = "Ejecutar chat"; +"Run chat" = "Ejecutar SimpleX"; -/* chat item action */ +/* No comment provided by engineer. */ +"Safely receive files" = "Recibe archivos de forma segura"; + +/* No comment provided by engineer. */ +"Safer groups" = "Grupos más seguros"; + +/* alert button +chat item action */ "Save" = "Guardar"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Guardar (y notificar contactos)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "Guardar y notificar contacto"; /* No comment provided by engineer. */ "Save and notify group members" = "Guardar y notificar grupo"; +/* No comment provided by engineer. */ +"Save and reconnect" = "Guardar y reconectar"; + /* No comment provided by engineer. */ "Save and update group profile" = "Guardar y actualizar perfil del grupo"; -/* No comment provided by engineer. */ -"Save archive" = "Guardar archivo"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Guardar configuración de auto aceptar"; - /* No comment provided by engineer. */ "Save group profile" = "Guardar perfil de grupo"; +/* No comment provided by engineer. */ +"Save list" = "Guardar lista"; + /* No comment provided by engineer. */ "Save passphrase and open chat" = "Guardar contraseña y abrir el chat"; /* No comment provided by engineer. */ "Save passphrase in Keychain" = "Guardar la contraseña en Keychain"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "¿Guardar preferencias?"; /* No comment provided by engineer. */ @@ -3032,14 +4366,26 @@ /* No comment provided by engineer. */ "Save servers" = "Guardar servidores"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "¿Guardar servidores?"; /* No comment provided by engineer. */ -"Save settings?" = "¿Guardar configuración?"; +"Save welcome message?" = "¿Guardar mensaje de bienvenida?"; + +/* alert title */ +"Save your profile?" = "¿Guardar tu perfil?"; /* No comment provided by engineer. */ -"Save welcome message?" = "¿Guardar mensaje de bienvenida?"; +"saved" = "guardado"; + +/* No comment provided by engineer. */ +"Saved" = "Guardado"; + +/* No comment provided by engineer. */ +"Saved from" = "Guardado desde"; + +/* No comment provided by engineer. */ +"saved from %@" = "Guardado desde %@"; /* message info title */ "Saved message" = "Mensaje guardado"; @@ -3047,6 +4393,15 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Los servidores WebRTC ICE guardados serán eliminados"; +/* No comment provided by engineer. */ +"Saving %lld messages" = "Guardando %lld mensajes"; + +/* No comment provided by engineer. */ +"Scale" = "Escala"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Escanear / Pegar enlace"; + /* No comment provided by engineer. */ "Scan code" = "Escanear código"; @@ -3060,7 +4415,10 @@ "Scan security code from your contact's app." = "Escanea el código de seguridad desde la aplicación de tu contacto."; /* No comment provided by engineer. */ -"Scan server QR code" = "Escanear código QR del servidor"; +"Scan server QR code" = "Escanear código QR"; + +/* No comment provided by engineer. */ +"search" = "buscar"; /* No comment provided by engineer. */ "Search" = "Buscar"; @@ -3074,6 +4432,9 @@ /* network option */ "sec" = "seg"; +/* No comment provided by engineer. */ +"Secondary" = "Secundario"; + /* time unit */ "seconds" = "segundos"; @@ -3083,6 +4444,9 @@ /* server test step */ "Secure queue" = "Cola segura"; +/* No comment provided by engineer. */ +"Secured" = "Aseguradas"; + /* No comment provided by engineer. */ "Security assessment" = "Evaluación de la seguridad"; @@ -3092,9 +4456,18 @@ /* chat item text */ "security code changed" = "código de seguridad cambiado"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "Seleccionar"; +/* No comment provided by engineer. */ +"Select chat profile" = "Selecciona perfil de chat"; + +/* No comment provided by engineer. */ +"Selected %lld" = "Seleccionados %lld"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Las preferencias seleccionadas no permiten este mensaje."; + /* No comment provided by engineer. */ "Self-destruct" = "Autodestrucción"; @@ -3111,7 +4484,7 @@ "Send" = "Enviar"; /* No comment provided by engineer. */ -"Send a live message - it will update for the recipient(s) as you type it" = "Envía un mensaje en vivo: se actualizará para el(los) destinatario(s) a medida que se escribe"; +"Send a live message - it will update for the recipient(s) as you type it" = "Envía un mensaje en vivo: se actualizará para el (los) destinatario(s) a medida que se escribe"; /* No comment provided by engineer. */ "Send delivery receipts to" = "Enviar confirmaciones de entrega a"; @@ -3120,25 +4493,34 @@ "send direct message" = "Enviar mensaje directo"; /* No comment provided by engineer. */ -"Send direct message" = "Enviar mensaje directo"; - -/* No comment provided by engineer. */ -"Send direct message to connect" = "Enviar mensaje directo para conectar"; +"Send direct message to connect" = "Envía un mensaje para conectar"; /* No comment provided by engineer. */ "Send disappearing message" = "Enviar mensaje temporal"; +/* No comment provided by engineer. */ +"Send errors" = "Errores de envío"; + /* No comment provided by engineer. */ "Send link previews" = "Enviar previsualizacion de enlaces"; /* No comment provided by engineer. */ "Send live message" = "Mensaje en vivo"; +/* No comment provided by engineer. */ +"Send message to enable calls." = "Enviar mensaje para activar llamadas."; + +/* No comment provided by engineer. */ +"Send messages directly when IP address is protected and your or destination server does not support private routing." = "Enviar mensajes directamente cuando tu dirección IP está protegida y tu servidor o el de destino no admitan enrutamiento privado."; + +/* No comment provided by engineer. */ +"Send messages directly when your or destination server does not support private routing." = "Enviar mensajes directamente cuando tu servidor o el de destino no admitan enrutamiento privado."; + /* No comment provided by engineer. */ "Send notifications" = "Enviar notificaciones"; /* No comment provided by engineer. */ -"Send notifications:" = "Enviar notificaciones:"; +"Send private reports" = "Envía informes privados"; /* No comment provided by engineer. */ "Send questions and ideas" = "Consultas y sugerencias"; @@ -3150,9 +4532,9 @@ "Send them from gallery or custom keyboards." = "Envíalos desde la galería o desde teclados personalizados."; /* No comment provided by engineer. */ -"Send up to 100 last messages to new members." = "Enviar hasta 100 últimos mensajes a los miembros nuevos."; +"Send up to 100 last messages to new members." = "Se envían hasta 100 mensajes más recientes a los miembros nuevos."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "El remitente ha cancelado la transferencia de archivos."; /* No comment provided by engineer. */ @@ -3188,15 +4570,57 @@ /* copied message info */ "Sent at: %@" = "Enviado: %@"; +/* No comment provided by engineer. */ +"Sent directly" = "Directamente"; + /* notification */ "Sent file event" = "Evento de archivo enviado"; /* message info title */ "Sent message" = "Mensaje saliente"; +/* No comment provided by engineer. */ +"Sent messages" = "Mensajes enviados"; + /* No comment provided by engineer. */ "Sent messages will be deleted after set time." = "Los mensajes enviados se eliminarán una vez transcurrido el tiempo establecido."; +/* No comment provided by engineer. */ +"Sent reply" = "Respuesta enviada"; + +/* No comment provided by engineer. */ +"Sent total" = "Total enviados"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Mediante proxy"; + +/* No comment provided by engineer. */ +"Server" = "Servidor"; + +/* alert message */ +"Server added to operator %@." = "Servidor añadido al operador %@."; + +/* No comment provided by engineer. */ +"Server address" = "Dirección del servidor"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "La dirección del servidor es incompatible con la configuración de la red: %@."; + +/* srv error text. */ +"Server address is incompatible with network settings." = "La dirección del servidor es incompatible con la configuración de la red."; + +/* alert title */ +"Server operator changed." = "El operador del servidor ha cambiado."; + +/* No comment provided by engineer. */ +"Server operators" = "Operadores de servidores"; + +/* alert title */ +"Server protocol changed." = "El protocolo del servidor ha cambiado."; + +/* queue info */ +"server queue info: %@\n\nlast received msg: %@" = "información cola del servidor: %1$@\n\núltimo mensaje recibido: %2$@"; + /* server test error */ "Server requires authorization to create queues, check password" = "El servidor requiere autorización para crear colas, comprueba la contraseña"; @@ -3204,35 +4628,62 @@ "Server requires authorization to upload, check password" = "El servidor requiere autorización para subir, comprueba la contraseña"; /* No comment provided by engineer. */ -"Server test failed!" = "¡Error en prueba del servidor!"; +"Server test failed!" = "¡Prueba no superada!"; + +/* No comment provided by engineer. */ +"Server type" = "Tipo de servidor"; + +/* srv error text */ +"Server version is incompatible with network settings." = "La versión del servidor es incompatible con la configuración de red."; + +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "La versión del servidor es incompatible con tu aplicación: %@."; /* No comment provided by engineer. */ "Servers" = "Servidores"; +/* No comment provided by engineer. */ +"Servers info" = "Info servidores"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "Las estadísticas de los servidores serán restablecidas. ¡No puede deshacerse!"; + /* No comment provided by engineer. */ "Session code" = "Código de sesión"; /* No comment provided by engineer. */ "Set 1 day" = "Establecer 1 día"; +/* No comment provided by engineer. */ +"Set chat name…" = "Nombre para el chat…"; + /* No comment provided by engineer. */ "Set contact name…" = "Escribe el nombre del contacto…"; +/* No comment provided by engineer. */ +"Set default theme" = "Establecer tema predeterminado"; + /* No comment provided by engineer. */ "Set group preferences" = "Establecer preferencias de grupo"; /* No comment provided by engineer. */ "Set it instead of system authentication." = "Úsalo en lugar de la autenticación del sistema."; +/* No comment provided by engineer. */ +"Set message expiration in chats." = "Establece el vencimiento para los mensajes en los chats."; + /* profile update event chat item */ "set new contact address" = "nueva dirección de contacto"; /* profile update event chat item */ -"set new profile picture" = "nueva imagen de perfil"; +"set new profile picture" = "tiene nueva imagen del perfil"; /* No comment provided by engineer. */ "Set passcode" = "Código autodestrucción"; +/* No comment provided by engineer. */ +"Set passphrase" = "Definir frase de contraseña"; + /* No comment provided by engineer. */ "Set passphrase to export" = "Escribe la contraseña para exportar"; @@ -3245,27 +4696,58 @@ /* No comment provided by engineer. */ "Settings" = "Configuración"; -/* chat item action */ +/* alert message */ +"Settings were changed." = "La configuración ha sido modificada."; + +/* No comment provided by engineer. */ +"Shape profile images" = "Dar forma a las imágenes de perfil"; + +/* alert action +chat item action */ "Share" = "Compartir"; /* No comment provided by engineer. */ "Share 1-time link" = "Compartir enlace de un uso"; +/* No comment provided by engineer. */ +"Share 1-time link with a friend" = "Compartir enlace de un uso con un amigo"; + /* No comment provided by engineer. */ "Share address" = "Compartir dirección"; /* No comment provided by engineer. */ +"Share address publicly" = "Campartir dirección públicamente"; + +/* alert title */ "Share address with contacts?" = "¿Compartir la dirección con los contactos?"; +/* No comment provided by engineer. */ +"Share from other apps." = "Comparte desde otras aplicaciones."; + /* No comment provided by engineer. */ "Share link" = "Compartir enlace"; /* No comment provided by engineer. */ -"Share this 1-time invite link" = "Compartir este enlace de un uso"; +"Share profile" = "Perfil a compartir"; + +/* No comment provided by engineer. */ +"Share SimpleX address on social media." = "Comparte tu dirección SimpleX en redes sociales."; + +/* No comment provided by engineer. */ +"Share this 1-time invite link" = "Comparte este enlace de un solo uso"; + +/* No comment provided by engineer. */ +"Share to SimpleX" = "Compartir con Simplex"; /* No comment provided by engineer. */ "Share with contacts" = "Compartir con contactos"; +/* No comment provided by engineer. */ +"Short link" = "Enlace corto"; + +/* No comment provided by engineer. */ +"Show → on messages sent via private routing." = "Mostrar → en mensajes con enrutamiento privado."; + /* No comment provided by engineer. */ "Show calls in phone history" = "Mostrar llamadas en el historial del teléfono"; @@ -3275,18 +4757,42 @@ /* No comment provided by engineer. */ "Show last messages" = "Mostrar último mensaje"; +/* No comment provided by engineer. */ +"Show message status" = "Estado del mensaje"; + +/* No comment provided by engineer. */ +"Show percentage" = "Mostrar porcentajes"; + /* No comment provided by engineer. */ "Show preview" = "Mostrar vista previa"; +/* No comment provided by engineer. */ +"Show QR code" = "Mostrar código QR"; + /* No comment provided by engineer. */ "Show:" = "Mostrar:"; +/* No comment provided by engineer. */ +"SimpleX" = "SimpleX"; + /* No comment provided by engineer. */ "SimpleX address" = "Dirección SimpleX"; /* No comment provided by engineer. */ "SimpleX Address" = "Dirección SimpleX"; +/* No comment provided by engineer. */ +"SimpleX address and 1-time links are safe to share via any messenger." = "Compartir enlaces de un solo uso y direcciones SimpleX es seguro a través de cualquier medio."; + +/* No comment provided by engineer. */ +"SimpleX address or 1-time link?" = "¿Dirección SimpleX o enlace de un uso?"; + +/* simplex link type */ +"SimpleX channel link" = "Enlace de canal SimpleX"; + +/* No comment provided by engineer. */ +"SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app." = "Simplex Chat y Flux han acordado incluir en la aplicación servidores operados por Flux."; + /* No comment provided by engineer. */ "SimpleX Chat security was audited by Trail of Bits." = "La seguridad de SimpleX Chat ha sido auditada por Trail of Bits."; @@ -3299,9 +4805,15 @@ /* simplex link type */ "SimpleX group link" = "Enlace de grupo SimpleX"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "Enlaces SimpleX"; +/* No comment provided by engineer. */ +"SimpleX links are prohibited." = "Los enlaces SimpleX no se permiten en este grupo."; + +/* No comment provided by engineer. */ +"SimpleX links not allowed" = "Enlaces SimpleX no permitidos"; + /* No comment provided by engineer. */ "SimpleX Lock" = "Bloqueo SimpleX"; @@ -3317,9 +4829,15 @@ /* simplex link type */ "SimpleX one-time invitation" = "Invitación SimpleX de un uso"; +/* No comment provided by engineer. */ +"SimpleX protocols reviewed by Trail of Bits." = "Protocolos de SimpleX auditados por Trail of Bits."; + /* No comment provided by engineer. */ "Simplified incognito mode" = "Modo incógnito simplificado"; +/* No comment provided by engineer. */ +"Size" = "Tamaño"; + /* No comment provided by engineer. */ "Skip" = "Omitir"; @@ -3327,17 +4845,45 @@ "Skipped messages" = "Mensajes omitidos"; /* No comment provided by engineer. */ -"Small groups (max 20)" = "Grupos pequeños (máx. 20)"; +"Small groups (max 20)" = "Grupos pequeños (max. 20)"; /* No comment provided by engineer. */ -"SMP servers" = "Servidores SMP"; +"SMP server" = "Servidor SMP"; + +/* No comment provided by engineer. */ +"SOCKS proxy" = "Proxy SOCKS"; + +/* blur media */ +"Soft" = "Suave"; + +/* No comment provided by engineer. */ +"Some app settings were not migrated." = "Algunas configuraciones de la app no han sido migradas."; + +/* No comment provided by engineer. */ +"Some file(s) were not exported:" = "Algunos archivos no han sido exportados:"; /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "Algunos errores no críticos ocurrieron durante la importación - para más detalles puedes ver la consola de Chat."; +/* No comment provided by engineer. */ +"Some non-fatal errors occurred during import:" = "Han ocurrido algunos errores no críticos durante la importación:"; + +/* alert message */ +"Some servers failed the test:\n%@" = "Algunos servidores no han superado la prueba:\n%@"; + /* notification title */ "Somebody" = "Alguien"; +/* blocking reason +report reason */ +"Spam" = "Spam"; + +/* No comment provided by engineer. */ +"Square, circle, or anything in between." = "Cuadrada, circular o cualquier forma intermedia."; + +/* chat item text */ +"standard end-to-end encryption" = "cifrado estándar de extremo a extremo"; + /* No comment provided by engineer. */ "Start chat" = "Iniciar chat"; @@ -3347,20 +4893,26 @@ /* No comment provided by engineer. */ "Start migration" = "Iniciar migración"; +/* No comment provided by engineer. */ +"Starting from %@." = "Iniciado el %@."; + /* No comment provided by engineer. */ "starting…" = "inicializando…"; /* No comment provided by engineer. */ -"Stop" = "Detener"; +"Statistics" = "Estadísticas"; /* No comment provided by engineer. */ -"Stop chat to enable database actions" = "Detén SimpleX para habilitar las acciones sobre la base de datos"; +"Stop" = "Parar"; /* No comment provided by engineer. */ -"Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Para poder exportar, importar o eliminar la base de datos primero debes detener Chat. Durante el tiempo que esté detenido no podrás recibir ni enviar mensajes."; +"Stop chat" = "Parar SimpleX"; /* No comment provided by engineer. */ -"Stop chat?" = "¿Detener Chat?"; +"Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Para exportar, importar o eliminar la base de datos debes parar SimpleX. Mientra tanto no podrás enviar ni recibir mensajes."; + +/* No comment provided by engineer. */ +"Stop chat?" = "¿Parar Chat?"; /* cancel file action */ "Stop file" = "Detener archivo"; @@ -3371,36 +4923,66 @@ /* No comment provided by engineer. */ "Stop sending file?" = "¿Dejar de enviar el archivo?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "Dejar de compartir"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "¿Dejar de compartir la dirección?"; /* authentication reason */ -"Stop SimpleX" = "Detener SimpleX"; +"Stop SimpleX" = "Parar SimpleX"; + +/* No comment provided by engineer. */ +"Stopping chat" = "Parando chat"; + +/* No comment provided by engineer. */ +"Storage" = "Almacenamiento"; /* No comment provided by engineer. */ "strike" = "tachado"; +/* blur media */ +"Strong" = "Fuerte"; + /* No comment provided by engineer. */ "Submit" = "Enviar"; +/* No comment provided by engineer. */ +"Subscribed" = "Suscritas"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Errores de suscripción"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Suscripciones ignoradas"; + /* No comment provided by engineer. */ "Support SimpleX Chat" = "Soporte SimpleX Chat"; +/* No comment provided by engineer. */ +"Switch audio and video during the call." = "Intercambia audio y video durante la llamada."; + +/* No comment provided by engineer. */ +"Switch chat profile for 1-time invitations." = "Cambia el perfil de chat para invitaciones de un solo uso."; + /* No comment provided by engineer. */ "System" = "Sistema"; /* No comment provided by engineer. */ "System authentication" = "Autenticación del sistema"; +/* No comment provided by engineer. */ +"Tail" = "Cola"; + /* No comment provided by engineer. */ "Take picture" = "Tomar foto"; /* No comment provided by engineer. */ "Tap button " = "Pulsa el botón "; +/* No comment provided by engineer. */ +"Tap Create SimpleX address in the menu to create it later." = "Pulsa Crear dirección SimpleX en el menú para crearla más tarde."; + /* No comment provided by engineer. */ "Tap to activate profile." = "Pulsa sobre un perfil para activarlo."; @@ -3414,16 +4996,19 @@ "Tap to join incognito" = "Pulsa para unirte en modo incógnito"; /* No comment provided by engineer. */ -"Tap to paste link" = "Pulsa para pegar enlace"; +"Tap to paste link" = "Pulsa para pegar el enlacePulsa para pegar enlace"; /* No comment provided by engineer. */ "Tap to scan" = "Pulsa para escanear"; /* No comment provided by engineer. */ -"Tap to start a new chat" = "Pulsa para iniciar chat nuevo"; +"TCP connection" = "Conexión TCP"; /* No comment provided by engineer. */ -"TCP connection timeout" = "Tiempo de espera de la conexión TCP agotado"; +"TCP connection timeout" = "Timeout de la conexión TCP"; + +/* No comment provided by engineer. */ +"TCP port for messaging" = "Puerto TCP para mensajes"; /* No comment provided by engineer. */ "TCP_KEEPCNT" = "TCP_KEEPCNT"; @@ -3434,8 +5019,14 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* file error alert title */ +"Temporary file error" = "Error en archivo temporal"; + /* server test failure */ -"Test failed at step %@." = "La prueba ha fallado en el paso %@."; +"Test failed at step %@." = "Prueba no superada en el paso %@."; + +/* No comment provided by engineer. */ +"Test notifications" = "Probar notificaciones"; /* No comment provided by engineer. */ "Test server" = "Probar servidor"; @@ -3443,29 +5034,35 @@ /* No comment provided by engineer. */ "Test servers" = "Probar servidores"; -/* No comment provided by engineer. */ -"Tests failed!" = "¡Pruebas fallidas!"; +/* alert title */ +"Tests failed!" = "¡Pruebas no superadas!"; /* No comment provided by engineer. */ "Thank you for installing SimpleX Chat!" = "¡Gracias por instalar SimpleX Chat!"; /* No comment provided by engineer. */ -"Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Gracias a los usuarios: [contribuye vía Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#traducir-el-aplicaciones)!"; +"Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "¡Nuestro agradecimiento a todos los colaboradores, [puedes contribuir a través de Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#traducir-el-aplicaciones)!"; /* No comment provided by engineer. */ -"Thanks to the users – contribute via Weblate!" = "¡Gracias a los colaboradores! Contribuye a través de Weblate."; +"Thanks to the users – contribute via Weblate!" = "¡Nuestro agradecimiento a todos los colaboradores! Puedes contribuir a través de Weblate"; /* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "La primera plataforma sin identificadores de usuario: diseñada para la privacidad."; +"The app can notify you when you receive messages or contact requests - please open settings to enable." = "La aplicación puede notificarte cuando recibas mensajes o solicitudes de contacto: por favor, abre la configuración para activarlo."; /* No comment provided by engineer. */ -"The app can notify you when you receive messages or contact requests - please open settings to enable." = "La aplicación puede notificarte cuando recibas mensajes o solicitudes de contacto: abre la configuración para habilitar."; +"The app protects your privacy by using different operators in each conversation." = "La aplicación protege tu privacidad mediante el uso de diferentes operadores en cada conversación."; + +/* No comment provided by engineer. */ +"The app will ask to confirm downloads from unknown file servers (except .onion)." = "La aplicación pedirá que confirmes las descargas desde servidores de archivos desconocidos (excepto si son .onion)."; /* No comment provided by engineer. */ "The attempt to change database passphrase was not completed." = "El intento de cambiar la contraseña de la base de datos no se ha completado."; /* No comment provided by engineer. */ -"The code you scanned is not a SimpleX link QR code." = "El código QR escaneado no es un enlace SimpleX."; +"The code you scanned is not a SimpleX link QR code." = "El código QR escaneado no es un enlace de SimpleX."; + +/* No comment provided by engineer. */ +"The connection reached the limit of undelivered messages, your contact may be offline." = "La conexión ha alcanzado el límite de mensajes no entregados. es posible que tu contacto esté desconectado."; /* No comment provided by engineer. */ "The connection you accepted will be cancelled!" = "¡La conexión que has aceptado se cancelará!"; @@ -3479,6 +5076,9 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "El cifrado funciona y un cifrado nuevo no es necesario. ¡Podría dar lugar a errores de conexión!"; +/* No comment provided by engineer. */ +"The future of messaging" = "La nueva generación de mensajería privada"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "El hash del mensaje anterior es diferente."; @@ -3492,13 +5092,22 @@ "The message will be marked as moderated for all members." = "El mensaje será marcado como moderado para todos los miembros."; /* No comment provided by engineer. */ -"The next generation of private messaging" = "La nueva generación de mensajería privada"; +"The messages will be deleted for all members." = "Los mensajes serán eliminados para todos los miembros."; + +/* No comment provided by engineer. */ +"The messages will be marked as moderated for all members." = "Los mensajes serán marcados como moderados para todos los miembros."; /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "La base de datos antigua no se eliminó durante la migración, puede eliminarse."; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "El perfil sólo se comparte con tus contactos."; +"Your profile is stored on your device and only shared with your contacts." = "El perfil sólo se comparte con tus contactos."; + +/* No comment provided by engineer. */ +"The same conditions will apply to operator **%@**." = "Las mismas condiciones se aplicarán al operador **%@**."; + +/* No comment provided by engineer. */ +"The second preset operator in the app!" = "¡Segundo operador predefinido!"; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "¡El doble check que nos faltaba! ✅"; @@ -3507,29 +5116,47 @@ "The sender will NOT be notified" = "El remitente NO será notificado"; /* No comment provided by engineer. */ -"The servers for new connections of your current chat profile **%@**." = "Lista de servidores para las conexiones nuevas de tu perfil actual **%@**."; +"The servers for new connections of your current chat profile **%@**." = "Servidores para conexiones nuevas en tu perfil **%@**."; /* No comment provided by engineer. */ -"The text you pasted is not a SimpleX link." = "El texto pegado no es un enlace SimpleX."; +"The servers for new files of your current chat profile **%@**." = "Servidores para enviar archivos en tu perfil **%@**."; /* No comment provided by engineer. */ -"Theme" = "Tema"; +"The text you pasted is not a SimpleX link." = "El texto pegado no es un enlace de SimpleX."; + +/* No comment provided by engineer. */ +"The uploaded database archive will be permanently removed from the servers." = "El archivo de bases de datos subido será eliminado permanentemente de los servidores."; + +/* No comment provided by engineer. */ +"Themes" = "Temas"; + +/* No comment provided by engineer. */ +"These conditions will also apply for: **%@**." = "Estas condiciones también se aplican para: **%@**."; /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Esta configuración afecta a tu perfil actual **%@**."; /* No comment provided by engineer. */ -"They can be overridden in contact and group settings." = "Se pueden anular en la configuración de contactos."; +"They can be overridden in contact and group settings." = "Se puede modificar desde la configuración particular de cada grupo y contacto."; /* No comment provided by engineer. */ "This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Esta acción es irreversible. Se eliminarán todos los archivos y multimedia recibidos y enviados. Las imágenes de baja resolución permanecerán."; /* No comment provided by engineer. */ -"This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Esta acción es irreversible. Se eliminarán los mensajes enviados y recibidos anteriores a la selección. Puede tardar varios minutos."; +"This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Esta acción es irreversible. Se eliminarán los mensajes enviados y recibidos anteriores a la selección. Podría tardar varios minutos."; + +/* alert message */ +"This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted." = "Todos los mensajes previos al período seleccionado serán eliminados del chat. ¡No puede deshacerse!"; /* No comment provided by engineer. */ "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Esta acción es irreversible. Tu perfil, contactos, mensajes y archivos se perderán irreversiblemente."; +/* E2EE info chat item */ +"This chat is protected by end-to-end encryption." = "Este chat está protegido por cifrado de extremo a extremo."; + +/* E2EE info chat item */ +"This chat is protected by quantum resistant end-to-end encryption." = "Este chat está protegido por cifrado de extremo a extremo resistente a tecnología cuántica."; + /* notification title */ "this contact" = "este contacto"; @@ -3551,9 +5178,21 @@ /* No comment provided by engineer. */ "This is your own SimpleX address!" = "¡Esta es tu propia dirección SimpleX!"; +/* No comment provided by engineer. */ +"This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link." = "Este enlace requiere una versión más reciente de la aplicación. Por favor, actualiza la aplicación o pide a tu contacto un enlace compatible."; + +/* No comment provided by engineer. */ +"This link was used with another mobile device, please create a new link on the desktop." = "Este enlace ha sido usado en otro dispositivo móvil, por favor crea un enlace nuevo en el ordenador."; + +/* No comment provided by engineer. */ +"This message was deleted or not received yet." = "El mensaje ha sido eliminado o aún no se ha recibido."; + /* No comment provided by engineer. */ "This setting applies to messages in your current chat profile **%@**." = "Esta configuración se aplica a los mensajes del perfil actual **%@**."; +/* No comment provided by engineer. */ +"Title" = "Título"; + /* No comment provided by engineer. */ "To ask any questions and to receive updates:" = "Para consultar cualquier duda y recibir actualizaciones:"; @@ -3567,7 +5206,7 @@ "To make a new connection" = "Para hacer una conexión nueva"; /* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Para proteger tu privacidad, en lugar de los identificadores de usuario que usan el resto de plataformas, SimpleX dispone de identificadores para las colas de mensajes, independientes para cada uno de tus contactos."; +"To protect against your link being replaced, you can compare contact security codes." = "Para protegerte contra una sustitución del enlace, puedes comparar los códigos de seguridad con tu contacto."; /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "Para proteger la zona horaria, los archivos de imagen/voz usan la hora UTC."; @@ -3575,24 +5214,60 @@ /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Para proteger tu información, activa el Bloqueo SimpleX.\nSe te pedirá que completes la autenticación antes de activar esta función."; +/* No comment provided by engineer. */ +"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Para proteger tu dirección IP, el enrutamiento privado usa tu lista de servidores SMP para enviar mensajes."; + +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "Para proteger tu privacidad, SimpleX usa identificadores distintos para cada uno de tus contactos."; + +/* No comment provided by engineer. */ +"To receive" = "Para recibir"; + +/* No comment provided by engineer. */ +"To record speech please grant permission to use Microphone." = "Para grabación de voz, por favor concede el permiso para usar el micrófono."; + +/* No comment provided by engineer. */ +"To record video please grant permission to use Camera." = "Para grabación de vídeo, por favor concede el permiso para usar la cámara."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Para grabar el mensaje de voz concede permiso para usar el micrófono."; /* No comment provided by engineer. */ "To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Para hacer visible tu perfil oculto, introduce la contraseña en el campo de búsqueda del menú **Mis perfiles**."; +/* No comment provided by engineer. */ +"To send" = "Para enviar"; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Para permitir las notificaciones automáticas instantáneas, la base de datos se debe migrar."; +/* No comment provided by engineer. */ +"To use the servers of **%@**, accept conditions of use." = "Para usar los servidores de **%@**, debes aceptar las condiciones de uso."; + /* No comment provided by engineer. */ "To verify end-to-end encryption with your contact compare (or scan) the code on your devices." = "Para verificar el cifrado de extremo a extremo con tu contacto, compara (o escanea) el código en ambos dispositivos."; +/* No comment provided by engineer. */ +"Toggle chat list:" = "Alternar lista de chats:"; + /* No comment provided by engineer. */ "Toggle incognito when connecting." = "Activa incógnito al conectar."; +/* token status */ +"Token status: %@." = "Estado token: %@."; + +/* No comment provided by engineer. */ +"Toolbar opacity" = "Opacidad barra"; + +/* No comment provided by engineer. */ +"Total" = "Total"; + /* No comment provided by engineer. */ "Transport isolation" = "Aislamiento de transporte"; +/* No comment provided by engineer. */ +"Transport sessions" = "Sesiones de transporte"; + /* No comment provided by engineer. */ "Trying to connect to the server used to receive messages from this contact (error: %@)." = "Intentando conectar con el servidor usado para recibir mensajes de este contacto (error: %@)."; @@ -3621,21 +5296,21 @@ "Unblock member" = "Desbloquear miembro"; /* No comment provided by engineer. */ -"Unblock member for all?" = "¿Desbloquear miembro para todos?"; +"Unblock member for all?" = "¿Desbloquear el miembro para todos?"; /* No comment provided by engineer. */ "Unblock member?" = "¿Desbloquear miembro?"; /* rcv group event chat item */ -"unblocked %@" = "%@ desbloqueado"; +"unblocked %@" = "ha desbloqueado a %@"; -/* item status description */ -"Unexpected error: %@" = "Error inesperado: %@"; +/* No comment provided by engineer. */ +"Undelivered messages" = "Mensajes no entregados"; /* No comment provided by engineer. */ "Unexpected migration state" = "Estado de migración inesperado"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "No fav."; /* No comment provided by engineer. */ @@ -3662,6 +5337,12 @@ /* No comment provided by engineer. */ "Unknown error" = "Error desconocido"; +/* No comment provided by engineer. */ +"unknown servers" = "con servidores desconocidos"; + +/* alert title */ +"Unknown servers!" = "¡Servidores desconocidos!"; + /* No comment provided by engineer. */ "unknown status" = "estado desconocido"; @@ -3669,7 +5350,7 @@ "Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions." = "A menos que utilices la interfaz de llamadas de iOS, activa el modo No molestar para evitar interrupciones."; /* No comment provided by engineer. */ -"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "A menos que tu contacto haya eliminado la conexión o\nque este enlace ya se haya usado, podría ser un error. Por favor, notifícalo.\nPara conectarte, pide a tu contacto que cree otro enlace de conexión y comprueba que tienes buena conexión de red."; +"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "A menos que tu contacto haya eliminado la conexión o el enlace se haya usado, podría ser un error. Por favor, notifícalo.\nPara conectarte pide a tu contacto que cree otro enlace y comprueba la conexión de red."; /* No comment provided by engineer. */ "Unlink" = "Desenlazar"; @@ -3683,21 +5364,24 @@ /* authentication reason */ "Unlock app" = "Desbloquear aplicación"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "Activar audio"; /* No comment provided by engineer. */ +"unprotected" = "con IP desprotegida"; + +/* swipe action */ "Unread" = "No leído"; +/* No comment provided by engineer. */ +"Unsupported connection link" = "Enlace de conexión no compatible"; + /* No comment provided by engineer. */ "Up to 100 last messages are sent to new members." = "Hasta 100 últimos mensajes son enviados a los miembros nuevos."; /* No comment provided by engineer. */ "Update" = "Actualizar"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = "¿Actualizar la configuración de los hosts .onion?"; - /* No comment provided by engineer. */ "Update database passphrase" = "Actualizar contraseña de la base de datos"; @@ -3705,7 +5389,10 @@ "Update network settings?" = "¿Actualizar la configuración de red?"; /* No comment provided by engineer. */ -"Update transport isolation mode?" = "¿Actualizar el modo de aislamiento de transporte?"; +"Update settings?" = "¿Actualizar configuración?"; + +/* No comment provided by engineer. */ +"Updated conditions" = "Condiciones actualizadas"; /* rcv group event chat item */ "updated group profile" = "ha actualizado el perfil del grupo"; @@ -3714,20 +5401,35 @@ "updated profile" = "perfil actualizado"; /* No comment provided by engineer. */ -"Updating settings will re-connect the client to all servers." = "Al actualizar la configuración el cliente se reconectará a todos los servidores."; - -/* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "Al actualizar esta configuración el cliente se reconectará a todos los servidores."; +"Updating settings will re-connect the client to all servers." = "Para actualizar la configuración el cliente se reconectará a todos los servidores."; /* No comment provided by engineer. */ "Upgrade and open chat" = "Actualizar y abrir Chat"; +/* No comment provided by engineer. */ +"Upload errors" = "Errores en subida"; + +/* No comment provided by engineer. */ +"Upload failed" = "Error de subida"; + /* server test step */ "Upload file" = "Subir archivo"; +/* No comment provided by engineer. */ +"Uploaded" = "Subido"; + +/* No comment provided by engineer. */ +"Uploaded files" = "Archivos subidos"; + +/* No comment provided by engineer. */ +"Uploading archive" = "Subiendo archivo"; + /* No comment provided by engineer. */ "Use .onion hosts" = "Usar hosts .onion"; +/* No comment provided by engineer. */ +"Use %@" = "Usar %@"; + /* No comment provided by engineer. */ "Use chat" = "Usar Chat"; @@ -3735,7 +5437,13 @@ "Use current profile" = "Usar perfil actual"; /* No comment provided by engineer. */ -"Use for new connections" = "Usar para conexiones nuevas"; +"Use for files" = "Uso para archivos"; + +/* No comment provided by engineer. */ +"Use for messages" = "Uso para mensajes"; + +/* No comment provided by engineer. */ +"Use for new connections" = "Para conexiones nuevas"; /* No comment provided by engineer. */ "Use from desktop" = "Usar desde ordenador"; @@ -3749,17 +5457,47 @@ /* No comment provided by engineer. */ "Use only local notifications?" = "¿Usar sólo notificaciones locales?"; +/* No comment provided by engineer. */ +"Use private routing with unknown servers when IP address is not protected." = "Usar enrutamiento privado con servidores desconocidos cuando tu dirección IP no está protegida."; + +/* No comment provided by engineer. */ +"Use private routing with unknown servers." = "Usar enrutamiento privado con servidores de mensaje desconocidos."; + /* No comment provided by engineer. */ "Use server" = "Usar servidor"; +/* No comment provided by engineer. */ +"Use servers" = "Usar servidores"; + +/* No comment provided by engineer. */ +"Use short links (BETA)" = "Usar enlaces cortos (BETA)"; + /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "¿Usar servidores SimpleX Chat?"; /* No comment provided by engineer. */ -"User profile" = "Perfil de usuario"; +"Use SOCKS proxy" = "Usar proxy SOCKS"; /* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = "Usar hosts .onion requiere un proveedor VPN compatible."; +"Use TCP port %@ when no port is specified." = "Se usa el puerto TCP %@ cuando no se ha especificado otro."; + +/* No comment provided by engineer. */ +"Use TCP port 443 for preset servers only." = "Usar puerto TCP 443 solo en servidores predefinidos."; + +/* No comment provided by engineer. */ +"Use the app while in the call." = "Usar la aplicación durante la llamada."; + +/* No comment provided by engineer. */ +"Use the app with one hand." = "Usa la aplicación con una sola mano."; + +/* No comment provided by engineer. */ +"Use web port" = "Usar puerto web"; + +/* No comment provided by engineer. */ +"User selection" = "Selección de usuarios"; + +/* No comment provided by engineer. */ +"Username" = "Nombre de usuario"; /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Usar servidores SimpleX Chat."; @@ -3782,6 +5520,12 @@ /* No comment provided by engineer. */ "Verify connections" = "Verificar conexiones"; +/* No comment provided by engineer. */ +"Verify database passphrase" = "Verificar la contraseña de la base de datos"; + +/* No comment provided by engineer. */ +"Verify passphrase" = "Verificar frase de contraseña"; + /* No comment provided by engineer. */ "Verify security code" = "Comprobar código de seguridad"; @@ -3803,6 +5547,9 @@ /* No comment provided by engineer. */ "Via secure quantum resistant protocol." = "Mediante protocolo seguro de resistencia cuántica."; +/* No comment provided by engineer. */ +"video" = "video"; + /* No comment provided by engineer. */ "Video call" = "Videollamada"; @@ -3810,17 +5557,23 @@ "video call (not e2e encrypted)" = "videollamada (sin cifrar)"; /* No comment provided by engineer. */ -"Video will be received when your contact completes uploading it." = "El video se recibirá cuando tu contacto termine de subirlo."; +"Video will be received when your contact completes uploading it." = "El video se recibirá cuando el contacto termine de subirlo."; /* No comment provided by engineer. */ -"Video will be received when your contact is online, please wait or check later!" = "El vídeo se recibirá cuando tu contacto esté en línea, por favor espera o compruébalo más tarde."; +"Video will be received when your contact is online, please wait or check later!" = "El vídeo se recibirá cuando el contacto esté en línea, por favor espera o revisa más tarde."; /* No comment provided by engineer. */ "Videos and files up to 1gb" = "Vídeos y archivos de hasta 1Gb"; +/* No comment provided by engineer. */ +"View conditions" = "Ver condiciones"; + /* No comment provided by engineer. */ "View security code" = "Mostrar código de seguridad"; +/* No comment provided by engineer. */ +"View updated conditions" = "Ver condiciones actualizadas"; + /* chat feature */ "Visible history" = "Historial visible"; @@ -3834,7 +5587,10 @@ "Voice messages are prohibited in this chat." = "Los mensajes de voz no están permitidos en este chat."; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "Los mensajes de voz no están permitidos en este grupo."; +"Voice messages are prohibited." = "Los mensajes de voz no están permitidos en este grupo."; + +/* No comment provided by engineer. */ +"Voice messages not allowed" = "Mensajes de voz no permitidos"; /* No comment provided by engineer. */ "Voice messages prohibited!" = "¡Mensajes de voz no permitidos!"; @@ -3857,9 +5613,18 @@ /* No comment provided by engineer. */ "Waiting for video" = "Esperando el vídeo"; +/* No comment provided by engineer. */ +"Wallpaper accent" = "Color imagen de fondo"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Color de fondo"; + /* No comment provided by engineer. */ "wants to connect to you!" = "¡quiere contactar contigo!"; +/* No comment provided by engineer. */ +"Warning: starting chat on multiple devices is not supported and will cause message delivery failures" = "Atención: el inicio del chat en varios dispositivos es incompatible y provocará fallos en la entrega de mensajes"; + /* No comment provided by engineer. */ "Warning: you may lose some data!" = "Atención: ¡puedes perder algunos datos!"; @@ -3875,6 +5640,9 @@ /* No comment provided by engineer. */ "Welcome message" = "Mensaje de bienvenida"; +/* No comment provided by engineer. */ +"Welcome message is too long" = "Mensaje de bienvenida demasiado largo"; + /* No comment provided by engineer. */ "What's new" = "Novedades"; @@ -3882,11 +5650,26 @@ "When available" = "Si disponibles"; /* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "Cuando alguien solicite conectarse podrás aceptar o rechazar la solicitud."; +"When connecting audio and video calls." = "Al iniciar llamadas de audio y vídeo."; + +/* No comment provided by engineer. */ +"when IP hidden" = "con IP oculta"; + +/* No comment provided by engineer. */ +"When more than one operator is enabled, none of them has metadata to learn who communicates with whom." = "Cuando está habilitado más de un operador, ninguno dispone de los metadatos para conocer quién se comunica con quién."; /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Cuando compartes un perfil incógnito con alguien, este perfil también se usará para los grupos a los que te inviten."; +/* No comment provided by engineer. */ +"WiFi" = "WiFi"; + +/* No comment provided by engineer. */ +"Will be enabled in direct chats!" = "¡Será habilitado en los chats directos!"; + +/* No comment provided by engineer. */ +"Wired ethernet" = "Ethernet por cable"; + /* No comment provided by engineer. */ "With encrypted files and media." = "Con cifrado de archivos y multimedia."; @@ -3896,20 +5679,35 @@ /* No comment provided by engineer. */ "With reduced battery usage." = "Con uso reducido de batería."; +/* No comment provided by engineer. */ +"Without Tor or VPN, your IP address will be visible to file servers." = "Sin Tor o VPN, tu dirección IP será visible para los servidores de archivos."; + +/* alert message */ +"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Sin Tor o VPN, tu dirección IP será visible para estos servidores XFTP: %@."; + /* No comment provided by engineer. */ "Wrong database passphrase" = "Contraseña de base de datos incorrecta"; +/* snd error text */ +"Wrong key or unknown connection - most likely this connection is deleted." = "Clave incorrecta o conexión desconocida - probablemente esta conexión fue eliminada."; + +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Clave incorrecta o dirección del bloque del archivo desconocida. Es probable que el archivo se haya eliminado."; + /* No comment provided by engineer. */ "Wrong passphrase!" = "¡Contraseña incorrecta!"; /* No comment provided by engineer. */ -"XFTP servers" = "Servidores XFTP"; +"XFTP server" = "Servidor XFTP"; /* pref value */ "yes" = "sí"; /* No comment provided by engineer. */ -"You" = "Tú"; +"you" = "tu"; + +/* No comment provided by engineer. */ +"You **must not** use the same database on two devices." = "**No debes** usar la misma base de datos en dos dispositivos."; /* No comment provided by engineer. */ "You accepted connection" = "Has aceptado la conexión"; @@ -3921,7 +5719,10 @@ "You already have a chat profile with the same display name. Please choose another name." = "Ya tienes un perfil con este nombre mostrado. Por favor, elige otro nombre."; /* No comment provided by engineer. */ -"You are already connected to %@." = "Ya estás conectado a %@."; +"You are already connected to %@." = "Ya estás conectado con %@."; + +/* No comment provided by engineer. */ +"You are already connected with %@." = "Ya estás conectado con %@."; /* No comment provided by engineer. */ "You are already connecting to %@." = "Ya estás conectando con %@."; @@ -3953,6 +5754,9 @@ /* No comment provided by engineer. */ "You are invited to group" = "Has sido invitado a un grupo"; +/* No comment provided by engineer. */ +"You are not connected to these servers. Private routing is used to deliver messages to them." = "No tienes conexión directa a estos servidores. Los mensajes destinados a estos usan enrutamiento privado."; + /* No comment provided by engineer. */ "you are observer" = "Tu rol es observador"; @@ -3962,6 +5766,12 @@ /* No comment provided by engineer. */ "You can accept calls from lock screen, without device and app authentication." = "Puede aceptar llamadas desde la pantalla de bloqueo, sin autenticación de dispositivos y aplicaciones."; +/* No comment provided by engineer. */ +"You can change it in Appearance settings." = "Puedes cambiar la posición de la barra desde el menú Apariencia."; + +/* No comment provided by engineer. */ +"You can configure servers via settings." = "Puedes configurar los servidores a través de su configuración."; + /* No comment provided by engineer. */ "You can create it later" = "Puedes crearla más tarde"; @@ -3971,6 +5781,9 @@ /* No comment provided by engineer. */ "You can enable them later via app Privacy & Security settings." = "Puedes activarlos más tarde en la configuración de Privacidad y Seguridad."; +/* No comment provided by engineer. */ +"You can give another try." = "Puedes intentarlo de nuevo."; + /* No comment provided by engineer. */ "You can hide or mute a user profile - swipe it to the right." = "Puedes ocultar o silenciar un perfil deslizándolo a la derecha."; @@ -3978,7 +5791,13 @@ "You can make it visible to your SimpleX contacts via Settings." = "Puedes hacerlo visible para tus contactos de SimpleX en Configuración."; /* notification body */ -"You can now send messages to %@" = "Ya puedes enviar mensajes a %@"; +"You can now chat with %@" = "Ya puedes chatear con %@"; + +/* No comment provided by engineer. */ +"You can send messages to %@ from Archived contacts." = "Puedes enviar mensajes a %@ desde Contactos archivados."; + +/* No comment provided by engineer. */ +"You can set connection name, to remember who the link was shared with." = "Puedes añadir un nombre a la conexión para recordar a quién corresponde."; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "Puedes configurar las notificaciones de la pantalla de bloqueo desde Configuración."; @@ -3990,10 +5809,10 @@ "You can share this address with your contacts to let them connect with **%@**." = "Puedes compartir esta dirección con tus contactos para que puedan conectar con **%@**."; /* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "Puedes compartir tu dirección como enlace o código QR para que cualquiera pueda conectarse contigo."; +"You can start chat via app Settings / Database or by restarting the app" = "Puede iniciar Chat a través de la Configuración / Base de datos de la aplicación o reiniciando la aplicación"; /* No comment provided by engineer. */ -"You can start chat via app Settings / Database or by restarting the app" = "Puede iniciar Chat a través de la Configuración / Base de datos de la aplicación o reiniciando la aplicación"; +"You can still view conversation with %@ in the list of chats." = "Aún puedes ver la conversación con %@ en la lista de chats."; /* No comment provided by engineer. */ "You can turn on SimpleX Lock via Settings." = "Puedes activar el Bloqueo SimpleX a través de Configuración."; @@ -4001,7 +5820,7 @@ /* No comment provided by engineer. */ "You can use markdown to format messages:" = "Puedes usar la sintaxis markdown para dar formato a tus mensajes:"; -/* No comment provided by engineer. */ +/* alert message */ "You can view invitation link again in connection details." = "Podrás ver el enlace de invitación en detalles de conexión."; /* No comment provided by engineer. */ @@ -4020,10 +5839,10 @@ "you changed role of %@ to %@" = "has cambiado el rol de %1$@ a %2$@"; /* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "Tú controlas a través de qué servidor(es) **recibes** los mensajes. Tus contactos controlan a través de qué servidor(es) **envías** tus mensajes."; +"You could not be verified; please try again." = "No has podido ser autenticado. Inténtalo de nuevo."; /* No comment provided by engineer. */ -"You could not be verified; please try again." = "No has podido ser autenticado. Inténtalo de nuevo."; +"You decide who can connect." = "Tu decides quién se conecta."; /* No comment provided by engineer. */ "You have already requested connection via this address!" = "¡Ya has solicitado la conexión mediante esta dirección!"; @@ -4031,9 +5850,6 @@ /* No comment provided by engineer. */ "You have already requested connection!\nRepeat connection request?" = "Ya has solicitado la conexión\n¿Repetir solicitud?"; -/* No comment provided by engineer. */ -"You have no chats" = "No tienes chats"; - /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "La contraseña no se almacena en el dispositivo, tienes que introducirla cada vez que inicies la aplicación."; @@ -4049,9 +5865,18 @@ /* snd group event chat item */ "you left" = "has salido"; +/* No comment provided by engineer. */ +"You may migrate the exported database." = "Puedes migrar la base de datos exportada."; + +/* No comment provided by engineer. */ +"You may save the exported archive." = "Puedes guardar el archivo exportado."; + /* No comment provided by engineer. */ "You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." = "Debes usar la versión más reciente de tu base de datos ÚNICAMENTE en un dispositivo, de lo contrario podrías dejar de recibir mensajes de algunos contactos."; +/* No comment provided by engineer. */ +"You need to allow your contact to call to be able to call them." = "Debes permitir que tus contacto te llamen para poder llamarles."; + /* No comment provided by engineer. */ "You need to allow your contact to send voice messages to be able to send them." = "Para poder enviar mensajes de voz antes debes permitir que tu contacto pueda enviarlos."; @@ -4070,23 +5895,26 @@ /* chat list item description */ "you shared one-time link incognito" = "has compartido enlace de un solo uso en modo incógnito"; +/* token info */ +"You should receive notifications." = "Deberías recibir notificaciones."; + /* snd group event chat item */ "you unblocked %@" = "has desbloqueado a %@"; /* No comment provided by engineer. */ -"You will be connected to group when the group host's device is online, please wait or check later!" = "Te conectarás al grupo cuando el dispositivo del anfitrión esté en línea, por favor espera o compruébalo más tarde."; +"You will be connected to group when the group host's device is online, please wait or check later!" = "Te conectarás al grupo cuando el dispositivo del anfitrión esté en línea, por favor espera o revisa más tarde."; /* No comment provided by engineer. */ -"You will be connected when group link host's device is online, please wait or check later!" = "Te conectarás cuando el dispositivo propietario del grupo esté en línea, por favor espera o compruébalo más tarde."; +"You will be connected when group link host's device is online, please wait or check later!" = "Te conectarás cuando el dispositivo propietario del grupo esté en línea, por favor espera o revisa más tarde."; /* No comment provided by engineer. */ -"You will be connected when your connection request is accepted, please wait or check later!" = "Te conectarás cuando tu solicitud se acepte, por favor espera o compruébalo más tarde."; +"You will be connected when your connection request is accepted, please wait or check later!" = "Te conectarás cuando tu solicitud se acepte, por favor espera o revisa más tarde."; /* No comment provided by engineer. */ -"You will be connected when your contact's device is online, please wait or check later!" = "Te conectarás cuando el dispositivo de tu contacto esté en línea, por favor espera o compruébalo más tarde."; +"You will be connected when your contact's device is online, please wait or check later!" = "Te conectarás cuando el dispositivo del contacto esté en línea, por favor espera o revisa más tarde."; /* No comment provided by engineer. */ -"You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Se te pedirá identificarte cuándo inicies o continues usando la aplicación tras 30 segundos en segundo plano."; +"You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Se te pedirá autenticarte cuando inicies la aplicación o sigas usándola tras 30 segundos en segundo plano."; /* No comment provided by engineer. */ "You will connect to all group members." = "Te conectarás con todos los miembros del grupo."; @@ -4094,6 +5922,9 @@ /* No comment provided by engineer. */ "You will still receive calls and notifications from muted profiles when they are active." = "Seguirás recibiendo llamadas y notificaciones de los perfiles silenciados cuando estén activos."; +/* No comment provided by engineer. */ +"You will stop receiving messages from this chat. Chat history will be preserved." = "Dejarás de recibir mensajes de este chat. El historial del chat se conserva."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Dejarás de recibir mensajes de este grupo. El historial del chat se conservará."; @@ -4109,9 +5940,6 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Estás usando un perfil incógnito en este grupo. Para evitar descubrir tu perfil principal no se permite invitar contactos"; -/* No comment provided by engineer. */ -"Your %@ servers" = "Mis servidores %@"; - /* No comment provided by engineer. */ "Your calls" = "Llamadas"; @@ -4121,11 +5949,14 @@ /* No comment provided by engineer. */ "Your chat database is not encrypted - set passphrase to encrypt it." = "La base de datos no está cifrada - establece una contraseña para cifrarla."; +/* alert title */ +"Your chat preferences" = "Tus preferencias de chat"; + /* No comment provided by engineer. */ "Your chat profiles" = "Mis perfiles"; /* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Tu contacto debe estar en línea para que se complete la conexión.\nPuedes cancelar esta conexión y eliminar el contacto (e intentarlo más tarde con un enlace nuevo)."; +"Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." = "Tu conexión ha sido trasladada a %@ pero ha ocurrido un error inesperado al redirigirte al perfil."; /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "El contacto ha enviado un archivo mayor al máximo admitido (%@)."; @@ -4136,6 +5967,9 @@ /* No comment provided by engineer. */ "Your contacts will remain connected." = "Tus contactos permanecerán conectados."; +/* No comment provided by engineer. */ +"Your credentials may be sent unencrypted." = "Tus credenciales podrían ser enviadas sin cifrar."; + /* No comment provided by engineer. */ "Your current chat database will be DELETED and REPLACED with the imported one." = "La base de datos actual será ELIMINADA y SUSTITUIDA por la importada."; @@ -4155,10 +5989,13 @@ "Your profile" = "Tu perfil"; /* No comment provided by engineer. */ -"Your profile **%@** will be shared." = "Tu perfil **%@** será compartido."; +"Your profile **%@** will be shared." = "El perfil **%@** será compartido."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Tu perfil se almacena en tu dispositivo y sólo se comparte con tus contactos.\nLos servidores de SimpleX no pueden ver tu perfil."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Tu perfil es almacenado en tu dispositivo y solamente se comparte con tus contactos. Los servidores SimpleX no pueden ver tu perfil."; + +/* alert message */ +"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Tu perfil ha sido modificado. Si lo guardas la actualización será enviada a todos tus contactos."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Tu perfil, contactos y mensajes se almacenan en tu dispositivo."; @@ -4167,10 +6004,10 @@ "Your random profile" = "Tu perfil aleatorio"; /* No comment provided by engineer. */ -"Your server" = "Tu servidor"; +"Your server address" = "Dirección del servidor"; /* No comment provided by engineer. */ -"Your server address" = "Dirección de tu servidor"; +"Your servers" = "Tus servidores"; /* No comment provided by engineer. */ "Your settings" = "Configuración"; @@ -4178,9 +6015,3 @@ /* No comment provided by engineer. */ "Your SimpleX address" = "Mi dirección SimpleX"; -/* No comment provided by engineer. */ -"Your SMP servers" = "Servidores SMP"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "Servidores XFTP"; - diff --git a/apps/ios/fi.lproj/Localizable.strings b/apps/ios/fi.lproj/Localizable.strings index ddd085141b..4891c7fb26 100644 --- a/apps/ios/fi.lproj/Localizable.strings +++ b/apps/ios/fi.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (voidaan kopioida)"; @@ -25,24 +10,9 @@ /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- ääniviestit enintään 5 minuuttia.\n- mukautettu katoamisaika.\n- historian muokkaaminen."; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 värillinen!"; -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - -/* No comment provided by engineer. */ -")" = ")"; - /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Osallistu](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -52,9 +22,6 @@ /* No comment provided by engineer. */ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Tähti GitHubissa](https://github.com/simplex-chat/simplex-chat)"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Lisää uusi kontakti**: luo kertakäyttöinen QR-koodi tai linkki kontaktille."; - /* No comment provided by engineer. */ "**e2e encrypted** audio call" = "**e2e-salattu** äänipuhelu"; @@ -62,16 +29,16 @@ "**e2e encrypted** video call" = "**e2e-salattu** videopuhelu"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Yksityisempi**: tarkista uudet viestit 20 minuutin välein. Laitetunnus jaetaan SimpleX Chat -palvelimen kanssa, mutta ei sitä, kuinka monta yhteystietoa tai viestiä sinulla on."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Yksityisempi**: tarkista uudet viestit 20 minuutin välein. Laitetunnus jaetaan SimpleX Chat -palvelimen kanssa, mutta ei sitä, kuinka monta yhteystietoa tai viestiä sinulla on."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Yksityisin**: älä käytä SimpleX Chat -ilmoituspalvelinta, tarkista viestit ajoittain taustalla (riippuu siitä, kuinka usein käytät sovellusta)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**Yksityisin**: älä käytä SimpleX Chat -ilmoituspalvelinta, tarkista viestit ajoittain taustalla (riippuu siitä, kuinka usein käytät sovellusta)."; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Huomaa**: et voi palauttaa tai muuttaa tunnuslausetta, jos kadotat sen."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Suositus**: laitetunnus ja ilmoitukset lähetetään SimpleX Chat -ilmoituspalvelimelle, mutta ei viestin sisältöä, kokoa tai sitä, keneltä se on peräisin."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Suositus**: laitetunnus ja ilmoitukset lähetetään SimpleX Chat -ilmoituspalvelimelle, mutta ei viestin sisältöä, kokoa tai sitä, keneltä se on peräisin."; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Varoitus**: Välittömät push-ilmoitukset vaativat tunnuslauseen, joka on tallennettu Keychainiin."; @@ -121,9 +88,6 @@ /* No comment provided by engineer. */ "%@ is verified" = "%@ on vahvistettu"; -/* No comment provided by engineer. */ -"%@ servers" = "%@ palvelimet"; - /* notification title */ "%@ wants to connect!" = "%@ haluaa muodostaa yhteyden!"; @@ -175,9 +139,6 @@ /* No comment provided by engineer. */ "%lld new interface languages" = "%lld uutta käyttöliittymän kieltä"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld sekunti(a)"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld sekuntia"; @@ -220,7 +181,8 @@ /* No comment provided by engineer. */ "0s" = "0s"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "1 päivä"; /* time interval */ @@ -229,10 +191,12 @@ /* No comment provided by engineer. */ "1 minute" = "1 minuutti"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "1 kuukausi"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "1 viikko"; /* No comment provided by engineer. */ @@ -268,23 +232,15 @@ /* No comment provided by engineer. */ "Abort changing address?" = "Keskeytä osoitteenvaihto?"; -/* No comment provided by engineer. */ -"About SimpleX" = "Tietoja SimpleX:stä"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "Tietoja SimpleX osoitteesta"; - /* No comment provided by engineer. */ "About SimpleX Chat" = "Tietoja SimpleX Chatistä"; /* No comment provided by engineer. */ "above, then choose:" = "edellä, valitse sitten:"; -/* No comment provided by engineer. */ -"Accent color" = "Korostusväri"; - /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "Hyväksy"; /* No comment provided by engineer. */ @@ -293,7 +249,8 @@ /* notification body */ "Accept contact request from %@?" = "Hyväksy kontaktipyyntö %@:ltä?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "Hyväksy tuntematon"; /* call status */ @@ -302,14 +259,11 @@ /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Lisää osoite profiiliisi, jotta kontaktisi voivat jakaa sen muiden kanssa. Profiilipäivitys lähetetään kontakteillesi."; -/* No comment provided by engineer. */ -"Add preset servers" = "Lisää esiasetettuja palvelimia"; - /* No comment provided by engineer. */ "Add profile" = "Lisää profiili"; /* No comment provided by engineer. */ -"Add server…" = "Lisää palvelin…"; +"Add server" = "Lisää palvelin"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "Lisää palvelimia skannaamalla QR-koodeja."; @@ -431,6 +385,9 @@ /* No comment provided by engineer. */ "Answer call" = "Vastaa puheluun"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "Avoimen lähdekoodin protokolla ja koodi - kuka tahansa voi käyttää palvelimia."; + /* No comment provided by engineer. */ "App build: %@" = "Sovellusversio: %@"; @@ -551,7 +508,8 @@ /* No comment provided by engineer. */ "Can't invite contacts!" = "Kontakteja ei voi kutsua!"; -/* No comment provided by engineer. */ +/* alert action +alert button */ "Cancel" = "Peruuta"; /* feature offered item */ @@ -560,7 +518,7 @@ /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "Ei pääsyä avainnippuun tietokannan salasanan tallentamiseksi"; -/* No comment provided by engineer. */ +/* alert title */ "Cannot receive file" = "Tiedostoa ei voi vastaanottaa"; /* No comment provided by engineer. */ @@ -591,7 +549,7 @@ "Change self-destruct mode" = "Vaihda itsetuhotilaa"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "Vaihda itsetuhoutuva pääsykoodi"; /* chat item text */ @@ -609,9 +567,6 @@ /* chat item text */ "changing address…" = "muuttamassa osoitetta…"; -/* No comment provided by engineer. */ -"Chat archive" = "Chat-arkisto"; - /* No comment provided by engineer. */ "Chat console" = "Chat-konsoli"; @@ -634,9 +589,12 @@ "Chat preferences" = "Chat-asetukset"; /* No comment provided by engineer. */ -"Chats" = "Keskustelut"; +"Chat profile" = "Käyttäjäprofiili"; /* No comment provided by engineer. */ +"Chats" = "Keskustelut"; + +/* alert title */ "Check server address and try again." = "Tarkista palvelimen osoite ja yritä uudelleen."; /* No comment provided by engineer. */ @@ -648,7 +606,7 @@ /* No comment provided by engineer. */ "Choose from library" = "Valitse kirjastosta"; -/* No comment provided by engineer. */ +/* swipe action */ "Clear" = "Tyhjennä"; /* No comment provided by engineer. */ @@ -663,9 +621,6 @@ /* No comment provided by engineer. */ "colored" = "värillinen"; -/* No comment provided by engineer. */ -"Colors" = "Värit"; - /* server test step */ "Compare file" = "Vertaa tiedostoa"; @@ -735,7 +690,7 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "Yhteyden muodostaminen palvelimeen... (virhe: %@)"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "yhdistää…"; /* No comment provided by engineer. */ @@ -777,9 +732,6 @@ /* notification */ "Contact is connected" = "Kontakti on yhdistetty"; -/* No comment provided by engineer. */ -"Contact is not connected yet!" = "Kontaktia ei ole vielä yhdistetty!"; - /* No comment provided by engineer. */ "Contact name" = "Kontaktin nimi"; @@ -795,7 +747,7 @@ /* No comment provided by engineer. */ "Continue" = "Jatka"; -/* chat item action */ +/* No comment provided by engineer. */ "Copy" = "Kopioi"; /* No comment provided by engineer. */ @@ -804,9 +756,6 @@ /* No comment provided by engineer. */ "Create" = "Luo"; -/* No comment provided by engineer. */ -"Create an address to let people connect with you." = "Luo osoite, jolla ihmiset voivat ottaa sinuun yhteyttä."; - /* server test step */ "Create file" = "Luo tiedosto"; @@ -819,6 +768,9 @@ /* No comment provided by engineer. */ "Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" = "Luo uusi profiili [työpöytäsovelluksessa](https://simplex.chat/downloads/). 💻"; +/* No comment provided by engineer. */ +"Create profile" = "Luo profiilisi"; + /* server test step */ "Create queue" = "Luo jono"; @@ -831,9 +783,6 @@ /* No comment provided by engineer. */ "Create your profile" = "Luo profiilisi"; -/* No comment provided by engineer. */ -"Created on %@" = "Luotu %@"; - /* No comment provided by engineer. */ "creator" = "luoja"; @@ -921,7 +870,8 @@ /* message decrypt error item */ "Decryption error" = "Salauksen purkuvirhe"; -/* pref value */ +/* delete after time +pref value */ "default (%@)" = "oletusarvo (%@)"; /* No comment provided by engineer. */ @@ -930,7 +880,8 @@ /* No comment provided by engineer. */ "default (yes)" = "oletusarvo (kyllä)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "Poista"; /* No comment provided by engineer. */ @@ -945,12 +896,6 @@ /* No comment provided by engineer. */ "Delete all files" = "Poista kaikki tiedostot"; -/* No comment provided by engineer. */ -"Delete archive" = "Poista arkisto"; - -/* No comment provided by engineer. */ -"Delete chat archive?" = "Poista keskusteluarkisto?"; - /* No comment provided by engineer. */ "Delete chat profile" = "Poista keskusteluprofiili"; @@ -963,9 +908,6 @@ /* No comment provided by engineer. */ "Delete contact" = "Poista kontakti"; -/* No comment provided by engineer. */ -"Delete Contact" = "Poista kontakti"; - /* No comment provided by engineer. */ "Delete database" = "Poista tietokanta"; @@ -1005,7 +947,7 @@ /* No comment provided by engineer. */ "Delete message?" = "Poista viesti?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "Poista viestit"; /* No comment provided by engineer. */ @@ -1017,9 +959,6 @@ /* No comment provided by engineer. */ "Delete old database?" = "Poista vanha tietokanta?"; -/* No comment provided by engineer. */ -"Delete pending connection" = "Poista vireillä oleva yhteys"; - /* No comment provided by engineer. */ "Delete pending connection?" = "Poistetaanko odottava yhteys?"; @@ -1084,7 +1023,7 @@ "Direct messages" = "Yksityisviestit"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "Yksityisviestit jäsenten välillä ovat kiellettyjä tässä ryhmässä."; +"Direct messages between members are prohibited." = "Yksityisviestit jäsenten välillä ovat kiellettyjä tässä ryhmässä."; /* No comment provided by engineer. */ "Disable (keep overrides)" = "Poista käytöstä (pidä ohitukset)"; @@ -1108,7 +1047,7 @@ "Disappearing messages are prohibited in this chat." = "Katoavat viestit ovat kiellettyjä tässä keskustelussa."; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "Katoavat viestit ovat kiellettyjä tässä ryhmässä."; +"Disappearing messages are prohibited." = "Katoavat viestit ovat kiellettyjä tässä ryhmässä."; /* No comment provided by engineer. */ "Disappears at" = "Katoaa klo"; @@ -1167,7 +1106,7 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "Salli (pidä ohitukset)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "Ota automaattinen viestien poisto käyttöön?"; /* No comment provided by engineer. */ @@ -1299,9 +1238,6 @@ /* No comment provided by engineer. */ "Error accepting contact request" = "Virhe kontaktipyynnön hyväksymisessä"; -/* No comment provided by engineer. */ -"Error accessing database file" = "Virhe tietokantatiedoston käyttämisessä"; - /* No comment provided by engineer. */ "Error adding member(s)" = "Virhe lisättäessä jäseniä"; @@ -1338,9 +1274,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "Virhe yhteyden poistamisessa"; -/* No comment provided by engineer. */ -"Error deleting contact" = "Virhe kontaktin poistamisessa"; - /* No comment provided by engineer. */ "Error deleting database" = "Virhe tietokannan poistamisessa"; @@ -1371,18 +1304,12 @@ /* No comment provided by engineer. */ "Error joining group" = "Virhe ryhmään liittymisessä"; -/* No comment provided by engineer. */ -"Error loading %@ servers" = "Virhe %@-palvelimien lataamisessa"; - -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Virhe tiedoston vastaanottamisessa"; /* No comment provided by engineer. */ "Error removing member" = "Virhe poistettaessa jäsentä"; -/* No comment provided by engineer. */ -"Error saving %@ servers" = "Virhe %@ palvelimien tallentamisessa"; - /* No comment provided by engineer. */ "Error saving group profile" = "Virhe ryhmäprofiilin tallentamisessa"; @@ -1413,7 +1340,7 @@ /* No comment provided by engineer. */ "Error stopping chat" = "Virhe keskustelun lopettamisessa"; -/* No comment provided by engineer. */ +/* alertTitle */ "Error switching profile!" = "Virhe profiilin vaihdossa!"; /* No comment provided by engineer. */ @@ -1434,7 +1361,9 @@ /* No comment provided by engineer. */ "Error: " = "Virhe: "; -/* No comment provided by engineer. */ +/* alert message +file error text +snd error text */ "Error: %@" = "Virhe: %@"; /* No comment provided by engineer. */ @@ -1446,9 +1375,6 @@ /* No comment provided by engineer. */ "Even when disabled in the conversation." = "Jopa kun ei käytössä keskustelussa."; -/* No comment provided by engineer. */ -"event happened" = "tapahtuma tapahtui"; - /* No comment provided by engineer. */ "Exit without saving" = "Poistu tallentamatta"; @@ -1470,7 +1396,7 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "Nopea ja ei odotusta, kunnes lähettäjä on online-tilassa!"; -/* No comment provided by engineer. */ +/* swipe action */ "Favorite" = "Suosikki"; /* No comment provided by engineer. */ @@ -1492,7 +1418,7 @@ "Files and media" = "Tiedostot ja media"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "Tiedostot ja media ovat tässä ryhmässä kiellettyjä."; +"Files and media are prohibited." = "Tiedostot ja media ovat tässä ryhmässä kiellettyjä."; /* No comment provided by engineer. */ "Files and media prohibited!" = "Tiedostot ja media kielletty!"; @@ -1536,9 +1462,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Koko nimi (valinnainen)"; -/* No comment provided by engineer. */ -"Full name:" = "Koko nimi:"; - /* No comment provided by engineer. */ "Fully re-implemented - work in background!" = "Täysin uudistettu - toimii taustalla!"; @@ -1578,24 +1501,6 @@ /* No comment provided by engineer. */ "Group links" = "Ryhmälinkit"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "Ryhmän jäsenet voivat lisätä viestireaktioita."; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "Ryhmän jäsenet voivat poistaa lähetetyt viestit peruuttamattomasti. (24 tuntia)"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "Ryhmän jäsenet voivat lähettää suoraviestejä."; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "Ryhmän jäsenet voivat lähettää katoavia viestejä."; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "Ryhmän jäsenet voivat lähettää tiedostoja ja mediaa."; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "Ryhmän jäsenet voivat lähettää ääniviestejä."; - /* notification */ "Group message:" = "Ryhmäviesti:"; @@ -1653,9 +1558,6 @@ /* time unit */ "hours" = "tuntia"; -/* No comment provided by engineer. */ -"How it works" = "Kuinka se toimii"; - /* No comment provided by engineer. */ "How SimpleX works" = "Miten SimpleX toimii"; @@ -1696,7 +1598,7 @@ "Immediately" = "Heti"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "Immuuni roskapostille ja väärinkäytöksille"; +"Immune to spam" = "Immuuni roskapostille ja väärinkäytöksille"; /* No comment provided by engineer. */ "Import" = "Tuo"; @@ -1765,10 +1667,10 @@ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Asenna [SimpleX Chat terminaalille](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"Instant push notifications will be hidden!\n" = "Välittömät push-ilmoitukset ovat piilossa!\n"; +"Instant" = "Heti"; /* No comment provided by engineer. */ -"Instantly" = "Heti"; +"Instant push notifications will be hidden!\n" = "Välittömät push-ilmoitukset ovat piilossa!\n"; /* No comment provided by engineer. */ "Interface" = "Käyttöliittymä"; @@ -1785,7 +1687,7 @@ /* invalid chat item */ "invalid data" = "virheelliset tiedot"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "Virheellinen palvelinosoite!"; /* item status text */ @@ -1831,7 +1733,7 @@ "Irreversible message deletion is prohibited in this chat." = "Viestien peruuttamaton poisto on kielletty tässä keskustelussa."; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "Viestien peruuttamaton poisto on kielletty tässä ryhmässä."; +"Irreversible message deletion is prohibited." = "Viestien peruuttamaton poisto on kielletty tässä ryhmässä."; /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "Se mahdollistaa useiden nimettömien yhteyksien muodostamisen yhdessä keskusteluprofiilissa ilman, että niiden välillä on jaettuja tietoja."; @@ -1854,7 +1756,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "Japanilainen käyttöliittymä"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "Liity"; /* No comment provided by engineer. */ @@ -1884,7 +1786,7 @@ /* No comment provided by engineer. */ "Learn more" = "Lue lisää"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "Poistu"; /* No comment provided by engineer. */ @@ -1914,9 +1816,6 @@ /* No comment provided by engineer. */ "Live messages" = "Live-viestit"; -/* No comment provided by engineer. */ -"Local" = "Paikallinen"; - /* No comment provided by engineer. */ "Local name" = "Paikallinen nimi"; @@ -1929,24 +1828,15 @@ /* No comment provided by engineer. */ "Lock mode" = "Lukitustila"; -/* No comment provided by engineer. */ -"Make a private connection" = "Luo yksityinen yhteys"; - /* No comment provided by engineer. */ "Make one message disappear" = "Hävitä yksi viesti"; /* No comment provided by engineer. */ "Make profile private!" = "Tee profiilista yksityinen!"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Varmista, että %@-palvelinosoitteet ovat oikeassa muodossa, että ne on erotettu toisistaan riveittäin ja että ne eivät ole päällekkäisiä (%@)."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Varmista, että WebRTC ICE -palvelinosoitteet ovat oikeassa muodossa, rivieroteltuina ja että ne eivät ole päällekkäisiä."; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Monet ihmiset kysyivät: *Jos SimpleX:llä ei ole käyttäjätunnuksia, miten se voi toimittaa viestejä?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "Merkitse poistetuksi kaikilta"; @@ -1983,6 +1873,24 @@ /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "Jäsen poistetaan ryhmästä - tätä ei voi perua!"; +/* No comment provided by engineer. */ +"Members can add message reactions." = "Ryhmän jäsenet voivat lisätä viestireaktioita."; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "Ryhmän jäsenet voivat poistaa lähetetyt viestit peruuttamattomasti. (24 tuntia)"; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "Ryhmän jäsenet voivat lähettää suoraviestejä."; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "Ryhmän jäsenet voivat lähettää katoavia viestejä."; + +/* No comment provided by engineer. */ +"Members can send files and media." = "Ryhmän jäsenet voivat lähettää tiedostoja ja mediaa."; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "Ryhmän jäsenet voivat lähettää ääniviestejä."; + /* item status text */ "Message delivery error" = "Viestin toimitusvirhe"; @@ -1999,7 +1907,7 @@ "Message reactions are prohibited in this chat." = "Viestireaktiot ovat kiellettyjä tässä keskustelussa."; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "Viestireaktiot ovat kiellettyjä tässä ryhmässä."; +"Message reactions are prohibited." = "Viestireaktiot ovat kiellettyjä tässä ryhmässä."; /* notification */ "message received" = "viesti vastaanotettu"; @@ -2026,7 +1934,7 @@ "Migration is completed" = "Siirto on valmis"; /* No comment provided by engineer. */ -"Migrations: %@" = "Siirrot: %@"; +"Migrations:" = "Siirrot:"; /* time unit */ "minutes" = "minuuttia"; @@ -2058,19 +1966,16 @@ /* item status description */ "Most likely this connection is deleted." = "Todennäköisesti tämä yhteys on poistettu."; -/* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "Todennäköisesti tämä kontakti on poistanut yhteyden sinuun."; - /* No comment provided by engineer. */ "Multiple chat profiles" = "Useita keskusteluprofiileja"; -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "Mykistä"; /* No comment provided by engineer. */ "Muted when inactive!" = "Mykistetty ei-aktiivisena!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "Nimi"; /* No comment provided by engineer. */ @@ -2082,7 +1987,7 @@ /* No comment provided by engineer. */ "Network status" = "Verkon tila"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "ei koskaan"; /* notification */ @@ -2091,9 +1996,6 @@ /* notification */ "New contact:" = "Uusi kontakti:"; -/* No comment provided by engineer. */ -"New database archive" = "Uusi tietokanta-arkisto"; - /* No comment provided by engineer. */ "New display name" = "Uusi näyttönimi"; @@ -2151,12 +2053,18 @@ /* No comment provided by engineer. */ "No permission to record voice message" = "Ei lupaa ääniviestin tallentamiseen"; +/* No comment provided by engineer. */ +"No push server" = "Paikallinen"; + /* No comment provided by engineer. */ "No received or sent files" = "Ei vastaanotettuja tai lähetettyjä tiedostoja"; /* copied message info in history */ "no text" = "ei tekstiä"; +/* No comment provided by engineer. */ +"No user identifiers." = "Ensimmäinen alusta ilman käyttäjätunnisteita – suunniteltu yksityiseksi."; + /* No comment provided by engineer. */ "Notifications" = "Ilmoitukset"; @@ -2170,11 +2078,11 @@ "observer" = "tarkkailija"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "pois"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Pois"; /* feature offered item */ @@ -2183,15 +2091,12 @@ /* feature offered item */ "offered %@: %@" = "tarjottu %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ "Old database" = "Vanha tietokanta"; -/* No comment provided by engineer. */ -"Old database archive" = "Vanha tietokanta-arkisto"; - /* group pref value */ "on" = "päällä"; @@ -2199,16 +2104,16 @@ "One-time invitation link" = "Kertakutsulinkki"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "Yhteyden muodostamiseen tarvitaan Onion-isäntiä. Edellyttää VPN:n sallimista."; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "Yhteyden muodostamiseen tarvitaan Onion-isäntiä.\nEdellyttää VPN:n sallimista."; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "Onion-isäntiä käytetään, kun niitä on saatavilla. Edellyttää VPN:n sallimista."; +"Onion hosts will be used when available.\nRequires compatible VPN." = "Onion-isäntiä käytetään, kun niitä on saatavilla.\nEdellyttää VPN:n sallimista."; /* No comment provided by engineer. */ "Onion hosts will not be used." = "Onion-isäntiä ei käytetä."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Vain asiakaslaitteet tallentavat käyttäjäprofiileja, yhteystietoja, ryhmiä ja viestejä, jotka on lähetetty **kaksinkertaisella päästä päähän -salauksella**."; +"Only client devices store user profiles, contacts, groups, and messages." = "Vain asiakaslaitteet tallentavat käyttäjäprofiileja, yhteystietoja, ryhmiä ja viestejä, jotka on lähetetty **kaksinkertaisella päästä päähän -salauksella**."; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Vain ryhmän omistajat voivat muuttaa ryhmän asetuksia."; @@ -2258,12 +2163,6 @@ /* No comment provided by engineer. */ "Open Settings" = "Avaa Asetukset"; -/* authentication reason */ -"Open user profiles" = "Avaa käyttäjäprofiilit"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "Avoimen lähdekoodin protokolla ja koodi - kuka tahansa voi käyttää palvelimia."; - /* member role */ "owner" = "omistaja"; @@ -2292,10 +2191,7 @@ "peer-to-peer" = "vertais"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "Ihmiset voivat ottaa sinuun yhteyttä vain jakamiesi linkkien kautta."; - -/* No comment provided by engineer. */ -"Periodically" = "Ajoittain"; +"Periodic" = "Ajoittain"; /* message decrypt error item */ "Permanent decryption error" = "Pysyvä salauksen purkuvirhe"; @@ -2351,9 +2247,6 @@ /* No comment provided by engineer. */ "Preserve the last message draft, with attachments." = "Säilytä viimeinen viestiluonnos liitteineen."; -/* No comment provided by engineer. */ -"Preset server" = "Esiasetettu palvelin"; - /* No comment provided by engineer. */ "Preset server address" = "Esiasetettu palvelimen osoite"; @@ -2378,7 +2271,7 @@ /* No comment provided by engineer. */ "Profile password" = "Profiilin salasana"; -/* No comment provided by engineer. */ +/* alert message */ "Profile update will be sent to your contacts." = "Profiilipäivitys lähetetään kontakteillesi."; /* No comment provided by engineer. */ @@ -2426,14 +2319,14 @@ /* chat item menu */ "React…" = "Reagoi…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "Lue"; /* No comment provided by engineer. */ "Read more" = "Lue lisää"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Lue lisää [Käyttöoppaasta](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "Lue lisää [Käyttöoppaasta](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)."; /* No comment provided by engineer. */ "Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Lue lisää [Käyttöoppaasta](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; @@ -2441,9 +2334,6 @@ /* No comment provided by engineer. */ "Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Lue lisää [GitHub-arkistosta](https://github.com/simplex-chat/simplex-chat#readme)."; -/* No comment provided by engineer. */ -"Read more in our GitHub repository." = "Lue lisää GitHub-tietovarastostamme."; - /* No comment provided by engineer. */ "Receipts are disabled" = "Kuittaukset pois käytöstä"; @@ -2492,7 +2382,8 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "Pienempi akun käyttö"; -/* reject incoming call via notification */ +/* reject incoming call via notification +swipe action */ "Reject" = "Hylkää"; /* No comment provided by engineer. */ @@ -2576,9 +2467,6 @@ /* chat item action */ "Reveal" = "Paljasta"; -/* No comment provided by engineer. */ -"Revert" = "Palauta"; - /* No comment provided by engineer. */ "Revoke" = "Peruuta"; @@ -2594,13 +2482,14 @@ /* No comment provided by engineer. */ "Run chat" = "Käynnistä chat"; -/* chat item action */ +/* alert button +chat item action */ "Save" = "Tallenna"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Tallenna (ja ilmoita kontakteille)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "Tallenna ja ilmoita kontaktille"; /* No comment provided by engineer. */ @@ -2609,12 +2498,6 @@ /* No comment provided by engineer. */ "Save and update group profile" = "Tallenna ja päivitä ryhmäprofiili"; -/* No comment provided by engineer. */ -"Save archive" = "Tallenna arkisto"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Tallenna automaattisen hyväksynnän asetukset"; - /* No comment provided by engineer. */ "Save group profile" = "Tallenna ryhmäprofiili"; @@ -2624,7 +2507,7 @@ /* No comment provided by engineer. */ "Save passphrase in Keychain" = "Tallenna tunnuslause Avainnippuun"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "Tallenna asetukset?"; /* No comment provided by engineer. */ @@ -2633,12 +2516,9 @@ /* No comment provided by engineer. */ "Save servers" = "Tallenna palvelimet"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "Tallenna palvelimet?"; -/* No comment provided by engineer. */ -"Save settings?" = "Tallenna asetukset?"; - /* No comment provided by engineer. */ "Save welcome message?" = "Tallenna tervetuloviesti?"; @@ -2681,7 +2561,7 @@ /* chat item text */ "security code changed" = "turvakoodi on muuttunut"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "Valitse"; /* No comment provided by engineer. */ @@ -2705,9 +2585,6 @@ /* No comment provided by engineer. */ "Send delivery receipts to" = "Lähetä toimituskuittaukset vastaanottajalle"; -/* No comment provided by engineer. */ -"Send direct message" = "Lähetä yksityisviesti"; - /* No comment provided by engineer. */ "Send disappearing message" = "Lähetä katoava viesti"; @@ -2720,9 +2597,6 @@ /* No comment provided by engineer. */ "Send notifications" = "Lähetys ilmoitukset"; -/* No comment provided by engineer. */ -"Send notifications:" = "Lähetys ilmoitukset:"; - /* No comment provided by engineer. */ "Send questions and ideas" = "Lähetä kysymyksiä ja ideoita"; @@ -2732,7 +2606,7 @@ /* No comment provided by engineer. */ "Send them from gallery or custom keyboards." = "Lähetä ne galleriasta tai mukautetuista näppäimistöistä."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Lähettäjä peruutti tiedoston siirron."; /* No comment provided by engineer. */ @@ -2816,7 +2690,8 @@ /* No comment provided by engineer. */ "Settings" = "Asetukset"; -/* chat item action */ +/* alert action +chat item action */ "Share" = "Jaa"; /* No comment provided by engineer. */ @@ -2825,7 +2700,7 @@ /* No comment provided by engineer. */ "Share address" = "Jaa osoite"; -/* No comment provided by engineer. */ +/* alert title */ "Share address with contacts?" = "Jaa osoite kontakteille?"; /* No comment provided by engineer. */ @@ -2867,7 +2742,7 @@ /* simplex link type */ "SimpleX group link" = "SimpleX-ryhmän linkki"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "SimpleX-linkit"; /* No comment provided by engineer. */ @@ -2894,9 +2769,6 @@ /* No comment provided by engineer. */ "Small groups (max 20)" = "Pienryhmät (max 20)"; -/* No comment provided by engineer. */ -"SMP servers" = "SMP-palvelimet"; - /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "Tuonnin aikana tapahtui joitakin ei-vakavia virheitä – saatat nähdä Chat-konsolissa lisätietoja."; @@ -2915,9 +2787,6 @@ /* No comment provided by engineer. */ "Stop" = "Lopeta"; -/* No comment provided by engineer. */ -"Stop chat to enable database actions" = "Pysäytä keskustelu tietokantatoimien mahdollistamiseksi"; - /* No comment provided by engineer. */ "Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Pysäytä keskustelut viedäksesi, tuodaksesi tai poistaaksesi keskustelujen tietokannan. Et voi vastaanottaa ja lähettää viestejä, kun keskustelut on pysäytetty."; @@ -2933,10 +2802,10 @@ /* No comment provided by engineer. */ "Stop sending file?" = "Lopeta tiedoston lähettäminen?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "Lopeta jakaminen"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "Lopeta osoitteen jakaminen?"; /* authentication reason */ @@ -2972,9 +2841,6 @@ /* No comment provided by engineer. */ "Tap to join incognito" = "Napauta liittyäksesi incognito-tilassa"; -/* No comment provided by engineer. */ -"Tap to start a new chat" = "Aloita uusi keskustelu napauttamalla"; - /* No comment provided by engineer. */ "TCP connection timeout" = "TCP-yhteyden aikakatkaisu"; @@ -2996,7 +2862,7 @@ /* No comment provided by engineer. */ "Test servers" = "Testipalvelimet"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "Testit epäonnistuivat!"; /* No comment provided by engineer. */ @@ -3008,9 +2874,6 @@ /* No comment provided by engineer. */ "Thanks to the users – contribute via Weblate!" = "Kiitokset käyttäjille – osallistu Weblaten kautta!"; -/* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "Ensimmäinen alusta ilman käyttäjätunnisteita – suunniteltu yksityiseksi."; - /* No comment provided by engineer. */ "The app can notify you when you receive messages or contact requests - please open settings to enable." = "Sovellus voi ilmoittaa sinulle, kun saat viestejä tai yhteydenottopyyntöjä - avaa asetukset ottaaksesi ne käyttöön."; @@ -3029,6 +2892,9 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "Salaus toimii ja uutta salaussopimusta ei tarvita. Tämä voi johtaa yhteysvirheisiin!"; +/* No comment provided by engineer. */ +"The future of messaging" = "Seuraavan sukupolven yksityisviestit"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "Edellisen viestin tarkiste on erilainen."; @@ -3041,14 +2907,11 @@ /* No comment provided by engineer. */ "The message will be marked as moderated for all members." = "Viesti merkitään moderoiduksi kaikille jäsenille."; -/* No comment provided by engineer. */ -"The next generation of private messaging" = "Seuraavan sukupolven yksityisviestit"; - /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "Vanhaa tietokantaa ei poistettu siirron aikana, se voidaan kuitenkin poistaa."; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "Profiili jaetaan vain kontaktiesi kanssa."; +"Your profile is stored on your device and only shared with your contacts." = "Profiili jaetaan vain kontaktiesi kanssa."; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "Toinen kuittaus, joka uupui! ✅"; @@ -3059,9 +2922,6 @@ /* No comment provided by engineer. */ "The servers for new connections of your current chat profile **%@**." = "Palvelimet nykyisen keskusteluprofiilisi uusille yhteyksille **%@**."; -/* No comment provided by engineer. */ -"Theme" = "Teema"; - /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Nämä asetukset koskevat nykyistä profiiliasi **%@**."; @@ -3098,15 +2958,15 @@ /* No comment provided by engineer. */ "To make a new connection" = "Uuden yhteyden luominen"; -/* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Yksityisyyden suojaamiseksi kaikkien muiden alustojen käyttämien käyttäjätunnusten sijaan SimpleX käyttää viestijonojen tunnisteita, jotka ovat kaikille kontakteille erillisiä."; - /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "Aikavyöhykkeen suojaamiseksi kuva-/äänitiedostot käyttävät UTC:tä."; /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Suojaa tietosi ottamalla SimpleX Lock käyttöön.\nSinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus otetaan käyttöön."; +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "Yksityisyyden suojaamiseksi kaikkien muiden alustojen käyttämien käyttäjätunnusten sijaan SimpleX käyttää viestijonojen tunnisteita, jotka ovat kaikille kontakteille erillisiä."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Jos haluat nauhoittaa ääniviestin, anna lupa käyttää mikrofonia."; @@ -3137,13 +2997,10 @@ /* No comment provided by engineer. */ "Unable to record voice message" = "Ääniviestiä ei voi tallentaa"; -/* item status description */ -"Unexpected error: %@" = "Odottamaton virhe: %@"; - /* No comment provided by engineer. */ "Unexpected migration state" = "Odottamaton siirtotila"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "Epäsuotuisa."; /* No comment provided by engineer. */ @@ -3182,36 +3039,27 @@ /* authentication reason */ "Unlock app" = "Avaa sovellus"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "Poista mykistys"; -/* No comment provided by engineer. */ +/* swipe action */ "Unread" = "Lukematon"; /* No comment provided by engineer. */ "Update" = "Päivitä"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = "Päivitä .onion-isäntien asetus?"; - /* No comment provided by engineer. */ "Update database passphrase" = "Päivitä tietokannan tunnuslause"; /* No comment provided by engineer. */ "Update network settings?" = "Päivitä verkkoasetukset?"; -/* No comment provided by engineer. */ -"Update transport isolation mode?" = "Päivitä kuljetuksen eristystila?"; - /* rcv group event chat item */ "updated group profile" = "päivitetty ryhmäprofiili"; /* No comment provided by engineer. */ "Updating settings will re-connect the client to all servers." = "Asetusten päivittäminen yhdistää asiakkaan uudelleen kaikkiin palvelimiin."; -/* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "Tämän asetuksen päivittäminen yhdistää asiakkaan uudelleen kaikkiin palvelimiin."; - /* No comment provided by engineer. */ "Upgrade and open chat" = "Päivitä ja avaa keskustelu"; @@ -3242,12 +3090,6 @@ /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Käytä SimpleX Chat palvelimia?"; -/* No comment provided by engineer. */ -"User profile" = "Käyttäjäprofiili"; - -/* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = ".onion-isäntien käyttäminen vaatii yhteensopivan VPN-palveluntarjoajan."; - /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Käyttää SimpleX Chat -palvelimia."; @@ -3303,7 +3145,7 @@ "Voice messages are prohibited in this chat." = "Ääniviestit ovat kiellettyjä tässä keskustelussa."; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "Ääniviestit ovat kiellettyjä tässä ryhmässä."; +"Voice messages are prohibited." = "Ääniviestit ovat kiellettyjä tässä ryhmässä."; /* No comment provided by engineer. */ "Voice messages prohibited!" = "Ääniviestit kielletty!"; @@ -3347,9 +3189,6 @@ /* No comment provided by engineer. */ "When available" = "Kun saatavilla"; -/* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "Kun ihmiset pyytävät yhteyden muodostamista, voit hyväksyä tai hylätä sen."; - /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Kun jaat inkognitoprofiilin jonkun kanssa, tätä profiilia käytetään ryhmissä, joihin tämä sinut kutsuu."; @@ -3362,15 +3201,9 @@ /* No comment provided by engineer. */ "Wrong passphrase!" = "Väärä tunnuslause!"; -/* No comment provided by engineer. */ -"XFTP servers" = "XFTP-palvelimet"; - /* pref value */ "yes" = "kyllä"; -/* No comment provided by engineer. */ -"You" = "Sinä"; - /* No comment provided by engineer. */ "You accepted connection" = "Hyväksyit yhteyden"; @@ -3411,7 +3244,7 @@ "You can hide or mute a user profile - swipe it to the right." = "Voit piilottaa tai mykistää käyttäjäprofiilin pyyhkäisemällä sitä oikealle."; /* notification body */ -"You can now send messages to %@" = "Voit nyt lähettää viestejä %@:lle"; +"You can now chat with %@" = "Voit nyt lähettää viestejä %@:lle"; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "Voit määrittää lukitusnäytön ilmoituksen esikatselun asetuksista."; @@ -3422,9 +3255,6 @@ /* No comment provided by engineer. */ "You can share this address with your contacts to let them connect with **%@**." = "Voit jakaa tämän osoitteen kontaktiesi kanssa, jotta ne voivat muodostaa yhteyden **%@** kanssa."; -/* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "Voit jakaa osoitteesi linkkinä tai QR-koodina - kuka tahansa voi muodostaa yhteyden sinuun."; - /* No comment provided by engineer. */ "You can start chat via app Settings / Database or by restarting the app" = "Voit aloittaa keskustelun sovelluksen Asetukset / Tietokanta kautta tai käynnistämällä sovelluksen uudelleen"; @@ -3449,14 +3279,11 @@ /* snd group event chat item */ "you changed role of %@ to %@" = "olet vaihtanut %1$@:n roolin %2$@:ksi"; -/* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "Sinä hallitset, minkä palvelim(i)en kautta **viestit vastaanotetaan**, kontaktisi - palvelimet, joita käytät viestien lähettämiseen niille."; - /* No comment provided by engineer. */ "You could not be verified; please try again." = "Sinua ei voitu todentaa; yritä uudelleen."; /* No comment provided by engineer. */ -"You have no chats" = "Sinulla ei ole keskusteluja"; +"You decide who can connect." = "Kimin bağlanabileceğine siz karar verirsiniz."; /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "Sinun on annettava tunnuslause aina, kun sovellus käynnistyy - sitä ei tallenneta laitteeseen."; @@ -3524,9 +3351,6 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Käytät tässä ryhmässä incognito-profiilia. Kontaktien kutsuminen ei ole sallittua, jotta pääprofiilisi ei tule jaetuksi"; -/* No comment provided by engineer. */ -"Your %@ servers" = "%@-palvelimesi"; - /* No comment provided by engineer. */ "Your calls" = "Puhelusi"; @@ -3539,9 +3363,6 @@ /* No comment provided by engineer. */ "Your chat profiles" = "Keskusteluprofiilisi"; -/* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Kontaktin tulee olla online-tilassa, jotta yhteys voidaan muodostaa.\nVoit peruuttaa tämän yhteyden ja poistaa kontaktin (ja yrittää myöhemmin uudella linkillä)."; - /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "Yhteyshenkilösi lähetti tiedoston, joka on suurempi kuin tällä hetkellä tuettu enimmäiskoko (%@)."; @@ -3570,7 +3391,7 @@ "Your profile **%@** will be shared." = "Profiilisi **%@** jaetaan."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Profiilisi tallennetaan laitteeseesi ja jaetaan vain yhteystietojesi kanssa.\nSimpleX-palvelimet eivät näe profiiliasi."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Profiilisi tallennetaan laitteeseesi ja jaetaan vain yhteystietojesi kanssa. SimpleX-palvelimet eivät näe profiiliasi."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Profiilisi, kontaktisi ja toimitetut viestit tallennetaan laitteellesi."; @@ -3578,9 +3399,6 @@ /* No comment provided by engineer. */ "Your random profile" = "Satunnainen profiilisi"; -/* No comment provided by engineer. */ -"Your server" = "Palvelimesi"; - /* No comment provided by engineer. */ "Your server address" = "Palvelimesi osoite"; @@ -3590,9 +3408,3 @@ /* No comment provided by engineer. */ "Your SimpleX address" = "SimpleX-osoitteesi"; -/* No comment provided by engineer. */ -"Your SMP servers" = "SMP-palvelimesi"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "XFTP-palvelimesi"; - diff --git a/apps/ios/fr.lproj/Localizable.strings b/apps/ios/fr.lproj/Localizable.strings index 4417eb35a1..4dd75039dc 100644 --- a/apps/ios/fr.lproj/Localizable.strings +++ b/apps/ios/fr.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (peut être copié)"; @@ -31,30 +16,15 @@ /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- messages vocaux pouvant durer jusqu'à 5 minutes.\n- délai personnalisé de disparition.\n- l'historique de modification."; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 coloré!"; -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - /* No comment provided by engineer. */ "(new)" = "(nouveau)"; /* No comment provided by engineer. */ "(this device v%@)" = "(cet appareil v%@)"; -/* No comment provided by engineer. */ -")" = ")"; - /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Contribuer](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -65,10 +35,7 @@ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Star sur GitHub](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add contact**: to create a new invitation link, or connect via a link you received." = "**Ajouter un contact** : pour créer un nouveau lien d'invitation ou vous connecter via un lien que vous avez reçu."; - -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Ajouter un nouveau contact** : pour créer un lien ou code QR unique pour votre contact."; +"**Create 1-time link**: to create and share a new invitation link." = "**Ajouter un contact** : pour créer un nouveau lien d'invitation."; /* No comment provided by engineer. */ "**Create group**: to create a new group." = "**Créer un groupe** : pour créer un nouveau groupe."; @@ -80,20 +47,29 @@ "**e2e encrypted** video call" = "appel vidéo **chiffré de bout en bout**"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Vie privée** : vérification de nouveaux messages toute les 20 minutes. Le token de l'appareil est partagé avec le serveur SimpleX, mais pas le nombre de messages ou de contacts."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Vie privée** : vérification de nouveaux messages toute les 20 minutes. Le token de l'appareil est partagé avec le serveur SimpleX, mais pas le nombre de messages ou de contacts."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Confidentiel** : ne pas utiliser le serveur de notifications SimpleX, vérification de nouveaux messages periodiquement en arrière plan (dépend de l'utilisation de l'app)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**Confidentiel** : ne pas utiliser le serveur de notifications SimpleX, vérification de nouveaux messages periodiquement en arrière plan (dépend de l'utilisation de l'app)."; + +/* No comment provided by engineer. */ +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Remarque** : l'utilisation de la même base de données sur deux appareils interrompt le déchiffrement des messages provenant de vos connexions, par mesure de sécurité."; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Veuillez noter** : vous NE pourrez PAS récupérer ou modifier votre phrase secrète si vous la perdez."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Recommandé** : le token de l'appareil et les notifications sont envoyés au serveur de notifications SimpleX, mais pas le contenu du message, sa taille ou son auteur."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Recommandé** : le token de l'appareil et les notifications sont envoyés au serveur de notifications SimpleX, mais pas le contenu du message, sa taille ou son auteur."; + +/* No comment provided by engineer. */ +"**Scan / Paste link**: to connect via a link you received." = "**Scanner / Coller** : pour vous connecter via un lien que vous avez reçu."; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Avertissement** : les notifications push instantanées nécessitent une phrase secrète enregistrée dans la keychain."; +/* No comment provided by engineer. */ +"**Warning**: the archive will be removed." = "**Avertissement** : l'archive sera supprimée."; + /* No comment provided by engineer. */ "*bold*" = "\\*gras*"; @@ -136,6 +112,9 @@ /* No comment provided by engineer. */ "%@ connected" = "%@ connecté(e)"; +/* No comment provided by engineer. */ +"%@ downloaded" = "%@ téléchargé"; + /* notification title */ "%@ is connected!" = "%@ est connecté·e !"; @@ -145,12 +124,21 @@ /* No comment provided by engineer. */ "%@ is verified" = "%@ est vérifié·e"; +/* No comment provided by engineer. */ +"%@ server" = "Serveur %@"; + /* No comment provided by engineer. */ "%@ servers" = "Serveurs %@"; +/* No comment provided by engineer. */ +"%@ uploaded" = "%@ envoyé"; + /* notification title */ "%@ wants to connect!" = "%@ veut se connecter !"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + /* No comment provided by engineer. */ "%@, %@ and %lld members" = "%@, %@ et %lld membres"; @@ -163,9 +151,24 @@ /* time interval */ "%d days" = "%d jours"; +/* forward confirmation reason */ +"%d file(s) are still being downloaded." = "%d fichier(s) en cours de téléchargement."; + +/* forward confirmation reason */ +"%d file(s) failed to download." = "Le téléchargement de %d fichier(s) a échoué."; + +/* forward confirmation reason */ +"%d file(s) were deleted." = "Le(s) fichier(s) %d a(ont) été supprimé(s)."; + +/* forward confirmation reason */ +"%d file(s) were not downloaded." = "Le(s) fichier(s) %d n'a (n'ont) pas été téléchargé(s)."; + /* time interval */ "%d hours" = "%d heures"; +/* alert title */ +"%d messages not forwarded" = "%d messages non transférés"; + /* time interval */ "%d min" = "%d min"; @@ -175,6 +178,9 @@ /* time interval */ "%d sec" = "%d sec"; +/* delete after time */ +"%d seconds(s)" = "%d seconde(s)"; + /* integrity error chat item */ "%d skipped message(s)" = "%d message·s sauté·s"; @@ -217,9 +223,6 @@ /* No comment provided by engineer. */ "%lld new interface languages" = "%lld nouvelles langues d'interface"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld seconde·s"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld secondes"; @@ -265,7 +268,8 @@ /* No comment provided by engineer. */ "0s" = "0s"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "1 jour"; /* time interval */ @@ -274,12 +278,23 @@ /* No comment provided by engineer. */ "1 minute" = "1 minute"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "1 mois"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "1 semaine"; +/* delete after time */ +"1 year" = "1 an"; + +/* No comment provided by engineer. */ +"1-time link" = "Lien unique"; + +/* No comment provided by engineer. */ +"1-time link can be used *with one contact only* - share in person or via any messenger." = "Le lien unique peut être utilisé *avec un seul contact* - partagez le en personne ou via n'importe quelle messagerie."; + /* No comment provided by engineer. */ "5 minutes" = "5 minutes"; @@ -305,7 +320,7 @@ "A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "Une connexion TCP distincte sera utilisée **pour chaque contact et membre de groupe**.\n**Veuillez noter** : si vous avez de nombreuses connexions, votre consommation de batterie et de réseau peut être nettement plus élevée et certaines liaisons peuvent échouer."; /* No comment provided by engineer. */ -"Abort" = "Annuler"; +"Abort" = "Abandonner"; /* No comment provided by engineer. */ "Abort changing address" = "Annuler le changement d'adresse"; @@ -314,10 +329,7 @@ "Abort changing address?" = "Abandonner le changement d'adresse ?"; /* No comment provided by engineer. */ -"About SimpleX" = "À propos de SimpleX"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "À propos de l'adresse SimpleX"; +"About operators" = "À propos des opérateurs"; /* No comment provided by engineer. */ "About SimpleX Chat" = "À propos de SimpleX Chat"; @@ -326,81 +338,158 @@ "above, then choose:" = "ci-dessus, puis choisissez :"; /* No comment provided by engineer. */ -"Accent color" = "Couleur principale"; +"Accent" = "Principale"; /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "Accepter"; +/* No comment provided by engineer. */ +"Accept conditions" = "Accepter les conditions"; + /* No comment provided by engineer. */ "Accept connection request?" = "Accepter la demande de connexion ?"; /* notification body */ "Accept contact request from %@?" = "Accepter la demande de contact de %@ ?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "Accepter en incognito"; /* call status */ "accepted call" = "appel accepté"; +/* No comment provided by engineer. */ +"Accepted conditions" = "Conditions acceptées"; + +/* chat list item title */ +"accepted invitation" = "invitation acceptée"; + +/* No comment provided by engineer. */ +"Acknowledged" = "Reçu avec accusé de réception"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Erreur d'accusé de réception"; + +/* token status text */ +"Active" = "Actif"; + +/* No comment provided by engineer. */ +"Active connections" = "Connections actives"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Ajoutez une adresse à votre profil, afin que vos contacts puissent la partager avec d'autres personnes. La mise à jour du profil sera envoyée à vos contacts."; /* No comment provided by engineer. */ -"Add contact" = "Ajouter le contact"; +"Add friends" = "Ajouter des amis"; /* No comment provided by engineer. */ -"Add preset servers" = "Ajouter des serveurs prédéfinis"; +"Add list" = "Ajouter une liste"; /* No comment provided by engineer. */ "Add profile" = "Ajouter un profil"; /* No comment provided by engineer. */ -"Add server…" = "Ajouter un serveur…"; +"Add server" = "Ajouter un serveur"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "Ajoutez des serveurs en scannant des codes QR."; +/* No comment provided by engineer. */ +"Add team members" = "Ajouter des membres à l'équipe"; + /* No comment provided by engineer. */ "Add to another device" = "Ajouter à un autre appareil"; +/* No comment provided by engineer. */ +"Add to list" = "Ajouter à la liste"; + /* No comment provided by engineer. */ "Add welcome message" = "Ajouter un message d'accueil"; +/* No comment provided by engineer. */ +"Add your team members to the conversations." = "Ajoutez les membres de votre équipe aux conversations."; + +/* No comment provided by engineer. */ +"Added media & file servers" = "Ajout de serveurs de médias et de fichiers"; + +/* No comment provided by engineer. */ +"Added message servers" = "Ajout de serveurs de messages"; + +/* No comment provided by engineer. */ +"Additional accent" = "Accent additionnel"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Accent additionnel 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Accent secondaire"; + /* No comment provided by engineer. */ "Address" = "Adresse"; /* No comment provided by engineer. */ "Address change will be aborted. Old receiving address will be used." = "Le changement d'adresse sera annulé. L'ancienne adresse de réception sera utilisée."; +/* No comment provided by engineer. */ +"Address or 1-time link?" = "Adresse ou lien unique ?"; + +/* No comment provided by engineer. */ +"Address settings" = "Paramètres de l'adresse"; + /* member role */ "admin" = "admin"; +/* feature role */ +"admins" = "admins"; + +/* No comment provided by engineer. */ +"Admins can block a member for all." = "Les admins peuvent bloquer un membre pour tous."; + /* No comment provided by engineer. */ "Admins can create the links to join groups." = "Les admins peuvent créer les liens qui permettent de rejoindre les groupes."; /* No comment provided by engineer. */ "Advanced network settings" = "Paramètres réseau avancés"; +/* No comment provided by engineer. */ +"Advanced settings" = "Paramètres avancés"; + /* chat item text */ "agreeing encryption for %@…" = "négociation du chiffrement avec %@…"; /* chat item text */ "agreeing encryption…" = "négociation du chiffrement…"; +/* No comment provided by engineer. */ +"All" = "Tout"; + /* No comment provided by engineer. */ "All app data is deleted." = "Toutes les données de l'application sont supprimées."; /* No comment provided by engineer. */ "All chats and messages will be deleted - this cannot be undone!" = "Toutes les discussions et tous les messages seront supprimés - il est impossible de revenir en arrière !"; +/* alert message */ +"All chats will be removed from the list %@, and the list deleted." = "Tous les chats seront supprimés de la liste %@, et la liste sera supprimée."; + /* No comment provided by engineer. */ "All data is erased when it is entered." = "Toutes les données sont effacées lorsqu'il est saisi."; +/* No comment provided by engineer. */ +"All data is kept private on your device." = "Toutes les données restent confinées dans votre appareil."; + /* No comment provided by engineer. */ "All group members will remain connected." = "Tous les membres du groupe resteront connectés."; +/* feature role */ +"all members" = "tous les membres"; + +/* No comment provided by engineer. */ +"All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages." = "Tous les messages et fichiers sont envoyés **chiffrés de bout en bout**, avec une sécurité post-quantique dans les messages directs."; + /* No comment provided by engineer. */ "All messages will be deleted - this cannot be undone!" = "Tous les messages seront supprimés - il n'est pas possible de revenir en arrière !"; @@ -410,21 +499,36 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Tous les nouveaux messages de %@ seront cachés !"; +/* profile dropdown */ +"All profiles" = "Tous les profiles"; + +/* No comment provided by engineer. */ +"All reports will be archived for you." = "Tous les rapports seront archivés pour vous."; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Tous vos contacts resteront connectés."; /* No comment provided by engineer. */ "All your contacts will remain connected. Profile update will be sent to your contacts." = "Tous vos contacts resteront connectés. La mise à jour du profil sera envoyée à vos contacts."; +/* No comment provided by engineer. */ +"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Tous vos contacts, conversations et fichiers seront chiffrés en toute sécurité et transférés par morceaux vers les relais XFTP configurés."; + /* No comment provided by engineer. */ "Allow" = "Autoriser"; /* No comment provided by engineer. */ "Allow calls only if your contact allows them." = "Autoriser les appels que si votre contact les autorise."; +/* No comment provided by engineer. */ +"Allow calls?" = "Autoriser les appels ?"; + /* No comment provided by engineer. */ "Allow disappearing messages only if your contact allows it to you." = "Autorise les messages éphémères seulement si votre contact vous l’autorise."; +/* No comment provided by engineer. */ +"Allow downgrade" = "Autoriser la rétrogradation"; + /* No comment provided by engineer. */ "Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Autoriser la suppression irréversible des messages uniquement si votre contact vous l'autorise. (24 heures)"; @@ -440,12 +544,21 @@ /* No comment provided by engineer. */ "Allow sending disappearing messages." = "Autorise l’envoi de messages éphémères."; +/* No comment provided by engineer. */ +"Allow sharing" = "Autoriser le partage"; + /* No comment provided by engineer. */ "Allow to irreversibly delete sent messages. (24 hours)" = "Autoriser la suppression irréversible de messages envoyés. (24 heures)"; +/* No comment provided by engineer. */ +"Allow to report messsages to moderators." = "Permettre de signaler des messages aux modérateurs."; + /* No comment provided by engineer. */ "Allow to send files and media." = "Permet l'envoi de fichiers et de médias."; +/* No comment provided by engineer. */ +"Allow to send SimpleX links." = "Autorise l'envoi de liens SimpleX."; + /* No comment provided by engineer. */ "Allow to send voice messages." = "Autoriser l'envoi de messages vocaux."; @@ -482,6 +595,9 @@ /* pref value */ "always" = "toujours"; +/* No comment provided by engineer. */ +"Always use private routing." = "Toujours utiliser le routage privé."; + /* No comment provided by engineer. */ "Always use relay" = "Se connecter via relais"; @@ -491,12 +607,21 @@ /* No comment provided by engineer. */ "and %lld other events" = "et %lld autres événements"; +/* report reason */ +"Another reason" = "Autre raison"; + /* No comment provided by engineer. */ "Answer call" = "Répondre à l'appel"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "N'importe qui peut heberger un serveur."; + /* No comment provided by engineer. */ "App build: %@" = "Build de l'app : %@"; +/* No comment provided by engineer. */ +"App data migration" = "Transfert des données de l'application"; + /* No comment provided by engineer. */ "App encrypts new local files (except videos)." = "L'application chiffre les nouveaux fichiers locaux (sauf les vidéos)."; @@ -509,6 +634,9 @@ /* No comment provided by engineer. */ "App passcode is replaced with self-destruct passcode." = "Le code d'accès de l'application est remplacé par un code d'autodestruction."; +/* No comment provided by engineer. */ +"App session" = "Session de l'app"; + /* No comment provided by engineer. */ "App version" = "Version de l'app"; @@ -518,9 +646,48 @@ /* No comment provided by engineer. */ "Appearance" = "Apparence"; +/* No comment provided by engineer. */ +"Apply" = "Appliquer"; + +/* No comment provided by engineer. */ +"Apply to" = "Appliquer à"; + +/* No comment provided by engineer. */ +"Archive" = "Archiver"; + +/* No comment provided by engineer. */ +"Archive %lld reports?" = "Archiver les rapports %lld ?"; + +/* No comment provided by engineer. */ +"Archive all reports?" = "Archiver tous les rapports ?"; + +/* No comment provided by engineer. */ +"Archive and upload" = "Archiver et téléverser"; + +/* No comment provided by engineer. */ +"Archive contacts to chat later." = "Archiver les contacts pour discuter plus tard."; + +/* No comment provided by engineer. */ +"Archive report" = "Archiver le rapport"; + +/* No comment provided by engineer. */ +"Archive report?" = "Archiver le rapport ?"; + +/* swipe action */ +"Archive reports" = "Archiver les rapports"; + +/* No comment provided by engineer. */ +"Archived contacts" = "Contacts archivés"; + +/* No comment provided by engineer. */ +"Archiving database" = "Archivage de la base de données"; + /* No comment provided by engineer. */ "Attach" = "Attacher"; +/* No comment provided by engineer. */ +"attempts" = "tentatives"; + /* No comment provided by engineer. */ "Audio & video calls" = "Appels audio et vidéo"; @@ -560,9 +727,15 @@ /* No comment provided by engineer. */ "Auto-accept images" = "Images auto-acceptées"; +/* alert title */ +"Auto-accept settings" = "Paramètres de réception automatique"; + /* No comment provided by engineer. */ "Back" = "Retour"; +/* No comment provided by engineer. */ +"Background" = "Fond"; + /* No comment provided by engineer. */ "Bad desktop address" = "Mauvaise adresse de bureau"; @@ -578,12 +751,39 @@ /* No comment provided by engineer. */ "Bad message ID" = "Mauvais ID de message"; +/* No comment provided by engineer. */ +"Better calls" = "Appels améliorés"; + /* No comment provided by engineer. */ "Better groups" = "Des groupes plus performants"; +/* No comment provided by engineer. */ +"Better groups performance" = "Meilleure performance des groupes"; + +/* No comment provided by engineer. */ +"Better message dates." = "Meilleures dates de messages."; + /* No comment provided by engineer. */ "Better messages" = "Meilleurs messages"; +/* No comment provided by engineer. */ +"Better networking" = "Meilleure gestion de réseau"; + +/* No comment provided by engineer. */ +"Better notifications" = "Notifications améliorées"; + +/* No comment provided by engineer. */ +"Better privacy and security" = "Meilleure protection de la privacité et de la sécurité"; + +/* No comment provided by engineer. */ +"Better security ✅" = "Sécurité accrue ✅"; + +/* No comment provided by engineer. */ +"Better user experience" = "Une meilleure expérience pour l'utilisateur"; + +/* No comment provided by engineer. */ +"Black" = "Noir"; + /* No comment provided by engineer. */ "Block" = "Bloquer"; @@ -608,12 +808,19 @@ /* rcv group event chat item */ "blocked %@" = "%@ bloqué"; -/* marked deleted chat item preview text */ +/* blocked chat item +marked deleted chat item preview text */ "blocked by admin" = "bloqué par l'administrateur"; /* No comment provided by engineer. */ "Blocked by admin" = "Bloqué par l'administrateur"; +/* No comment provided by engineer. */ +"Blur for better privacy." = "Rendez les images floues et protégez-les contre les regards indiscrets."; + +/* No comment provided by engineer. */ +"Blur media" = "Flouter les médias"; + /* No comment provided by engineer. */ "bold" = "gras"; @@ -635,9 +842,24 @@ /* No comment provided by engineer. */ "Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Bulgare, finnois, thaïlandais et ukrainien - grâce aux utilisateurs et à [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat) !"; +/* No comment provided by engineer. */ +"Business address" = "Adresse professionnelle"; + +/* No comment provided by engineer. */ +"Business chats" = "Discussions professionnelles"; + +/* No comment provided by engineer. */ +"Businesses" = "Entreprises"; + /* No comment provided by engineer. */ "By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Par profil de chat (par défaut) ou [par connexion](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)."; +/* No comment provided by engineer. */ +"By using SimpleX Chat you agree to:\n- send only legal content in public groups.\n- respect other users – no spam." = "En utilisant SimpleX Chat, vous acceptez de :\n- n'envoyer que du contenu légal dans les groupes publics.\n- respecter les autres utilisateurs - pas de spam."; + +/* No comment provided by engineer. */ +"call" = "appeler"; + /* No comment provided by engineer. */ "Call already ended!" = "Appel déjà terminé !"; @@ -653,9 +875,18 @@ /* No comment provided by engineer. */ "Calls" = "Appels"; +/* No comment provided by engineer. */ +"Calls prohibited!" = "Les appels ne sont pas autorisés !"; + /* No comment provided by engineer. */ "Camera not available" = "Caméra non disponible"; +/* No comment provided by engineer. */ +"Can't call contact" = "Impossible d'appeler le contact"; + +/* No comment provided by engineer. */ +"Can't call member" = "Impossible d'appeler le membre"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Impossible d'inviter le contact !"; @@ -663,8 +894,15 @@ "Can't invite contacts!" = "Impossible d'inviter les contacts !"; /* No comment provided by engineer. */ +"Can't message member" = "Impossible d'envoyer un message à ce membre"; + +/* alert action +alert button */ "Cancel" = "Annuler"; +/* No comment provided by engineer. */ +"Cancel migration" = "Annuler le transfert"; + /* feature offered item */ "cancelled %@" = "annulé %@"; @@ -672,11 +910,26 @@ "Cannot access keychain to save database password" = "Impossible d'accéder à la keychain pour enregistrer le mot de passe de la base de données"; /* No comment provided by engineer. */ +"Cannot forward message" = "Impossible de transférer le message"; + +/* alert title */ "Cannot receive file" = "Impossible de recevoir le fichier"; +/* snd error text */ +"Capacity exceeded - recipient did not receive previously sent messages." = "Capacité dépassée - le destinataire n'a pas pu recevoir les messages envoyés précédemment."; + +/* No comment provided by engineer. */ +"Cellular" = "Cellulaire"; + /* No comment provided by engineer. */ "Change" = "Changer"; +/* alert title */ +"Change automatic message deletion?" = "Modifier la suppression automatique des messages ?"; + +/* authentication reason */ +"Change chat profiles" = "Changer de profil de discussion"; + /* No comment provided by engineer. */ "Change database passphrase?" = "Changer la phrase secrète de la base de données ?"; @@ -702,7 +955,7 @@ "Change self-destruct mode" = "Modifier le mode d'autodestruction"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "Modifier le code d'autodestruction"; /* chat item text */ @@ -721,7 +974,16 @@ "changing address…" = "changement d'adresse…"; /* No comment provided by engineer. */ -"Chat archive" = "Archives du chat"; +"Chat" = "Discussions"; + +/* No comment provided by engineer. */ +"Chat already exists" = "La discussion existe déjà"; + +/* No comment provided by engineer. */ +"Chat already exists!" = "La discussion existe déjà !"; + +/* No comment provided by engineer. */ +"Chat colors" = "Couleurs de chat"; /* No comment provided by engineer. */ "Chat console" = "Console du chat"; @@ -732,6 +994,9 @@ /* No comment provided by engineer. */ "Chat database deleted" = "Base de données du chat supprimée"; +/* No comment provided by engineer. */ +"Chat database exported" = "Exportation de la base de données des discussions"; + /* No comment provided by engineer. */ "Chat database imported" = "Base de données du chat importée"; @@ -744,18 +1009,48 @@ /* No comment provided by engineer. */ "Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "Le chat est arrêté. Si vous avez déjà utilisé cette base de données sur un autre appareil, vous devez la transférer à nouveau avant de démarrer le chat."; +/* No comment provided by engineer. */ +"Chat list" = "Liste de discussion"; + +/* No comment provided by engineer. */ +"Chat migrated!" = "Messagerie transférée !"; + /* No comment provided by engineer. */ "Chat preferences" = "Préférences de chat"; +/* alert message */ +"Chat preferences were changed." = "Les préférences de discussion ont été modifiées."; + +/* No comment provided by engineer. */ +"Chat profile" = "Profil d'utilisateur"; + +/* No comment provided by engineer. */ +"Chat theme" = "Thème de chat"; + +/* No comment provided by engineer. */ +"Chat will be deleted for all members - this cannot be undone!" = "La discussion sera supprimé pour tous les membres - cela ne peut pas être annulé !"; + +/* No comment provided by engineer. */ +"Chat will be deleted for you - this cannot be undone!" = "Le discussion sera supprimé pour vous - il n'est pas possible de revenir en arrière !"; + /* No comment provided by engineer. */ "Chats" = "Discussions"; /* No comment provided by engineer. */ +"Check messages every 20 min." = "Consulter les messages toutes les 20 minutes."; + +/* No comment provided by engineer. */ +"Check messages when allowed." = "Consulter les messages quand c'est possible."; + +/* alert title */ "Check server address and try again." = "Vérifiez l'adresse du serveur et réessayez."; /* No comment provided by engineer. */ "Chinese and Spanish interface" = "Interface en chinois et en espagnol"; +/* No comment provided by engineer. */ +"Choose _Migrate from another device_ on the new device and scan QR code." = "Choisissez _Transferer depuis un autre appareil_ sur le nouvel appareil et scannez le code QR."; + /* No comment provided by engineer. */ "Choose file" = "Choisir le fichier"; @@ -763,6 +1058,15 @@ "Choose from library" = "Choisir dans la photothèque"; /* No comment provided by engineer. */ +"Chunks deleted" = "Chunks supprimés"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "Chunks téléchargés"; + +/* No comment provided by engineer. */ +"Chunks uploaded" = "Chunks téléversés"; + +/* swipe action */ "Clear" = "Effacer"; /* No comment provided by engineer. */ @@ -771,6 +1075,12 @@ /* No comment provided by engineer. */ "Clear conversation?" = "Effacer la conversation ?"; +/* No comment provided by engineer. */ +"Clear group?" = "Vider le groupe ?"; + +/* No comment provided by engineer. */ +"Clear or delete group?" = "Vider ou supprimer le groupe ?"; + /* No comment provided by engineer. */ "Clear private notes?" = "Effacer les notes privées ?"; @@ -778,10 +1088,16 @@ "Clear verification" = "Retirer la vérification"; /* No comment provided by engineer. */ -"colored" = "coloré"; +"Color chats with the new themes." = "Colorez vos discussions avec les nouveaux thèmes."; /* No comment provided by engineer. */ -"Colors" = "Couleurs"; +"Color mode" = "Mode de couleur"; + +/* No comment provided by engineer. */ +"colored" = "coloré"; + +/* report reason */ +"Community guidelines violation" = "Infraction aux règles communautaires"; /* server test step */ "Compare file" = "Comparer le fichier"; @@ -792,15 +1108,51 @@ /* No comment provided by engineer. */ "complete" = "complet"; +/* No comment provided by engineer. */ +"Completed" = "Complétées"; + +/* No comment provided by engineer. */ +"Conditions accepted on: %@." = "Conditions acceptées le : %@."; + +/* No comment provided by engineer. */ +"Conditions are accepted for the operator(s): **%@**." = "Les conditions sont acceptées pour le(s) opérateur(s) : **%@**."; + +/* No comment provided by engineer. */ +"Conditions are already accepted for these operator(s): **%@**." = "Les conditions sont déjà acceptées pour ces opérateurs : **%@**."; + +/* No comment provided by engineer. */ +"Conditions of use" = "Conditions d'utilisation"; + +/* No comment provided by engineer. */ +"Conditions will be accepted for the operator(s): **%@**." = "Les conditions seront acceptées pour le(s) opérateur(s) : **%@**."; + +/* No comment provided by engineer. */ +"Conditions will be accepted on: %@." = "Les conditions seront acceptées le : %@."; + +/* No comment provided by engineer. */ +"Conditions will be automatically accepted for enabled operators on: %@." = "Les conditions seront automatiquement acceptées pour les opérateurs activés le : %@."; + /* No comment provided by engineer. */ "Configure ICE servers" = "Configurer les serveurs ICE"; +/* No comment provided by engineer. */ +"Configure server operators" = "Configurer les opérateurs de serveur"; + /* No comment provided by engineer. */ "Confirm" = "Confirmer"; +/* No comment provided by engineer. */ +"Confirm contact deletion?" = "Confirmer la suppression du contact ?"; + /* No comment provided by engineer. */ "Confirm database upgrades" = "Confirmer la mise à niveau de la base de données"; +/* No comment provided by engineer. */ +"Confirm files from unknown servers." = "Confirmer les fichiers provenant de serveurs inconnus."; + +/* No comment provided by engineer. */ +"Confirm network settings" = "Confirmer les paramètres réseau"; + /* No comment provided by engineer. */ "Confirm new passphrase…" = "Confirmer la nouvelle phrase secrète…"; @@ -810,6 +1162,15 @@ /* No comment provided by engineer. */ "Confirm password" = "Confirmer le mot de passe"; +/* No comment provided by engineer. */ +"Confirm that you remember database passphrase to migrate it." = "Confirmer que vous vous souvenez de la phrase secrète de la base de données pour la transférer."; + +/* No comment provided by engineer. */ +"Confirm upload" = "Confirmer la transmission"; + +/* token status text */ +"Confirmed" = "Confirmé"; + /* server test step */ "Connect" = "Se connecter"; @@ -825,6 +1186,9 @@ /* No comment provided by engineer. */ "connect to SimpleX Chat developers." = "se connecter aux developpeurs de SimpleX Chat."; +/* No comment provided by engineer. */ +"Connect to your friends faster." = "Connectez-vous à vos amis plus rapidement."; + /* No comment provided by engineer. */ "Connect to yourself?" = "Se connecter à soi-même ?"; @@ -849,18 +1213,27 @@ /* No comment provided by engineer. */ "connected" = "connecté"; +/* No comment provided by engineer. */ +"Connected" = "Connecté"; + /* No comment provided by engineer. */ "Connected desktop" = "Bureau connecté"; /* rcv group event chat item */ "connected directly" = "s'est connecté.e de manière directe"; +/* No comment provided by engineer. */ +"Connected servers" = "Serveurs connectés"; + /* No comment provided by engineer. */ "Connected to desktop" = "Connecté au bureau"; /* No comment provided by engineer. */ "connecting" = "connexion"; +/* No comment provided by engineer. */ +"Connecting" = "Connexion"; + /* No comment provided by engineer. */ "connecting (accepted)" = "connexion (acceptée)"; @@ -882,15 +1255,24 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "Connexion au serveur… (erreur : %@)"; +/* No comment provided by engineer. */ +"Connecting to contact, please wait or check later!" = "Connexion au contact, veuillez patienter ou vérifier plus tard !"; + /* No comment provided by engineer. */ "Connecting to desktop" = "Connexion au bureau"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "connexion…"; /* No comment provided by engineer. */ "Connection" = "Connexion"; +/* No comment provided by engineer. */ +"Connection and servers status." = "État de la connexion et des serveurs."; + +/* No comment provided by engineer. */ +"Connection blocked" = "Connexion bloquée"; + /* No comment provided by engineer. */ "Connection error" = "Erreur de connexion"; @@ -900,18 +1282,39 @@ /* chat list item title (it should not be shown */ "connection established" = "connexion établie"; +/* No comment provided by engineer. */ +"Connection is blocked by server operator:\n%@" = "La connexion est bloquée par l'opérateur du serveur :\n%@"; + +/* No comment provided by engineer. */ +"Connection not ready." = "La connexion n'est pas prête."; + +/* No comment provided by engineer. */ +"Connection notifications" = "Notifications de connexion"; + /* No comment provided by engineer. */ "Connection request sent!" = "Demande de connexion envoyée !"; +/* No comment provided by engineer. */ +"Connection requires encryption renegotiation." = "La connexion nécessite une renégociation du cryptage."; + +/* No comment provided by engineer. */ +"Connection security" = "Sécurité des connexions"; + /* No comment provided by engineer. */ "Connection terminated" = "Connexion terminée"; /* No comment provided by engineer. */ "Connection timeout" = "Délai de connexion"; +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "La connexion avec le bureau s'est arrêtée"; + /* connection information */ "connection:%@" = "connexion : %@"; +/* No comment provided by engineer. */ +"Connections" = "Connexions"; + /* profile update event chat item */ "contact %@ changed to %@" = "le contact %1$@ est devenu %2$@"; @@ -921,6 +1324,9 @@ /* No comment provided by engineer. */ "Contact already exists" = "Contact déjà existant"; +/* No comment provided by engineer. */ +"Contact deleted!" = "Contact supprimé !"; + /* No comment provided by engineer. */ "contact has e2e encryption" = "Ce contact a le chiffrement de bout en bout"; @@ -934,7 +1340,7 @@ "Contact is connected" = "Le contact est connecté"; /* No comment provided by engineer. */ -"Contact is not connected yet!" = "Le contact n'est pas encore connecté !"; +"Contact is deleted." = "Le contact est supprimé."; /* No comment provided by engineer. */ "Contact name" = "Nom du contact"; @@ -942,21 +1348,36 @@ /* No comment provided by engineer. */ "Contact preferences" = "Préférences de contact"; +/* No comment provided by engineer. */ +"Contact will be deleted - this cannot be undone!" = "Le contact sera supprimé - il n'est pas possible de revenir en arrière !"; + /* No comment provided by engineer. */ "Contacts" = "Contacts"; /* No comment provided by engineer. */ "Contacts can mark messages for deletion; you will be able to view them." = "Vos contacts peuvent marquer les messages pour les supprimer ; vous pourrez les consulter."; +/* blocking reason */ +"Content violates conditions of use" = "Le contenu enfreint les conditions d'utilisation"; + /* No comment provided by engineer. */ "Continue" = "Continuer"; -/* chat item action */ +/* No comment provided by engineer. */ +"Conversation deleted!" = "Conversation supprimée !"; + +/* No comment provided by engineer. */ "Copy" = "Copier"; +/* No comment provided by engineer. */ +"Copy error" = "Erreur de copie"; + /* No comment provided by engineer. */ "Core version: v%@" = "Version du cœur : v%@"; +/* No comment provided by engineer. */ +"Corner" = "Coin"; + /* No comment provided by engineer. */ "Correct name to %@?" = "Corriger le nom pour %@ ?"; @@ -964,10 +1385,10 @@ "Create" = "Créer"; /* No comment provided by engineer. */ -"Create a group using a random profile." = "Création de groupes via un profil aléatoire."; +"Create 1-time link" = "Créer un lien unique"; /* No comment provided by engineer. */ -"Create an address to let people connect with you." = "Vous pouvez créer une adresse pour permettre aux autres utilisateurs de vous contacter."; +"Create a group using a random profile." = "Création de groupes via un profil aléatoire."; /* server test step */ "Create file" = "Créer un fichier"; @@ -981,6 +1402,9 @@ /* No comment provided by engineer. */ "Create link" = "Créer un lien"; +/* No comment provided by engineer. */ +"Create list" = "Créer une liste"; + /* No comment provided by engineer. */ "Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" = "Créer un nouveau profil sur [l'application de bureau](https://simplex.chat/downloads/). 💻"; @@ -999,6 +1423,9 @@ /* No comment provided by engineer. */ "Create your profile" = "Créez votre profil"; +/* No comment provided by engineer. */ +"Created" = "Créées"; + /* No comment provided by engineer. */ "Created at" = "Créé à"; @@ -1006,7 +1433,7 @@ "Created at: %@" = "Créé à : %@"; /* No comment provided by engineer. */ -"Created on %@" = "Créé le %@"; +"Creating archive link" = "Création d'un lien d'archive"; /* No comment provided by engineer. */ "Creating link…" = "Création d'un lien…"; @@ -1014,12 +1441,18 @@ /* No comment provided by engineer. */ "creator" = "créateur"; +/* No comment provided by engineer. */ +"Current conditions text couldn't be loaded, you can review conditions via this link:" = "Le texte sur les conditions actuelles n'a pas pu être chargé. Vous pouvez consulter les conditions en cliquant sur ce lien :"; + /* No comment provided by engineer. */ "Current Passcode" = "Code d'accès actuel"; /* No comment provided by engineer. */ "Current passphrase…" = "Phrase secrète actuelle…"; +/* No comment provided by engineer. */ +"Current profile" = "Profil actuel"; + /* No comment provided by engineer. */ "Currently maximum supported file size is %@." = "Actuellement, la taille maximale des fichiers supportés est de %@."; @@ -1029,9 +1462,18 @@ /* No comment provided by engineer. */ "Custom time" = "Délai personnalisé"; +/* No comment provided by engineer. */ +"Customizable message shape." = "Forme des messages personnalisable."; + +/* No comment provided by engineer. */ +"Customize theme" = "Personnaliser le thème"; + /* No comment provided by engineer. */ "Dark" = "Sombre"; +/* No comment provided by engineer. */ +"Dark mode colors" = "Couleurs en mode sombre"; + /* No comment provided by engineer. */ "Database downgrade" = "Rétrogradation de la base de données"; @@ -1092,13 +1534,20 @@ /* time unit */ "days" = "jours"; +/* No comment provided by engineer. */ +"Debug delivery" = "Livraison de débogage"; + /* No comment provided by engineer. */ "Decentralized" = "Décentralisé"; /* message decrypt error item */ "Decryption error" = "Erreur de déchiffrement"; -/* pref value */ +/* No comment provided by engineer. */ +"decryption errors" = "Erreurs de déchiffrement"; + +/* delete after time +pref value */ "default (%@)" = "défaut (%@)"; /* No comment provided by engineer. */ @@ -1107,9 +1556,13 @@ /* No comment provided by engineer. */ "default (yes)" = "par défaut (oui)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "Supprimer"; +/* No comment provided by engineer. */ +"Delete %lld messages of members?" = "Supprimer %lld messages de membres ?"; + /* No comment provided by engineer. */ "Delete %lld messages?" = "Supprimer %lld messages ?"; @@ -1129,10 +1582,10 @@ "Delete and notify contact" = "Supprimer et en informer le contact"; /* No comment provided by engineer. */ -"Delete archive" = "Supprimer l'archive"; +"Delete chat" = "Supprimer la discussion"; /* No comment provided by engineer. */ -"Delete chat archive?" = "Supprimer l'archive du chat ?"; +"Delete chat messages from your device." = "Supprimer les messages de chat de votre appareil."; /* No comment provided by engineer. */ "Delete chat profile" = "Supprimer le profil de chat"; @@ -1140,6 +1593,9 @@ /* No comment provided by engineer. */ "Delete chat profile?" = "Supprimer le profil du chat ?"; +/* No comment provided by engineer. */ +"Delete chat?" = "Supprimer la discussion ?"; + /* No comment provided by engineer. */ "Delete connection" = "Supprimer la connexion"; @@ -1147,14 +1603,14 @@ "Delete contact" = "Supprimer le contact"; /* No comment provided by engineer. */ -"Delete Contact" = "Supprimer le contact"; - -/* No comment provided by engineer. */ -"Delete contact?\nThis cannot be undone!" = "Supprimer le contact ?\nCette opération ne peut être annulée !"; +"Delete contact?" = "Supprimer le contact ?"; /* No comment provided by engineer. */ "Delete database" = "Supprimer la base de données"; +/* No comment provided by engineer. */ +"Delete database from this device" = "Supprimer la base de données de cet appareil"; + /* server test step */ "Delete file" = "Supprimer le fichier"; @@ -1185,13 +1641,16 @@ /* No comment provided by engineer. */ "Delete link?" = "Supprimer le lien ?"; +/* alert title */ +"Delete list?" = "Supprimer la liste ?"; + /* No comment provided by engineer. */ "Delete member message?" = "Supprimer le message de ce membre ?"; /* No comment provided by engineer. */ "Delete message?" = "Supprimer le message ?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "Supprimer les messages"; /* No comment provided by engineer. */ @@ -1204,7 +1663,7 @@ "Delete old database?" = "Supprimer l'ancienne base de données ?"; /* No comment provided by engineer. */ -"Delete pending connection" = "Supprimer la connexion en attente"; +"Delete or moderate up to 200 messages." = "Supprimer ou modérer jusqu'à 200 messages."; /* No comment provided by engineer. */ "Delete pending connection?" = "Supprimer la connexion en attente ?"; @@ -1215,12 +1674,24 @@ /* server test step */ "Delete queue" = "Supprimer la file d'attente"; +/* No comment provided by engineer. */ +"Delete report" = "Supprimer le rapport"; + +/* No comment provided by engineer. */ +"Delete up to 20 messages at once." = "Supprimez jusqu'à 20 messages à la fois."; + /* No comment provided by engineer. */ "Delete user profile?" = "Supprimer le profil utilisateur ?"; +/* No comment provided by engineer. */ +"Delete without notification" = "Supprimer sans notification"; + /* deleted chat item */ "deleted" = "supprimé"; +/* No comment provided by engineer. */ +"Deleted" = "Supprimées"; + /* No comment provided by engineer. */ "Deleted at" = "Supprimé à"; @@ -1233,6 +1704,12 @@ /* rcv group event chat item */ "deleted group" = "groupe supprimé"; +/* No comment provided by engineer. */ +"Deletion errors" = "Erreurs de suppression"; + +/* No comment provided by engineer. */ +"Delivered even when Apple drops them." = "Distribués même quand Apple les oublie."; + /* No comment provided by engineer. */ "Delivery" = "Distribution"; @@ -1240,7 +1717,7 @@ "Delivery receipts are disabled!" = "Les accusés de réception sont désactivés !"; /* No comment provided by engineer. */ -"Delivery receipts!" = "Justificatifs de réception!"; +"Delivery receipts!" = "Justificatifs de réception !"; /* No comment provided by engineer. */ "Description" = "Description"; @@ -1254,9 +1731,27 @@ /* No comment provided by engineer. */ "Desktop devices" = "Appareils de bureau"; +/* No comment provided by engineer. */ +"Destination server address of %@ is incompatible with forwarding server %@ settings." = "L'adresse du serveur de destination %@ est incompatible avec les paramètres du serveur de redirection %@."; + +/* snd error text */ +"Destination server error: %@" = "Erreur du serveur de destination : %@"; + +/* No comment provided by engineer. */ +"Destination server version of %@ is incompatible with forwarding server %@." = "La version du serveur de destination %@ est incompatible avec le serveur de redirection %@."; + +/* No comment provided by engineer. */ +"Detailed statistics" = "Statistiques détaillées"; + +/* No comment provided by engineer. */ +"Details" = "Détails"; + /* No comment provided by engineer. */ "Develop" = "Développer"; +/* No comment provided by engineer. */ +"Developer options" = "Options pour les développeurs"; + /* No comment provided by engineer. */ "Developer tools" = "Outils du développeur"; @@ -1282,11 +1777,20 @@ "Direct messages" = "Messages directs"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "Les messages directs entre membres sont interdits dans ce groupe."; +"Direct messages between members are prohibited in this chat." = "Les messages directs entre membres sont interdits dans cette discussion."; + +/* No comment provided by engineer. */ +"Direct messages between members are prohibited." = "Les messages directs entre membres sont interdits dans ce groupe."; /* No comment provided by engineer. */ "Disable (keep overrides)" = "Désactiver (conserver les remplacements)"; +/* alert title */ +"Disable automatic message deletion?" = "Désactiver la suppression automatique des messages ?"; + +/* alert button */ +"Disable delete messages" = "Désactiver la suppression des messages"; + /* No comment provided by engineer. */ "Disable for all" = "Désactiver pour tous"; @@ -1296,6 +1800,9 @@ /* No comment provided by engineer. */ "disabled" = "désactivé"; +/* No comment provided by engineer. */ +"Disabled" = "Désactivé"; + /* No comment provided by engineer. */ "Disappearing message" = "Message éphémère"; @@ -1306,7 +1813,7 @@ "Disappearing messages are prohibited in this chat." = "Les messages éphémères sont interdits dans cette discussion."; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "Les messages éphémères sont interdits dans ce groupe."; +"Disappearing messages are prohibited." = "Les messages éphémères sont interdits dans ce groupe."; /* No comment provided by engineer. */ "Disappears at" = "Disparaîtra le"; @@ -1332,36 +1839,85 @@ /* No comment provided by engineer. */ "Do not send history to new members." = "Ne pas envoyer d'historique aux nouveaux membres."; +/* No comment provided by engineer. */ +"Do NOT send messages directly, even if your or destination server does not support private routing." = "Ne pas envoyer de messages directement, même si votre serveur ou le serveur de destination ne prend pas en charge le routage privé."; + +/* No comment provided by engineer. */ +"Do not use credentials with proxy." = "Ne pas utiliser d'identifiants avec le proxy."; + +/* No comment provided by engineer. */ +"Do NOT use private routing." = "Ne pas utiliser de routage privé."; + /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "N'utilisez PAS SimpleX pour les appels d'urgence."; +/* No comment provided by engineer. */ +"Documents:" = "Documents:"; + /* No comment provided by engineer. */ "Don't create address" = "Ne pas créer d'adresse"; /* No comment provided by engineer. */ "Don't enable" = "Ne pas activer"; +/* No comment provided by engineer. */ +"Don't miss important messages." = "Ne manquez pas les messages importants."; + /* No comment provided by engineer. */ "Don't show again" = "Ne plus afficher"; +/* No comment provided by engineer. */ +"Done" = "Terminé"; + /* No comment provided by engineer. */ "Downgrade and open chat" = "Rétrograder et ouvrir le chat"; +/* alert button +chat item action */ +"Download" = "Télécharger"; + +/* No comment provided by engineer. */ +"Download errors" = "Erreurs de téléchargement"; + +/* No comment provided by engineer. */ +"Download failed" = "Échec du téléchargement"; + /* server test step */ "Download file" = "Télécharger le fichier"; +/* alert action */ +"Download files" = "Télécharger les fichiers"; + +/* No comment provided by engineer. */ +"Downloaded" = "Téléchargé"; + +/* No comment provided by engineer. */ +"Downloaded files" = "Fichiers téléchargés"; + +/* No comment provided by engineer. */ +"Downloading archive" = "Téléchargement de l'archive"; + +/* No comment provided by engineer. */ +"Downloading link details" = "Téléchargement des détails du lien"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Nom d'affichage en double !"; /* integrity error chat item */ "duplicate message" = "message dupliqué"; +/* No comment provided by engineer. */ +"duplicates" = "doublons"; + /* No comment provided by engineer. */ "Duration" = "Durée"; /* No comment provided by engineer. */ "e2e encrypted" = "chiffré de bout en bout"; +/* No comment provided by engineer. */ +"E2E encrypted notifications." = "Notifications chiffrées E2E."; + /* chat item action */ "Edit" = "Modifier"; @@ -1374,15 +1930,21 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "Activer (conserver les remplacements)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "Activer la suppression automatique des messages ?"; /* No comment provided by engineer. */ "Enable camera access" = "Autoriser l'accès à la caméra"; +/* No comment provided by engineer. */ +"Enable Flux in Network & servers settings for better metadata privacy." = "Activez Flux dans les paramètres du réseau et des serveurs pour une meilleure confidentialité des métadonnées."; + /* No comment provided by engineer. */ "Enable for all" = "Activer pour tous"; +/* No comment provided by engineer. */ +"Enable in direct chats (BETA)!" = "Activer dans les conversations directes (BETA) !"; + /* No comment provided by engineer. */ "Enable instant notifications?" = "Activer les notifications instantanées ?"; @@ -1410,6 +1972,12 @@ /* enabled status */ "enabled" = "activé"; +/* No comment provided by engineer. */ +"Enabled" = "Activé"; + +/* No comment provided by engineer. */ +"Enabled for" = "Activé pour"; + /* enabled status */ "enabled for contact" = "activé pour le contact"; @@ -1482,6 +2050,9 @@ /* chat item text */ "encryption re-negotiation required for %@" = "renégociation de chiffrement requise pour %@"; +/* No comment provided by engineer. */ +"Encryption renegotiation in progress." = "Renégociation du chiffrement en cours."; + /* No comment provided by engineer. */ "ended" = "terminé"; @@ -1497,6 +2068,9 @@ /* No comment provided by engineer. */ "Enter Passcode" = "Entrer le code d'accès"; +/* No comment provided by engineer. */ +"Enter passphrase" = "Entrer la phrase secrète"; + /* No comment provided by engineer. */ "Enter passphrase…" = "Entrez la phrase secrète…"; @@ -1527,24 +2101,39 @@ /* No comment provided by engineer. */ "Error aborting address change" = "Erreur lors de l'annulation du changement d'adresse"; +/* alert title */ +"Error accepting conditions" = "Erreur lors de la validation des conditions"; + /* No comment provided by engineer. */ "Error accepting contact request" = "Erreur de validation de la demande de contact"; -/* No comment provided by engineer. */ -"Error accessing database file" = "Erreur d'accès au fichier de la base de données"; - /* No comment provided by engineer. */ "Error adding member(s)" = "Erreur lors de l'ajout de membre·s"; +/* alert title */ +"Error adding server" = "Erreur lors de l'ajout du serveur"; + /* No comment provided by engineer. */ "Error changing address" = "Erreur de changement d'adresse"; +/* No comment provided by engineer. */ +"Error changing connection profile" = "Erreur lors du changement de profil de connexion"; + /* No comment provided by engineer. */ "Error changing role" = "Erreur lors du changement de rôle"; /* No comment provided by engineer. */ "Error changing setting" = "Erreur de changement de paramètre"; +/* No comment provided by engineer. */ +"Error changing to incognito!" = "Erreur lors du passage en mode incognito !"; + +/* No comment provided by engineer. */ +"Error checking token status" = "Erreur lors de la vérification de l'état du jeton (token)"; + +/* No comment provided by engineer. */ +"Error connecting to forwarding server %@. Please try later." = "Erreur de connexion au serveur de redirection %@. Veuillez réessayer plus tard."; + /* No comment provided by engineer. */ "Error creating address" = "Erreur lors de la création de l'adresse"; @@ -1554,6 +2143,9 @@ /* No comment provided by engineer. */ "Error creating group link" = "Erreur lors de la création du lien du groupe"; +/* alert title */ +"Error creating list" = "Erreur lors de la création de la liste"; + /* No comment provided by engineer. */ "Error creating member contact" = "Erreur lors de la création du contact du membre"; @@ -1563,6 +2155,9 @@ /* No comment provided by engineer. */ "Error creating profile!" = "Erreur lors de la création du profil !"; +/* No comment provided by engineer. */ +"Error creating report" = "Erreur lors de la création du rapport"; + /* No comment provided by engineer. */ "Error decrypting file" = "Erreur lors du déchiffrement du fichier"; @@ -1575,9 +2170,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "Erreur lors de la suppression de la connexion"; -/* No comment provided by engineer. */ -"Error deleting contact" = "Erreur lors de la suppression du contact"; - /* No comment provided by engineer. */ "Error deleting database" = "Erreur lors de la suppression de la base de données"; @@ -1590,6 +2182,9 @@ /* No comment provided by engineer. */ "Error deleting user profile" = "Erreur lors de la suppression du profil utilisateur"; +/* No comment provided by engineer. */ +"Error downloading the archive" = "Erreur lors du téléchargement de l'archive"; + /* No comment provided by engineer. */ "Error enabling delivery receipts!" = "Erreur lors de l'activation des accusés de réception !"; @@ -1602,26 +2197,47 @@ /* No comment provided by engineer. */ "Error exporting chat database" = "Erreur lors de l'exportation de la base de données du chat"; +/* No comment provided by engineer. */ +"Error exporting theme: %@" = "Erreur d'exportation du thème : %@"; + /* No comment provided by engineer. */ "Error importing chat database" = "Erreur lors de l'importation de la base de données du chat"; /* No comment provided by engineer. */ "Error joining group" = "Erreur lors de la liaison avec le groupe"; +/* alert title */ +"Error loading servers" = "Erreur de chargement des serveurs"; + /* No comment provided by engineer. */ -"Error loading %@ servers" = "Erreur lors du chargement des serveurs %@"; +"Error migrating settings" = "Erreur lors de la migration des paramètres"; /* No comment provided by engineer. */ "Error opening chat" = "Erreur lors de l'ouverture du chat"; -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Erreur lors de la réception du fichier"; +/* No comment provided by engineer. */ +"Error reconnecting server" = "Erreur de reconnexion du serveur"; + +/* No comment provided by engineer. */ +"Error reconnecting servers" = "Erreur de reconnexion des serveurs"; + +/* alert title */ +"Error registering for notifications" = "Erreur lors de l'inscription aux notifications"; + /* No comment provided by engineer. */ "Error removing member" = "Erreur lors de la suppression d'un membre"; +/* alert title */ +"Error reordering lists" = "Erreur lors de la réorganisation des listes"; + /* No comment provided by engineer. */ -"Error saving %@ servers" = "Erreur lors de la sauvegarde des serveurs %@"; +"Error resetting statistics" = "Erreur de réinitialisation des statistiques"; + +/* alert title */ +"Error saving chat list" = "Erreur lors de l'enregistrement de la liste des chats"; /* No comment provided by engineer. */ "Error saving group profile" = "Erreur lors de la sauvegarde du profil de groupe"; @@ -1635,6 +2251,12 @@ /* No comment provided by engineer. */ "Error saving passphrase to keychain" = "Erreur lors de l'enregistrement de la phrase de passe dans la keychain"; +/* alert title */ +"Error saving servers" = "Erreur d'enregistrement des serveurs"; + +/* when migrating */ +"Error saving settings" = "Erreur lors de l'enregistrement des paramètres"; + /* No comment provided by engineer. */ "Error saving user password" = "Erreur d'enregistrement du mot de passe de l'utilisateur"; @@ -1660,17 +2282,26 @@ "Error stopping chat" = "Erreur lors de l'arrêt du chat"; /* No comment provided by engineer. */ +"Error switching profile" = "Erreur lors du changement de profil"; + +/* alertTitle */ "Error switching profile!" = "Erreur lors du changement de profil !"; /* No comment provided by engineer. */ "Error synchronizing connection" = "Erreur de synchronisation de connexion"; +/* No comment provided by engineer. */ +"Error testing server connection" = "Erreur lors du test de connexion au serveur"; + /* No comment provided by engineer. */ "Error updating group link" = "Erreur lors de la mise à jour du lien de groupe"; /* No comment provided by engineer. */ "Error updating message" = "Erreur lors de la mise à jour du message"; +/* alert title */ +"Error updating server" = "Erreur de mise à jour du serveur"; + /* No comment provided by engineer. */ "Error updating settings" = "Erreur lors de la mise à jour des paramètres"; @@ -1678,9 +2309,17 @@ "Error updating user privacy" = "Erreur de mise à jour de la confidentialité de l'utilisateur"; /* No comment provided by engineer. */ -"Error: " = "Erreur : "; +"Error uploading the archive" = "Erreur lors de l'envoi de l'archive"; /* No comment provided by engineer. */ +"Error verifying passphrase:" = "Erreur lors de la vérification de la phrase secrète :"; + +/* No comment provided by engineer. */ +"Error: " = "Erreur : "; + +/* alert message +file error text +snd error text */ "Error: %@" = "Erreur : %@"; /* No comment provided by engineer. */ @@ -1690,10 +2329,13 @@ "Error: URL is invalid" = "Erreur : URL invalide"; /* No comment provided by engineer. */ -"Even when disabled in the conversation." = "Même s'il est désactivé dans la conversation."; +"Errors" = "Erreurs"; + +/* servers error */ +"Errors in servers configuration." = "Erreurs dans la configuration des serveurs."; /* No comment provided by engineer. */ -"event happened" = "event happened"; +"Even when disabled in the conversation." = "Même s'il est désactivé dans la conversation."; /* No comment provided by engineer. */ "Exit without saving" = "Quitter sans enregistrer"; @@ -1701,15 +2343,27 @@ /* chat item action */ "Expand" = "Étendre"; +/* No comment provided by engineer. */ +"expired" = "expiré"; + +/* token status text */ +"Expired" = "Expiré"; + /* No comment provided by engineer. */ "Export database" = "Exporter la base de données"; /* No comment provided by engineer. */ "Export error:" = "Erreur lors de l'exportation :"; +/* No comment provided by engineer. */ +"Export theme" = "Exporter le thème"; + /* No comment provided by engineer. */ "Exported database archive." = "Archive de la base de données exportée."; +/* No comment provided by engineer. */ +"Exported file doesn't exist" = "Le fichier exporté n'existe pas"; + /* No comment provided by engineer. */ "Exporting database archive…" = "Exportation de l'archive de la base de données…"; @@ -1719,12 +2373,42 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "Rapide et ne nécessitant pas d'attendre que l'expéditeur soit en ligne !"; +/* No comment provided by engineer. */ +"Faster deletion of groups." = "Suppression plus rapide des groupes."; + /* No comment provided by engineer. */ "Faster joining and more reliable messages." = "Connexion plus rapide et messages plus fiables."; /* No comment provided by engineer. */ +"Faster sending messages." = "Envoi plus rapide des messages."; + +/* swipe action */ "Favorite" = "Favoris"; +/* No comment provided by engineer. */ +"Favorites" = "Favoris"; + +/* file error alert title */ +"File error" = "Erreur de fichier"; + +/* alert message */ +"File errors:\n%@" = "Erreurs de fichier :\n%@"; + +/* file error text */ +"File is blocked by server operator:\n%@." = "Le fichier est bloqué par l'opérateur du serveur :\n%@."; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "Fichier introuvable - le fichier a probablement été supprimé ou annulé."; + +/* file error text */ +"File server error: %@" = "Erreur de serveur de fichiers : %@"; + +/* No comment provided by engineer. */ +"File status" = "Statut du fichier"; + +/* copied message info */ +"File status: %@" = "Statut du fichier : %@"; + /* No comment provided by engineer. */ "File will be deleted from servers." = "Le fichier sera supprimé des serveurs."; @@ -1737,6 +2421,9 @@ /* No comment provided by engineer. */ "File: %@" = "Fichier : %@"; +/* No comment provided by engineer. */ +"Files" = "Fichiers"; + /* No comment provided by engineer. */ "Files & media" = "Fichiers & médias"; @@ -1744,7 +2431,10 @@ "Files and media" = "Fichiers et médias"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "Les fichiers et les médias sont interdits dans ce groupe."; +"Files and media are prohibited." = "Les fichiers et les médias sont interdits dans ce groupe."; + +/* No comment provided by engineer. */ +"Files and media not allowed" = "Fichiers et médias non autorisés"; /* No comment provided by engineer. */ "Files and media prohibited!" = "Fichiers et médias interdits !"; @@ -1752,6 +2442,12 @@ /* No comment provided by engineer. */ "Filter unread and favorite chats." = "Filtrer les messages non lus et favoris."; +/* No comment provided by engineer. */ +"Finalize migration" = "Finaliser le transfert"; + +/* No comment provided by engineer. */ +"Finalize migration on another device." = "Finalisez le transfert sur l'autre appareil."; + /* No comment provided by engineer. */ "Finally, we have them! 🚀" = "Enfin, les voilà ! 🚀"; @@ -1765,7 +2461,7 @@ "Fix connection" = "Réparer la connexion"; /* No comment provided by engineer. */ -"Fix connection?" = "Réparer la connexion?"; +"Fix connection?" = "Réparer la connexion ?"; /* No comment provided by engineer. */ "Fix encryption after restoring backups." = "Réparer le chiffrement après la restauration des sauvegardes."; @@ -1776,9 +2472,66 @@ /* No comment provided by engineer. */ "Fix not supported by group member" = "Correction non prise en charge par un membre du groupe"; +/* servers error */ +"For chat profile %@:" = "Pour le profil de discussion %@ :"; + /* No comment provided by engineer. */ "For console" = "Pour la console"; +/* No comment provided by engineer. */ +"For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server." = "Par exemple, si votre contact reçoit des messages via un serveur SimpleX Chat, votre application les transmettra via un serveur Flux."; + +/* No comment provided by engineer. */ +"For private routing" = "Pour le routage privé"; + +/* No comment provided by engineer. */ +"For social media" = "Pour les réseaux sociaux"; + +/* chat item action */ +"Forward" = "Transférer"; + +/* alert title */ +"Forward %d message(s)?" = "Transférer %d message(s) ?"; + +/* No comment provided by engineer. */ +"Forward and save messages" = "Transférer et sauvegarder des messages"; + +/* alert action */ +"Forward messages" = "Transférer les messages"; + +/* alert message */ +"Forward messages without files?" = "Transférer les messages sans les fichiers ?"; + +/* No comment provided by engineer. */ +"Forward up to 20 messages at once." = "Transférez jusqu'à 20 messages à la fois."; + +/* No comment provided by engineer. */ +"forwarded" = "transféré"; + +/* No comment provided by engineer. */ +"Forwarded" = "Transféré"; + +/* No comment provided by engineer. */ +"Forwarded from" = "Transféré depuis"; + +/* No comment provided by engineer. */ +"Forwarding %lld messages" = "Transfert des %lld messages"; + +/* No comment provided by engineer. */ +"Forwarding server %@ failed to connect to destination server %@. Please try later." = "Le serveur de redirection %@ n'a pas réussi à se connecter au serveur de destination %@. Veuillez réessayer plus tard."; + +/* No comment provided by engineer. */ +"Forwarding server address is incompatible with network settings: %@." = "L'adresse du serveur de redirection est incompatible avec les paramètres du réseau : %@."; + +/* No comment provided by engineer. */ +"Forwarding server version is incompatible with network settings: %@." = "La version du serveur de redirection est incompatible avec les paramètres du réseau : %@."; + +/* snd error text */ +"Forwarding server: %@\nDestination server error: %@" = "Serveur de transfert : %1$@\nErreur du serveur de destination : %2$@"; + +/* snd error text */ +"Forwarding server: %@\nError: %@" = "Serveur de transfert : %1$@\nErreur : %2$@"; + /* No comment provided by engineer. */ "Found desktop" = "Bureau trouvé"; @@ -1791,9 +2544,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Nom complet (optionnel)"; -/* No comment provided by engineer. */ -"Full name:" = "Nom complet :"; - /* No comment provided by engineer. */ "Fully decentralized – visible only to members." = "Entièrement décentralisé – visible que par ses membres."; @@ -1806,6 +2556,12 @@ /* No comment provided by engineer. */ "GIFs and stickers" = "GIFs et stickers"; +/* message preview */ +"Good afternoon!" = "Bonjour !"; + +/* message preview */ +"Good morning!" = "Bonjour !"; + /* No comment provided by engineer. */ "Group" = "Groupe"; @@ -1842,24 +2598,6 @@ /* No comment provided by engineer. */ "Group links" = "Liens de groupe"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "Les membres du groupe peuvent ajouter des réactions aux messages."; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "Les membres du groupe peuvent supprimer de manière irréversible les messages envoyés. (24 heures)"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "Les membres du groupe peuvent envoyer des messages directs."; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "Les membres du groupes peuvent envoyer des messages éphémères."; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "Les membres du groupe peuvent envoyer des fichiers et des médias."; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "Les membres du groupe peuvent envoyer des messages vocaux."; - /* notification */ "Group message:" = "Message du groupe :"; @@ -1921,7 +2659,10 @@ "hours" = "heures"; /* No comment provided by engineer. */ -"How it works" = "Comment ça fonctionne"; +"How it affects privacy" = "L'impact sur la vie privée"; + +/* No comment provided by engineer. */ +"How it helps privacy" = "Comment il contribue à la protection de la vie privée"; /* No comment provided by engineer. */ "How SimpleX works" = "Comment SimpleX fonctionne"; @@ -1935,6 +2676,9 @@ /* No comment provided by engineer. */ "How to use your servers" = "Comment utiliser vos serveurs"; +/* No comment provided by engineer. */ +"Hungarian interface" = "Interface en hongrois"; + /* No comment provided by engineer. */ "ICE servers (one per line)" = "Serveurs ICE (un par ligne)"; @@ -1963,7 +2707,7 @@ "Immediately" = "Immédiatement"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "Protégé du spam et des abus"; +"Immune to spam" = "Protégé du spam et des abus"; /* No comment provided by engineer. */ "Import" = "Importer"; @@ -1974,6 +2718,18 @@ /* No comment provided by engineer. */ "Import database" = "Importer la base de données"; +/* No comment provided by engineer. */ +"Import failed" = "Échec de l'importation"; + +/* No comment provided by engineer. */ +"Import theme" = "Importer un thème"; + +/* No comment provided by engineer. */ +"Importing archive" = "Importation de l'archive"; + +/* No comment provided by engineer. */ +"Improved delivery, reduced traffic usage.\nMore improvements are coming soon!" = "Amélioration de la distribution, réduction de l'utilisation du trafic.\nD'autres améliorations sont à venir !"; + /* No comment provided by engineer. */ "Improved message delivery" = "Amélioration de la transmission des messages"; @@ -1983,9 +2739,18 @@ /* No comment provided by engineer. */ "Improved server configuration" = "Configuration de serveur améliorée"; +/* No comment provided by engineer. */ +"In order to continue, chat should be stopped." = "Pour continuer, le chat doit être interrompu."; + /* No comment provided by engineer. */ "In reply to" = "En réponse à"; +/* No comment provided by engineer. */ +"In-call sounds" = "Sons d'appel"; + +/* No comment provided by engineer. */ +"inactive" = "inactif"; + /* No comment provided by engineer. */ "Incognito" = "Incognito"; @@ -2040,14 +2805,17 @@ /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Installer [SimpleX Chat pour terminal](https://github.com/simplex-chat/simplex-chat)"; +/* No comment provided by engineer. */ +"Instant" = "Instantané"; + /* No comment provided by engineer. */ "Instant push notifications will be hidden!\n" = "Les notifications push instantanées vont être cachées !\n"; /* No comment provided by engineer. */ -"Instantly" = "Instantané"; +"Interface" = "Interface"; /* No comment provided by engineer. */ -"Interface" = "Interface"; +"Interface colors" = "Couleurs d'interface"; /* invalid chat data */ "invalid chat" = "chat invalide"; @@ -2067,6 +2835,9 @@ /* No comment provided by engineer. */ "Invalid link" = "Lien invalide"; +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Confirmation de migration invalide"; + /* No comment provided by engineer. */ "Invalid name!" = "Nom invalide !"; @@ -2076,7 +2847,7 @@ /* No comment provided by engineer. */ "Invalid response" = "Réponse invalide"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "Adresse de serveur invalide !"; /* item status text */ @@ -2088,12 +2859,18 @@ /* group name */ "invitation to group %@" = "invitation au groupe %@"; +/* No comment provided by engineer. */ +"invite" = "inviter"; + /* No comment provided by engineer. */ "Invite friends" = "Inviter des amis"; /* No comment provided by engineer. */ "Invite members" = "Inviter des membres"; +/* No comment provided by engineer. */ +"Invite to chat" = "Inviter à discuter"; + /* No comment provided by engineer. */ "Invite to group" = "Inviter au groupe"; @@ -2115,6 +2892,9 @@ /* No comment provided by engineer. */ "iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "La keychain d'iOS sera utilisée pour stocker en toute sécurité la phrase secrète après le redémarrage de l'app ou la modification de la phrase secrète - il permettra de recevoir les notifications push."; +/* No comment provided by engineer. */ +"IP address" = "Adresse IP"; + /* No comment provided by engineer. */ "Irreversible message deletion" = "Suppression irréversible des messages"; @@ -2122,7 +2902,7 @@ "Irreversible message deletion is prohibited in this chat." = "La suppression irréversible de message est interdite dans ce chat."; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "La suppression irréversible de messages est interdite dans ce groupe."; +"Irreversible message deletion is prohibited." = "La suppression irréversible de messages est interdite dans ce groupe."; /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "Cela permet d'avoir plusieurs connections anonymes sans aucune données partagées entre elles sur un même profil."; @@ -2133,6 +2913,9 @@ /* No comment provided by engineer. */ "It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Cela peut arriver quand :\n1. Les messages ont expiré dans le client expéditeur après 2 jours ou sur le serveur après 30 jours.\n2. Le déchiffrement du message a échoué, car vous ou votre contact avez utilisé une ancienne sauvegarde de base de données.\n3. La connexion a été compromise."; +/* No comment provided by engineer. */ +"It protects your IP address and connections." = "Il protège votre adresse IP et vos connexions."; + /* No comment provided by engineer. */ "It seems like you are already connected via this link. If it is not the case, there was an error (%@)." = "Il semblerait que vous êtes déjà connecté via ce lien. Si ce n'est pas le cas, il y a eu une erreur (%@)."; @@ -2145,7 +2928,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "Interface en japonais"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "Rejoindre"; /* No comment provided by engineer. */ @@ -2172,13 +2955,16 @@ /* No comment provided by engineer. */ "Joining group" = "Entrain de rejoindre le groupe"; -/* No comment provided by engineer. */ +/* alert action */ "Keep" = "Conserver"; +/* No comment provided by engineer. */ +"Keep conversation" = "Garder la conversation"; + /* No comment provided by engineer. */ "Keep the app open to use it from desktop" = "Garder l'application ouverte pour l'utiliser depuis le bureau"; -/* No comment provided by engineer. */ +/* alert title */ "Keep unused invitation?" = "Conserver l'invitation inutilisée ?"; /* No comment provided by engineer. */ @@ -2196,9 +2982,15 @@ /* No comment provided by engineer. */ "Learn more" = "En savoir plus"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "Quitter"; +/* No comment provided by engineer. */ +"Leave chat" = "Quitter la discussion"; + +/* No comment provided by engineer. */ +"Leave chat?" = "Quitter la discussion ?"; + /* No comment provided by engineer. */ "Leave group" = "Quitter le groupe"; @@ -2235,9 +3027,6 @@ /* No comment provided by engineer. */ "Live messages" = "Messages dynamiques"; -/* No comment provided by engineer. */ -"Local" = "Local"; - /* No comment provided by engineer. */ "Local name" = "Nom local"; @@ -2250,24 +3039,15 @@ /* No comment provided by engineer. */ "Lock mode" = "Mode de verrouillage"; -/* No comment provided by engineer. */ -"Make a private connection" = "Établir une connexion privée"; - /* No comment provided by engineer. */ "Make one message disappear" = "Rendre un message éphémère"; /* No comment provided by engineer. */ "Make profile private!" = "Rendre un profil privé !"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Assurez-vous que les adresses des serveurs %@ sont au bon format et ne sont pas dupliquées, un par ligne (%@)."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Assurez-vous que les adresses des serveurs WebRTC ICE sont au bon format et ne sont pas dupliquées, un par ligne."; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Beaucoup se demandent : *si SimpleX n'a pas d'identifiant d'utilisateur, comment peut-il délivrer des messages ?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "Marquer comme supprimé pour tout le monde"; @@ -2286,6 +3066,12 @@ /* No comment provided by engineer. */ "Max 30 seconds, received instantly." = "Max 30 secondes, réception immédiate."; +/* No comment provided by engineer. */ +"Media & file servers" = "Serveurs de fichiers et de médias"; + +/* blur media */ +"Medium" = "Modéré"; + /* member role */ "member" = "membre"; @@ -2298,24 +3084,72 @@ /* rcv group event chat item */ "member connected" = "est connecté·e"; +/* item status text */ +"Member inactive" = "Membre inactif"; + +/* No comment provided by engineer. */ +"Member role will be changed to \"%@\". All chat members will be notified." = "Le rôle du membre sera modifié pour « %@ ». Tous les membres du chat seront notifiés."; + /* No comment provided by engineer. */ "Member role will be changed to \"%@\". All group members will be notified." = "Le rôle du membre sera changé pour \"%@\". Tous les membres du groupe en seront informés."; /* No comment provided by engineer. */ "Member role will be changed to \"%@\". The member will receive a new invitation." = "Le rôle du membre sera changé pour \"%@\". Ce membre recevra une nouvelle invitation."; +/* No comment provided by engineer. */ +"Member will be removed from chat - this cannot be undone!" = "Le membre sera retiré de la discussion - cela ne peut pas être annulé !"; + /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "Ce membre sera retiré du groupe - impossible de revenir en arrière !"; +/* No comment provided by engineer. */ +"Members can add message reactions." = "Les membres du groupe peuvent ajouter des réactions aux messages."; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "Les membres du groupe peuvent supprimer de manière irréversible les messages envoyés. (24 heures)"; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "Les membres du groupe peuvent envoyer des messages directs."; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "Les membres du groupes peuvent envoyer des messages éphémères."; + +/* No comment provided by engineer. */ +"Members can send files and media." = "Les membres du groupe peuvent envoyer des fichiers et des médias."; + +/* No comment provided by engineer. */ +"Members can send SimpleX links." = "Les membres du groupe peuvent envoyer des liens SimpleX."; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "Les membres du groupe peuvent envoyer des messages vocaux."; + +/* No comment provided by engineer. */ +"Menus" = "Menus"; + +/* No comment provided by engineer. */ +"message" = "message"; + /* item status text */ "Message delivery error" = "Erreur de distribution du message"; /* No comment provided by engineer. */ "Message delivery receipts!" = "Accusés de réception des messages !"; +/* item status text */ +"Message delivery warning" = "Avertissement sur la distribution des messages"; + /* No comment provided by engineer. */ "Message draft" = "Brouillon de message"; +/* item status text */ +"Message forwarded" = "Message transféré"; + +/* item status description */ +"Message may be delivered later if member becomes active." = "Le message peut être transmis plus tard si le membre devient actif."; + +/* No comment provided by engineer. */ +"Message queue info" = "Informations sur la file d'attente des messages"; + /* chat feature */ "Message reactions" = "Réactions aux messages"; @@ -2323,14 +3157,35 @@ "Message reactions are prohibited in this chat." = "Les réactions aux messages sont interdites dans ce chat."; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "Les réactions aux messages sont interdites dans ce groupe."; +"Message reactions are prohibited." = "Les réactions aux messages sont interdites dans ce groupe."; /* notification */ "message received" = "message reçu"; +/* No comment provided by engineer. */ +"Message reception" = "Réception de message"; + +/* No comment provided by engineer. */ +"Message servers" = "Serveurs de messages"; + +/* No comment provided by engineer. */ +"Message shape" = "Forme du message"; + +/* No comment provided by engineer. */ +"Message source remains private." = "La source du message reste privée."; + +/* No comment provided by engineer. */ +"Message status" = "Statut du message"; + +/* copied message info */ +"Message status: %@" = "Statut du message : %@"; + /* No comment provided by engineer. */ "Message text" = "Texte du message"; +/* No comment provided by engineer. */ +"Message too large" = "Message trop volumineux"; + /* No comment provided by engineer. */ "Messages" = "Messages"; @@ -2340,9 +3195,45 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "Les messages de %@ seront affichés !"; +/* No comment provided by engineer. */ +"Messages received" = "Messages reçus"; + +/* No comment provided by engineer. */ +"Messages sent" = "Messages envoyés"; + +/* alert message */ +"Messages were deleted after you selected them." = "Les messages ont été supprimés après avoir été sélectionnés."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Les messages, fichiers et appels sont protégés par un chiffrement **de bout en bout** avec une confidentialité persistante, une répudiation et une récupération en cas d'effraction."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Les messages, fichiers et appels sont protégés par un chiffrement **e2e résistant post-quantique** avec une confidentialité persistante, une répudiation et une récupération en cas d'effraction."; + +/* No comment provided by engineer. */ +"Migrate device" = "Transférer l'appareil"; + +/* No comment provided by engineer. */ +"Migrate from another device" = "Transférer depuis un autre appareil"; + +/* No comment provided by engineer. */ +"Migrate here" = "Transférer ici"; + +/* No comment provided by engineer. */ +"Migrate to another device" = "Transférer vers un autre appareil"; + +/* No comment provided by engineer. */ +"Migrate to another device via QR code." = "Transférer vers un autre appareil via un code QR."; + +/* No comment provided by engineer. */ +"Migrating" = "Transfert"; + /* No comment provided by engineer. */ "Migrating database archive…" = "Migration de l'archive de la base de données…"; +/* No comment provided by engineer. */ +"Migration complete" = "Transfert terminé"; + /* No comment provided by engineer. */ "Migration error:" = "Erreur de migration :"; @@ -2353,7 +3244,7 @@ "Migration is completed" = "La migration est terminée"; /* No comment provided by engineer. */ -"Migrations: %@" = "Migrations : %@"; +"Migrations:" = "Migrations :"; /* time unit */ "minutes" = "minutes"; @@ -2382,57 +3273,81 @@ /* No comment provided by engineer. */ "More improvements are coming soon!" = "Plus d'améliorations à venir !"; +/* No comment provided by engineer. */ +"More reliable network connection." = "Connexion réseau plus fiable."; + +/* No comment provided by engineer. */ +"More reliable notifications" = "Notifications plus fiables"; + /* item status description */ "Most likely this connection is deleted." = "Connexion probablement supprimée."; -/* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "Il est fort probable que ce contact ait supprimé la connexion avec vous."; - /* No comment provided by engineer. */ "Multiple chat profiles" = "Différents profils de chat"; -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "Muet"; /* No comment provided by engineer. */ "Muted when inactive!" = "Mute en cas d'inactivité !"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "Nom"; /* No comment provided by engineer. */ "Network & servers" = "Réseau et serveurs"; +/* No comment provided by engineer. */ +"Network connection" = "Connexion au réseau"; + +/* No comment provided by engineer. */ +"Network decentralization" = "Décentralisation du réseau"; + +/* snd error text */ +"Network issues - message expired after many attempts to send it." = "Problèmes de réseau - le message a expiré après plusieurs tentatives d'envoi."; + +/* No comment provided by engineer. */ +"Network management" = "Gestion du réseau"; + +/* No comment provided by engineer. */ +"Network operator" = "Opérateur de réseau"; + /* No comment provided by engineer. */ "Network settings" = "Paramètres réseau"; /* No comment provided by engineer. */ "Network status" = "État du réseau"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "jamais"; /* No comment provided by engineer. */ "New chat" = "Nouveau chat"; +/* No comment provided by engineer. */ +"New chat experience 🎉" = "Nouvelle expérience de discussion 🎉"; + /* notification */ "New contact request" = "Nouvelle demande de contact"; /* notification */ "New contact:" = "Nouveau contact :"; -/* No comment provided by engineer. */ -"New database archive" = "Nouvelle archive de base de données"; - /* No comment provided by engineer. */ "New desktop app!" = "Nouvelle application de bureau !"; /* No comment provided by engineer. */ "New display name" = "Nouveau nom d'affichage"; +/* notification */ +"New events" = "Nouveaux événements"; + /* No comment provided by engineer. */ "New in %@" = "Nouveautés de la %@"; +/* No comment provided by engineer. */ +"New media options" = "Nouvelles options de médias"; + /* No comment provided by engineer. */ "New member role" = "Nouveau rôle"; @@ -2448,6 +3363,15 @@ /* No comment provided by engineer. */ "New passphrase…" = "Nouvelle phrase secrète…"; +/* No comment provided by engineer. */ +"New server" = "Nouveau serveur"; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used every time you start the app." = "De nouveaux identifiants SOCKS seront utilisés chaque fois que vous démarrerez l'application."; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used for each server." = "De nouveaux identifiants SOCKS seront utilisées pour chaque serveur."; + /* pref value */ "no" = "non"; @@ -2469,6 +3393,9 @@ /* No comment provided by engineer. */ "No device token!" = "Pas de token d'appareil !"; +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Pas de connexion directe pour l'instant, le message est transmis par l'administrateur."; + /* No comment provided by engineer. */ "no e2e encryption" = "sans chiffrement de bout en bout"; @@ -2481,24 +3408,69 @@ /* No comment provided by engineer. */ "No history" = "Aucun historique"; +/* No comment provided by engineer. */ +"No info, try to reload" = "Pas d'info, essayez de recharger"; + +/* servers error */ +"No media & file servers." = "Pas de serveurs de médias et de fichiers."; + +/* servers error */ +"No message servers." = "Pas de serveurs de messages."; + +/* No comment provided by engineer. */ +"No network connection" = "Pas de connexion au réseau"; + +/* No comment provided by engineer. */ +"No permission to record speech" = "Enregistrement des conversations non autorisé"; + +/* No comment provided by engineer. */ +"No permission to record video" = "Enregistrement de la vidéo non autorisé"; + /* No comment provided by engineer. */ "No permission to record voice message" = "Pas l'autorisation d'enregistrer un message vocal"; +/* No comment provided by engineer. */ +"No push server" = "No push server"; + /* No comment provided by engineer. */ "No received or sent files" = "Aucun fichier reçu ou envoyé"; +/* servers error */ +"No servers for private message routing." = "Pas de serveurs pour le routage privé des messages."; + +/* servers error */ +"No servers to receive files." = "Pas de serveurs pour recevoir des fichiers."; + +/* servers error */ +"No servers to receive messages." = "Pas de serveurs pour recevoir des messages."; + +/* servers error */ +"No servers to send files." = "Pas de serveurs pour envoyer des fichiers."; + /* copied message info in history */ "no text" = "aucun texte"; +/* No comment provided by engineer. */ +"No user identifiers." = "Aucun identifiant d'utilisateur."; + /* No comment provided by engineer. */ "Not compatible!" = "Non compatible !"; +/* No comment provided by engineer. */ +"Nothing selected" = "Aucune sélection"; + +/* alert title */ +"Nothing to forward!" = "Rien à transférer !"; + /* No comment provided by engineer. */ "Notifications" = "Notifications"; /* No comment provided by engineer. */ "Notifications are disabled!" = "Les notifications sont désactivées !"; +/* No comment provided by engineer. */ +"Notifications privacy" = "Notifications sécurisées"; + /* No comment provided by engineer. */ "Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Désormais, les administrateurs peuvent :\n- supprimer les messages des membres.\n- désactiver des membres (rôle \"observateur\")"; @@ -2506,11 +3478,11 @@ "observer" = "observateur"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "off"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Off"; /* feature offered item */ @@ -2519,7 +3491,7 @@ /* feature offered item */ "offered %@: %@" = "propose %1$@ : %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ @@ -2528,9 +3500,6 @@ /* No comment provided by engineer. */ "Old database" = "Ancienne base de données"; -/* No comment provided by engineer. */ -"Old database archive" = "Archives de l'ancienne base de données"; - /* group pref value */ "on" = "on"; @@ -2538,16 +3507,22 @@ "One-time invitation link" = "Lien d'invitation unique"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "Les hôtes .onion seront nécessaires pour la connexion. Nécessite l'activation d'un VPN."; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "Les hôtes .onion seront **nécessaires** pour la connexion.\nNécessite l'activation d'un VPN."; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "Les hôtes .onion seront utilisés dès que possible. Nécessite l'activation d'un VPN."; +"Onion hosts will be used when available.\nRequires compatible VPN." = "Les hôtes .onion seront utilisés dès que possible.\nNécessite l'activation d'un VPN."; /* No comment provided by engineer. */ "Onion hosts will not be used." = "Les hôtes .onion ne seront pas utilisés."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Seuls les appareils clients stockent les profils des utilisateurs, les contacts, les groupes et les messages envoyés avec un **chiffrement de bout en bout à deux couches**."; +"Only chat owners can change preferences." = "Seuls les propriétaires peuvent modifier les préférences."; + +/* No comment provided by engineer. */ +"Only client devices store user profiles, contacts, groups, and messages." = "Seuls les appareils clients stockent les profils des utilisateurs, les contacts, les groupes et les messages envoyés avec un **chiffrement de bout en bout à deux couches**."; + +/* No comment provided by engineer. */ +"Only delete conversation" = "Ne supprimer que la conversation"; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Seuls les propriétaires du groupe peuvent modifier les préférences du groupe."; @@ -2588,39 +3563,75 @@ /* No comment provided by engineer. */ "Only your contact can send voice messages." = "Seul votre contact peut envoyer des messages vocaux."; -/* No comment provided by engineer. */ +/* alert action */ "Open" = "Ouvrir"; +/* No comment provided by engineer. */ +"Open changes" = "Ouvrir les modifications"; + /* No comment provided by engineer. */ "Open chat" = "Ouvrir le chat"; /* authentication reason */ "Open chat console" = "Ouvrir la console du chat"; +/* No comment provided by engineer. */ +"Open conditions" = "Ouvrir les conditions"; + /* No comment provided by engineer. */ "Open group" = "Ouvrir le groupe"; +/* authentication reason */ +"Open migration to another device" = "Ouvrir le transfert vers un autre appareil"; + /* No comment provided by engineer. */ "Open Settings" = "Ouvrir les Paramètres"; -/* authentication reason */ -"Open user profiles" = "Ouvrir les profils d'utilisateurs"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "Protocole et code open-source – n'importe qui peut heberger un serveur."; - /* No comment provided by engineer. */ "Opening app…" = "Ouverture de l'app…"; +/* No comment provided by engineer. */ +"Operator" = "Opérateur"; + +/* alert title */ +"Operator server" = "Serveur de l'opérateur"; + +/* No comment provided by engineer. */ +"Or import archive file" = "Ou importer un fichier d'archive"; + +/* No comment provided by engineer. */ +"Or paste archive link" = "Ou coller le lien de l'archive"; + /* No comment provided by engineer. */ "Or scan QR code" = "Ou scanner le code QR"; /* No comment provided by engineer. */ -"Or show this code" = "Ou présenter ce code"; +"Or securely share this file link" = "Ou partagez en toute sécurité le lien de ce fichier"; + +/* No comment provided by engineer. */ +"Or show this code" = "Ou montrez ce code"; + +/* No comment provided by engineer. */ +"Or to share privately" = "Ou à partager en privé"; + +/* No comment provided by engineer. */ +"other" = "autre"; + +/* No comment provided by engineer. */ +"Other" = "Autres"; + +/* No comment provided by engineer. */ +"other errors" = "autres erreurs"; + +/* alert message */ +"Other file errors:\n%@" = "Autres erreurs de fichiers :\n%@"; /* member role */ "owner" = "propriétaire"; +/* feature role */ +"owners" = "propriétaires"; + /* No comment provided by engineer. */ "Passcode" = "Code d'accès"; @@ -2636,6 +3647,9 @@ /* No comment provided by engineer. */ "Passcode set!" = "Code d'accès défini !"; +/* No comment provided by engineer. */ +"Password" = "Mot de passe"; + /* No comment provided by engineer. */ "Password to show" = "Mot de passe à entrer"; @@ -2658,23 +3672,35 @@ "peer-to-peer" = "pair-à-pair"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "On ne peut se connecter à vous qu’avec les liens que vous partagez."; +"Pending" = "En attente"; /* No comment provided by engineer. */ -"Periodically" = "Périodique"; +"Periodic" = "Périodique"; /* message decrypt error item */ "Permanent decryption error" = "Erreur de déchiffrement"; +/* No comment provided by engineer. */ +"Picture-in-picture calls" = "Appels picture-in-picture"; + /* No comment provided by engineer. */ "PING count" = "Nombre de PING"; /* No comment provided by engineer. */ "PING interval" = "Intervalle de PING"; +/* No comment provided by engineer. */ +"Play from the chat list." = "Aperçu depuis la liste de conversation."; + +/* No comment provided by engineer. */ +"Please ask your contact to enable calls." = "Veuillez demander à votre contact d'autoriser les appels."; + /* No comment provided by engineer. */ "Please ask your contact to enable sending voice messages." = "Veuillez demander à votre contact de permettre l'envoi de messages vocaux."; +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Veuillez vérifier que le téléphone portable et l'ordinateur de bureau sont connectés au même réseau local et que le pare-feu de l'ordinateur de bureau autorise la connexion.\nVeuillez faire part de tout autre problème aux développeurs."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Veuillez vérifier que vous avez utilisé le bon lien ou demandez à votre contact de vous en envoyer un autre."; @@ -2684,6 +3710,9 @@ /* No comment provided by engineer. */ "Please check yours and your contact preferences." = "Veuillez vérifier vos préférences ainsi que celles de votre contact."; +/* No comment provided by engineer. */ +"Please confirm that network settings are correct for this device." = "Veuillez confirmer que les paramètres réseau de cet appareil sont corrects."; + /* No comment provided by engineer. */ "Please contact developers.\nError: %@" = "Veuillez contacter les développeurs.\nErreur : %@"; @@ -2714,6 +3743,9 @@ /* No comment provided by engineer. */ "Polish interface" = "Interface en polonais"; +/* No comment provided by engineer. */ +"Port" = "Port"; + /* server test error */ "Possibly, certificate fingerprint in server address is incorrect" = "Il est possible que l'empreinte du certificat dans l'adresse du serveur soit incorrecte"; @@ -2721,26 +3753,44 @@ "Preserve the last message draft, with attachments." = "Conserver le brouillon du dernier message, avec les pièces jointes."; /* No comment provided by engineer. */ -"Preset server" = "Serveur prédéfini"; +"Preset server address" = "Adresse du serveur prédéfinie"; /* No comment provided by engineer. */ -"Preset server address" = "Adresse du serveur prédéfinie"; +"Preset servers" = "Serveurs prédéfinis"; /* No comment provided by engineer. */ "Preview" = "Aperçu"; +/* No comment provided by engineer. */ +"Previously connected servers" = "Serveurs précédemment connectés"; + /* No comment provided by engineer. */ "Privacy & security" = "Vie privée et sécurité"; +/* No comment provided by engineer. */ +"Privacy for your customers." = "Respect de la vie privée de vos clients."; + /* No comment provided by engineer. */ "Privacy redefined" = "La vie privée redéfinie"; /* No comment provided by engineer. */ "Private filenames" = "Noms de fichiers privés"; +/* No comment provided by engineer. */ +"Private message routing" = "Routage privé des messages"; + +/* No comment provided by engineer. */ +"Private message routing 🚀" = "Routage privé des messages 🚀"; + /* name of notes to self */ "Private notes" = "Notes privées"; +/* No comment provided by engineer. */ +"Private routing" = "Routage privé"; + +/* No comment provided by engineer. */ +"Private routing error" = "Erreur de routage privé"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profil et connexions au serveur"; @@ -2748,15 +3798,15 @@ "Profile image" = "Image de profil"; /* No comment provided by engineer. */ -"Profile name" = "Nom du profil"; - -/* No comment provided by engineer. */ -"Profile name:" = "Nom du profil :"; +"Profile images" = "Images de profil"; /* No comment provided by engineer. */ "Profile password" = "Mot de passe de profil"; /* No comment provided by engineer. */ +"Profile theme" = "Thème de profil"; + +/* alert message */ "Profile update will be sent to your contacts." = "La mise à jour du profil sera envoyée à vos contacts."; /* No comment provided by engineer. */ @@ -2780,41 +3830,71 @@ /* No comment provided by engineer. */ "Prohibit sending files and media." = "Interdire l'envoi de fichiers et de médias."; +/* No comment provided by engineer. */ +"Prohibit sending SimpleX links." = "Interdire l'envoi de liens SimpleX."; + /* No comment provided by engineer. */ "Prohibit sending voice messages." = "Interdire l'envoi de messages vocaux."; /* No comment provided by engineer. */ "Protect app screen" = "Protéger l'écran de l'app"; +/* No comment provided by engineer. */ +"Protect IP address" = "Protéger l'adresse IP"; + /* No comment provided by engineer. */ "Protect your chat profiles with a password!" = "Protégez vos profils de chat par un mot de passe !"; +/* No comment provided by engineer. */ +"Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." = "Protégez votre adresse IP des relais de messagerie choisis par vos contacts.\nActivez-le dans les paramètres *Réseau et serveurs*."; + /* No comment provided by engineer. */ "Protocol timeout" = "Délai du protocole"; /* No comment provided by engineer. */ "Protocol timeout per KB" = "Délai d'attente du protocole par KB"; +/* No comment provided by engineer. */ +"Proxied" = "Routé via un proxy"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Serveurs routés via des proxy"; + +/* No comment provided by engineer. */ +"Proxy requires password" = "Le proxy est protégé par un mot de passe"; + /* No comment provided by engineer. */ "Push notifications" = "Notifications push"; +/* No comment provided by engineer. */ +"Push server" = "Serveur Push"; + +/* chat item text */ +"quantum resistant e2e encryption" = "chiffrement e2e résistant post-quantique"; + +/* No comment provided by engineer. */ +"Quantum resistant encryption" = "Chiffrement résistant post-quantique"; + /* No comment provided by engineer. */ "Rate the app" = "Évaluer l'app"; +/* No comment provided by engineer. */ +"Reachable chat toolbar" = "Barre d'outils accessible"; + /* chat item menu */ "React…" = "Réagissez…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "Lire"; /* No comment provided by engineer. */ "Read more" = "En savoir plus"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Pour en savoir plus, consultez le [Guide de l'utilisateur](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Pour en savoir plus, consultez le [Guide de l'utilisateur](https ://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Pour en savoir plus, consultez le [Guide de l'utilisateur](https ://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "Pour en savoir plus, consultez le [Guide de l'utilisateur](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)."; /* No comment provided by engineer. */ "Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Pour en savoir plus, consultez le [Guide de l'utilisateur](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; @@ -2823,10 +3903,10 @@ "Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Pour en savoir plus, consultez notre [dépôt GitHub](https://github.com/simplex-chat/simplex-chat#readme)."; /* No comment provided by engineer. */ -"Read more in our GitHub repository." = "Plus d'informations sur notre GitHub."; +"Receipts are disabled" = "Les accusés de réception sont désactivés"; /* No comment provided by engineer. */ -"Receipts are disabled" = "Les accusés de réception sont désactivés"; +"Receive errors" = "Erreurs reçues"; /* No comment provided by engineer. */ "received answer…" = "réponse reçu…"; @@ -2846,6 +3926,15 @@ /* message info title */ "Received message" = "Message reçu"; +/* No comment provided by engineer. */ +"Received messages" = "Messages reçus"; + +/* No comment provided by engineer. */ +"Received reply" = "Réponse reçue"; + +/* No comment provided by engineer. */ +"Received total" = "Total reçu"; + /* No comment provided by engineer. */ "Receiving address will be changed to a different server. Address change will complete after sender comes online." = "L'adresse de réception sera changée pour un autre serveur. Le changement d'adresse sera terminé lorsque l'expéditeur sera en ligne."; @@ -2858,14 +3947,32 @@ /* No comment provided by engineer. */ "Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)." = "Historique récent et amélioration du [bot annuaire](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)."; +/* No comment provided by engineer. */ +"Recipient(s) can't see who this message is from." = "Le(s) destinataire(s) ne peut(vent) pas voir de qui provient ce message."; + /* No comment provided by engineer. */ "Recipients see updates as you type them." = "Les destinataires voient les mises à jour au fur et à mesure que vous leur écrivez."; +/* No comment provided by engineer. */ +"Reconnect" = "Reconnecter"; + /* No comment provided by engineer. */ "Reconnect all connected servers to force message delivery. It uses additional traffic." = "Reconnecter tous les serveurs connectés pour forcer la livraison des messages. Cette méthode utilise du trafic supplémentaire."; /* No comment provided by engineer. */ -"Reconnect servers?" = "Reconnecter les serveurs?"; +"Reconnect all servers" = "Reconnecter tous les serveurs"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "Reconnecter tous les serveurs ?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "Reconnecter le serveur pour forcer la livraison des messages. Utilise du trafic supplémentaire."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "Reconnecter le serveur ?"; + +/* No comment provided by engineer. */ +"Reconnect servers?" = "Reconnecter les serveurs ?"; /* No comment provided by engineer. */ "Record updated at" = "Enregistrement mis à jour le"; @@ -2876,7 +3983,8 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "Réduction de la consommation de batterie"; -/* reject incoming call via notification */ +/* reject incoming call via notification +swipe action */ "Reject" = "Rejeter"; /* No comment provided by engineer. */ @@ -2897,6 +4005,12 @@ /* No comment provided by engineer. */ "Remove" = "Supprimer"; +/* No comment provided by engineer. */ +"Remove archive?" = "Supprimer l'archive ?"; + +/* No comment provided by engineer. */ +"Remove image" = "Enlever l'image"; + /* No comment provided by engineer. */ "Remove member" = "Retirer le membre"; @@ -2928,29 +4042,56 @@ "Renegotiate encryption" = "Renégocier le chiffrement"; /* No comment provided by engineer. */ -"Renegotiate encryption?" = "Renégocier le chiffrement?"; +"Renegotiate encryption?" = "Renégocier le chiffrement ?"; /* No comment provided by engineer. */ "Repeat connection request?" = "Répéter la demande de connexion ?"; +/* No comment provided by engineer. */ +"Repeat download" = "Répéter le téléchargement"; + +/* No comment provided by engineer. */ +"Repeat import" = "Répéter l'importation"; + /* No comment provided by engineer. */ "Repeat join request?" = "Répéter la requête d'adhésion ?"; +/* No comment provided by engineer. */ +"Repeat upload" = "Répéter l'envoi"; + /* chat item action */ "Reply" = "Répondre"; +/* chat list item title */ +"requested to connect" = "demande à se connecter"; + /* No comment provided by engineer. */ "Required" = "Requis"; /* No comment provided by engineer. */ "Reset" = "Réinitialisation"; +/* No comment provided by engineer. */ +"Reset all hints" = "Rétablir tous les conseils"; + +/* No comment provided by engineer. */ +"Reset all statistics" = "Réinitialiser toutes les statistiques"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "Réinitialiser toutes les statistiques ?"; + /* No comment provided by engineer. */ "Reset colors" = "Réinitialisation des couleurs"; +/* No comment provided by engineer. */ +"Reset to app theme" = "Réinitialisation au thème de l'appli"; + /* No comment provided by engineer. */ "Reset to defaults" = "Réinitialisation des valeurs par défaut"; +/* No comment provided by engineer. */ +"Reset to user theme" = "Réinitialisation au thème de l'utilisateur"; + /* No comment provided by engineer. */ "Restart the app to create a new chat profile" = "Redémarrez l'application pour créer un nouveau profil de chat"; @@ -2976,7 +4117,7 @@ "Reveal" = "Révéler"; /* No comment provided by engineer. */ -"Revert" = "Revenir en arrière"; +"Review conditions" = "Vérifier les conditions"; /* No comment provided by engineer. */ "Revoke" = "Révoquer"; @@ -2993,27 +4134,31 @@ /* No comment provided by engineer. */ "Run chat" = "Exécuter le chat"; -/* chat item action */ +/* No comment provided by engineer. */ +"Safely receive files" = "Réception de fichiers en toute sécurité"; + +/* No comment provided by engineer. */ +"Safer groups" = "Groupes plus sûrs"; + +/* alert button +chat item action */ "Save" = "Enregistrer"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Enregistrer (et en informer les contacts)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "Enregistrer et en informer le contact"; /* No comment provided by engineer. */ "Save and notify group members" = "Enregistrer et en informer les membres du groupe"; +/* No comment provided by engineer. */ +"Save and reconnect" = "Sauvegarder et se reconnecter"; + /* No comment provided by engineer. */ "Save and update group profile" = "Enregistrer et mettre à jour le profil du groupe"; -/* No comment provided by engineer. */ -"Save archive" = "Enregistrer l'archive"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Enregistrer les paramètres de validation automatique"; - /* No comment provided by engineer. */ "Save group profile" = "Enregistrer le profil du groupe"; @@ -3023,7 +4168,7 @@ /* No comment provided by engineer. */ "Save passphrase in Keychain" = "Enregistrer la phrase secrète dans la Keychain"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "Enregistrer les préférences ?"; /* No comment provided by engineer. */ @@ -3032,14 +4177,26 @@ /* No comment provided by engineer. */ "Save servers" = "Enregistrer les serveurs"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "Enregistrer les serveurs ?"; /* No comment provided by engineer. */ -"Save settings?" = "Enregistrer les paramètres ?"; +"Save welcome message?" = "Enregistrer le message d'accueil ?"; + +/* alert title */ +"Save your profile?" = "Sauvegarder votre profil ?"; /* No comment provided by engineer. */ -"Save welcome message?" = "Enregistrer le message d'accueil ?"; +"saved" = "enregistré"; + +/* No comment provided by engineer. */ +"Saved" = "Enregistré"; + +/* No comment provided by engineer. */ +"Saved from" = "Enregistré depuis"; + +/* No comment provided by engineer. */ +"saved from %@" = "enregistré à partir de %@"; /* message info title */ "Saved message" = "Message enregistré"; @@ -3047,6 +4204,15 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Les serveurs WebRTC ICE sauvegardés seront supprimés"; +/* No comment provided by engineer. */ +"Saving %lld messages" = "Sauvegarde de %lld messages"; + +/* No comment provided by engineer. */ +"Scale" = "Échelle"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Scanner / Coller un lien"; + /* No comment provided by engineer. */ "Scan code" = "Scanner le code"; @@ -3062,6 +4228,9 @@ /* No comment provided by engineer. */ "Scan server QR code" = "Scanner un code QR de serveur"; +/* No comment provided by engineer. */ +"search" = "rechercher"; + /* No comment provided by engineer. */ "Search" = "Rechercher"; @@ -3074,6 +4243,9 @@ /* network option */ "sec" = "sec"; +/* No comment provided by engineer. */ +"Secondary" = "Secondaire"; + /* time unit */ "seconds" = "secondes"; @@ -3083,6 +4255,9 @@ /* server test step */ "Secure queue" = "File d'attente sécurisée"; +/* No comment provided by engineer. */ +"Secured" = "Sécurisées"; + /* No comment provided by engineer. */ "Security assessment" = "Évaluation de sécurité"; @@ -3092,9 +4267,18 @@ /* chat item text */ "security code changed" = "code de sécurité modifié"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "Choisir"; +/* No comment provided by engineer. */ +"Select chat profile" = "Sélectionner un profil de discussion"; + +/* No comment provided by engineer. */ +"Selected %lld" = "%lld sélectionné(s)"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Les préférences de chat sélectionnées interdisent ce message."; + /* No comment provided by engineer. */ "Self-destruct" = "Autodestruction"; @@ -3119,9 +4303,6 @@ /* No comment provided by engineer. */ "send direct message" = "envoyer un message direct"; -/* No comment provided by engineer. */ -"Send direct message" = "Envoyer un message direct"; - /* No comment provided by engineer. */ "Send direct message to connect" = "Envoyer un message direct pour vous connecter"; @@ -3129,16 +4310,25 @@ "Send disappearing message" = "Envoyer un message éphémère"; /* No comment provided by engineer. */ -"Send link previews" = "Envoi d'aperçus de liens"; +"Send errors" = "Erreurs d'envoi"; + +/* No comment provided by engineer. */ +"Send link previews" = "Aperçu des liens"; /* No comment provided by engineer. */ "Send live message" = "Envoyer un message dynamique"; /* No comment provided by engineer. */ -"Send notifications" = "Envoi de notifications"; +"Send message to enable calls." = "Envoyer un message pour activer les appels."; /* No comment provided by engineer. */ -"Send notifications:" = "Envoi de notifications :"; +"Send messages directly when IP address is protected and your or destination server does not support private routing." = "Envoyer les messages de manière directe lorsque l'adresse IP est protégée et que votre serveur ou le serveur de destination ne prend pas en charge le routage privé."; + +/* No comment provided by engineer. */ +"Send messages directly when your or destination server does not support private routing." = "Envoyez les messages de manière directe lorsque votre serveur ou le serveur de destination ne prend pas en charge le routage privé."; + +/* No comment provided by engineer. */ +"Send notifications" = "Envoi de notifications"; /* No comment provided by engineer. */ "Send questions and ideas" = "Envoyez vos questions et idées"; @@ -3152,7 +4342,7 @@ /* No comment provided by engineer. */ "Send up to 100 last messages to new members." = "Envoi des 100 derniers messages aux nouveaux membres."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "L'expéditeur a annulé le transfert de fichiers."; /* No comment provided by engineer. */ @@ -3188,27 +4378,84 @@ /* copied message info */ "Sent at: %@" = "Envoyé le : %@"; +/* No comment provided by engineer. */ +"Sent directly" = "Envoyé directement"; + /* notification */ "Sent file event" = "Événement de fichier envoyé"; /* message info title */ "Sent message" = "Message envoyé"; +/* No comment provided by engineer. */ +"Sent messages" = "Messages envoyés"; + /* No comment provided by engineer. */ "Sent messages will be deleted after set time." = "Les messages envoyés seront supprimés après une durée déterminée."; +/* No comment provided by engineer. */ +"Sent reply" = "Réponse envoyée"; + +/* No comment provided by engineer. */ +"Sent total" = "Total envoyé"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Envoyé via le proxy"; + +/* No comment provided by engineer. */ +"Server" = "Serveur"; + +/* alert message */ +"Server added to operator %@." = "Serveur ajouté à l'opérateur %@."; + +/* No comment provided by engineer. */ +"Server address" = "Adresse du serveur"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "L'adresse du serveur est incompatible avec les paramètres réseau : %@."; + +/* srv error text. */ +"Server address is incompatible with network settings." = "L'adresse du serveur est incompatible avec les paramètres du réseau."; + +/* alert title */ +"Server operator changed." = "L'opérateur du serveur a changé."; + +/* No comment provided by engineer. */ +"Server operators" = "Opérateurs de serveur"; + +/* alert title */ +"Server protocol changed." = "Le protocole du serveur a été modifié."; + +/* queue info */ +"server queue info: %@\n\nlast received msg: %@" = "info sur la file d'attente du serveur : %1$@\n\ndernier message reçu : %2$@"; + /* server test error */ "Server requires authorization to create queues, check password" = "Le serveur requiert une autorisation pour créer des files d'attente, vérifiez le mot de passe"; /* server test error */ -"Server requires authorization to upload, check password" = "Le serveur requiert une autorisation pour uploader, vérifiez le mot de passe"; +"Server requires authorization to upload, check password" = "Le serveur requiert une autorisation pour téléverser, vérifiez le mot de passe"; /* No comment provided by engineer. */ "Server test failed!" = "Échec du test du serveur !"; +/* No comment provided by engineer. */ +"Server type" = "Type de serveur"; + +/* srv error text */ +"Server version is incompatible with network settings." = "La version du serveur est incompatible avec les paramètres du réseau."; + +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "La version du serveur est incompatible avec votre appli : %@."; + /* No comment provided by engineer. */ "Servers" = "Serveurs"; +/* No comment provided by engineer. */ +"Servers info" = "Infos serveurs"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "Les statistiques des serveurs seront réinitialisées - il n'est pas possible de revenir en arrière !"; + /* No comment provided by engineer. */ "Session code" = "Code de session"; @@ -3218,6 +4465,9 @@ /* No comment provided by engineer. */ "Set contact name…" = "Définir le nom du contact…"; +/* No comment provided by engineer. */ +"Set default theme" = "Définir le thème par défaut"; + /* No comment provided by engineer. */ "Set group preferences" = "Définir les préférences du groupe"; @@ -3233,6 +4483,9 @@ /* No comment provided by engineer. */ "Set passcode" = "Définir le code d'accès"; +/* No comment provided by engineer. */ +"Set passphrase" = "Définir une phrase secrète"; + /* No comment provided by engineer. */ "Set passphrase to export" = "Définir la phrase secrète pour l'export"; @@ -3245,27 +4498,55 @@ /* No comment provided by engineer. */ "Settings" = "Paramètres"; -/* chat item action */ +/* alert message */ +"Settings were changed." = "Les paramètres ont été modifiés."; + +/* No comment provided by engineer. */ +"Shape profile images" = "Images de profil modelable"; + +/* alert action +chat item action */ "Share" = "Partager"; /* No comment provided by engineer. */ "Share 1-time link" = "Partager un lien unique"; +/* No comment provided by engineer. */ +"Share 1-time link with a friend" = "Partager un lien unique avec un ami"; + /* No comment provided by engineer. */ "Share address" = "Partager l'adresse"; /* No comment provided by engineer. */ +"Share address publicly" = "Partager publiquement votre adresse"; + +/* alert title */ "Share address with contacts?" = "Partager l'adresse avec vos contacts ?"; +/* No comment provided by engineer. */ +"Share from other apps." = "Partager depuis d'autres applications."; + /* No comment provided by engineer. */ "Share link" = "Partager le lien"; /* No comment provided by engineer. */ -"Share this 1-time invite link" = "Partager ce lien d'invitation unique"; +"Share profile" = "Partager le profil"; + +/* No comment provided by engineer. */ +"Share SimpleX address on social media." = "Partagez votre adresse SimpleX sur les réseaux sociaux."; + +/* No comment provided by engineer. */ +"Share this 1-time invite link" = "Partagez ce lien d'invitation unique"; + +/* No comment provided by engineer. */ +"Share to SimpleX" = "Partager sur SimpleX"; /* No comment provided by engineer. */ "Share with contacts" = "Partager avec vos contacts"; +/* No comment provided by engineer. */ +"Show → on messages sent via private routing." = "Afficher → sur les messages envoyés via le routage privé."; + /* No comment provided by engineer. */ "Show calls in phone history" = "Afficher les appels dans l'historique du téléphone"; @@ -3273,20 +4554,41 @@ "Show developer options" = "Afficher les options pour les développeurs"; /* No comment provided by engineer. */ -"Show last messages" = "Voir les derniers messages"; +"Show last messages" = "Aperçu des derniers messages"; /* No comment provided by engineer. */ -"Show preview" = "Afficher l'aperçu"; +"Show message status" = "Afficher le statut du message"; + +/* No comment provided by engineer. */ +"Show percentage" = "Afficher le pourcentage"; + +/* No comment provided by engineer. */ +"Show preview" = "Aperçu affiché"; + +/* No comment provided by engineer. */ +"Show QR code" = "Afficher le code QR"; /* No comment provided by engineer. */ "Show:" = "Afficher :"; +/* No comment provided by engineer. */ +"SimpleX" = "SimpleX"; + /* No comment provided by engineer. */ "SimpleX address" = "Adresse SimpleX"; /* No comment provided by engineer. */ "SimpleX Address" = "Adresse SimpleX"; +/* No comment provided by engineer. */ +"SimpleX address and 1-time links are safe to share via any messenger." = "Les adresses SimpleX et les liens à usage unique peuvent être partagés en toute sécurité via n'importe quelle messagerie."; + +/* No comment provided by engineer. */ +"SimpleX address or 1-time link?" = "Adresse SimpleX ou lien unique ?"; + +/* No comment provided by engineer. */ +"SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app." = "SimpleX Chat et Flux ont conclu un accord pour inclure les serveurs exploités par Flux dans l'application."; + /* No comment provided by engineer. */ "SimpleX Chat security was audited by Trail of Bits." = "La sécurité de SimpleX Chat a été auditée par Trail of Bits."; @@ -3299,9 +4601,15 @@ /* simplex link type */ "SimpleX group link" = "Lien de groupe SimpleX"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "Liens SimpleX"; +/* No comment provided by engineer. */ +"SimpleX links are prohibited." = "Les liens SimpleX sont interdits dans ce groupe."; + +/* No comment provided by engineer. */ +"SimpleX links not allowed" = "Les liens SimpleX ne sont pas autorisés"; + /* No comment provided by engineer. */ "SimpleX Lock" = "SimpleX Lock"; @@ -3317,9 +4625,15 @@ /* simplex link type */ "SimpleX one-time invitation" = "Invitation unique SimpleX"; +/* No comment provided by engineer. */ +"SimpleX protocols reviewed by Trail of Bits." = "Protocoles SimpleX audité par Trail of Bits."; + /* No comment provided by engineer. */ "Simplified incognito mode" = "Mode incognito simplifié"; +/* No comment provided by engineer. */ +"Size" = "Taille"; + /* No comment provided by engineer. */ "Skip" = "Passer"; @@ -3330,14 +4644,38 @@ "Small groups (max 20)" = "Petits groupes (max 20)"; /* No comment provided by engineer. */ -"SMP servers" = "Serveurs SMP"; +"SMP server" = "Serveur SMP"; + +/* No comment provided by engineer. */ +"SOCKS proxy" = "proxy SOCKS"; + +/* blur media */ +"Soft" = "Léger"; + +/* No comment provided by engineer. */ +"Some app settings were not migrated." = "Certains paramètres de l'application n'ont pas été migrés."; + +/* No comment provided by engineer. */ +"Some file(s) were not exported:" = "Certains fichiers n'ont pas été exportés :"; /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "Des erreurs non fatales se sont produites lors de l'importation - vous pouvez consulter la console de chat pour plus de détails."; +/* No comment provided by engineer. */ +"Some non-fatal errors occurred during import:" = "L'importation a entraîné des erreurs non fatales :"; + +/* alert message */ +"Some servers failed the test:\n%@" = "Certains serveurs ont échoué le test :\n%@"; + /* notification title */ "Somebody" = "Quelqu'un"; +/* No comment provided by engineer. */ +"Square, circle, or anything in between." = "Carré, circulaire, ou toute autre forme intermédiaire."; + +/* chat item text */ +"standard end-to-end encryption" = "chiffrement de bout en bout standard"; + /* No comment provided by engineer. */ "Start chat" = "Démarrer le chat"; @@ -3347,14 +4685,20 @@ /* No comment provided by engineer. */ "Start migration" = "Démarrer la migration"; +/* No comment provided by engineer. */ +"Starting from %@." = "À partir de %@."; + /* No comment provided by engineer. */ "starting…" = "lancement…"; +/* No comment provided by engineer. */ +"Statistics" = "Statistiques"; + /* No comment provided by engineer. */ "Stop" = "Arrêter"; /* No comment provided by engineer. */ -"Stop chat to enable database actions" = "Arrêter le chat pour permettre des actions sur la base de données"; +"Stop chat" = "Arrêter le chat"; /* No comment provided by engineer. */ "Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Arrêtez le chat pour exporter, importer ou supprimer la base de données du chat. Vous ne pourrez pas recevoir et envoyer de messages pendant que le chat est arrêté."; @@ -3371,36 +4715,63 @@ /* No comment provided by engineer. */ "Stop sending file?" = "Arrêter l'envoi du fichier ?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "Cesser le partage"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "Cesser le partage d'adresse ?"; /* authentication reason */ "Stop SimpleX" = "Arrêter SimpleX"; +/* No comment provided by engineer. */ +"Stopping chat" = "Arrêt du chat"; + /* No comment provided by engineer. */ "strike" = "barré"; +/* blur media */ +"Strong" = "Fort"; + /* No comment provided by engineer. */ "Submit" = "Soumettre"; +/* No comment provided by engineer. */ +"Subscribed" = "Inscriptions"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Erreurs d'inscription"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Inscriptions ignorées"; + /* No comment provided by engineer. */ "Support SimpleX Chat" = "Supporter SimpleX Chat"; +/* No comment provided by engineer. */ +"Switch audio and video during the call." = "Passer de l'audio à la vidéo pendant l'appel."; + +/* No comment provided by engineer. */ +"Switch chat profile for 1-time invitations." = "Changer de profil de chat pour les invitations à usage unique."; + /* No comment provided by engineer. */ "System" = "Système"; /* No comment provided by engineer. */ "System authentication" = "Authentification du système"; +/* No comment provided by engineer. */ +"Tail" = "Queue"; + /* No comment provided by engineer. */ "Take picture" = "Prendre une photo"; /* No comment provided by engineer. */ "Tap button " = "Appuyez sur le bouton "; +/* No comment provided by engineer. */ +"Tap Create SimpleX address in the menu to create it later." = "Appuyez sur Créer une adresse SimpleX dans le menu pour la créer ultérieurement."; + /* No comment provided by engineer. */ "Tap to activate profile." = "Appuyez pour activer un profil."; @@ -3420,7 +4791,7 @@ "Tap to scan" = "Appuyez pour scanner"; /* No comment provided by engineer. */ -"Tap to start a new chat" = "Appuyez ici pour démarrer une nouvelle discussion"; +"TCP connection" = "Connexion TCP"; /* No comment provided by engineer. */ "TCP connection timeout" = "Délai de connexion TCP"; @@ -3434,6 +4805,9 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* file error alert title */ +"Temporary file error" = "Erreur de fichier temporaire"; + /* server test failure */ "Test failed at step %@." = "Échec du test à l'étape %@."; @@ -3443,7 +4817,7 @@ /* No comment provided by engineer. */ "Test servers" = "Tester les serveurs"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "Échec des tests !"; /* No comment provided by engineer. */ @@ -3456,10 +4830,13 @@ "Thanks to the users – contribute via Weblate!" = "Merci aux utilisateurs - contribuez via Weblate !"; /* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "La 1ère plateforme sans aucun identifiant d'utilisateur – privée par design."; +"The app can notify you when you receive messages or contact requests - please open settings to enable." = "L'application peut vous avertir lorsque vous recevez des messages ou des demandes de contact - veuillez ouvrir les paramètres pour les activer."; /* No comment provided by engineer. */ -"The app can notify you when you receive messages or contact requests - please open settings to enable." = "L'application peut vous avertir lorsque vous recevez des messages ou des demandes de contact - veuillez ouvrir les paramètres pour les activer."; +"The app protects your privacy by using different operators in each conversation." = "L'application protège votre vie privée en utilisant des opérateurs différents pour chaque conversation."; + +/* No comment provided by engineer. */ +"The app will ask to confirm downloads from unknown file servers (except .onion)." = "L'application demandera de confirmer les téléchargements à partir de serveurs de fichiers inconnus (sauf .onion)."; /* No comment provided by engineer. */ "The attempt to change database passphrase was not completed." = "La tentative de modification de la phrase secrète de la base de données n'a pas abouti."; @@ -3467,6 +4844,9 @@ /* No comment provided by engineer. */ "The code you scanned is not a SimpleX link QR code." = "Le code scanné n'est pas un code QR de lien SimpleX."; +/* No comment provided by engineer. */ +"The connection reached the limit of undelivered messages, your contact may be offline." = "La connexion a atteint la limite des messages non délivrés, votre contact est peut-être hors ligne."; + /* No comment provided by engineer. */ "The connection you accepted will be cancelled!" = "La connexion que vous avez acceptée sera annulée !"; @@ -3479,6 +4859,9 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "Le chiffrement fonctionne et le nouvel accord de chiffrement n'est pas nécessaire. Cela peut provoquer des erreurs de connexion !"; +/* No comment provided by engineer. */ +"The future of messaging" = "La nouvelle génération de messagerie privée"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "Le hash du message précédent est différent."; @@ -3492,13 +4875,22 @@ "The message will be marked as moderated for all members." = "Le message sera marqué comme modéré pour tous les membres."; /* No comment provided by engineer. */ -"The next generation of private messaging" = "La nouvelle génération de messagerie privée"; +"The messages will be deleted for all members." = "Les messages seront supprimés pour tous les membres."; + +/* No comment provided by engineer. */ +"The messages will be marked as moderated for all members." = "Les messages seront marqués comme modérés pour tous les membres."; /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "L'ancienne base de données n'a pas été supprimée lors de la migration, elle peut être supprimée."; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "Le profil n'est partagé qu'avec vos contacts."; +"Your profile is stored on your device and only shared with your contacts." = "Le profil n'est partagé qu'avec vos contacts."; + +/* No comment provided by engineer. */ +"The same conditions will apply to operator **%@**." = "Les mêmes conditions s'appliquent à l'opérateur **%@**."; + +/* No comment provided by engineer. */ +"The second preset operator in the app!" = "Le deuxième opérateur prédéfini de l'application !"; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "Le deuxième coche que nous avons manqué ! ✅"; @@ -3509,11 +4901,20 @@ /* No comment provided by engineer. */ "The servers for new connections of your current chat profile **%@**." = "Les serveurs pour les nouvelles connexions de votre profil de chat actuel **%@**."; +/* No comment provided by engineer. */ +"The servers for new files of your current chat profile **%@**." = "Les serveurs pour les nouveaux fichiers de votre profil de chat actuel **%@**."; + /* No comment provided by engineer. */ "The text you pasted is not a SimpleX link." = "Le texte collé n'est pas un lien SimpleX."; /* No comment provided by engineer. */ -"Theme" = "Thème"; +"The uploaded database archive will be permanently removed from the servers." = "L'archive de la base de données envoyée sera définitivement supprimée des serveurs."; + +/* No comment provided by engineer. */ +"Themes" = "Thèmes"; + +/* No comment provided by engineer. */ +"These conditions will also apply for: **%@**." = "Ces conditions s'appliquent également aux : **%@**."; /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Ces paramètres s'appliquent à votre profil actuel **%@**."; @@ -3530,6 +4931,12 @@ /* No comment provided by engineer. */ "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Cette action ne peut être annulée - votre profil, vos contacts, vos messages et vos fichiers seront irréversiblement perdus."; +/* E2EE info chat item */ +"This chat is protected by end-to-end encryption." = "Cette discussion est protégée par un chiffrement de bout en bout."; + +/* E2EE info chat item */ +"This chat is protected by quantum resistant end-to-end encryption." = "Cette discussion est protégée par un chiffrement de bout en bout résistant aux technologies quantiques."; + /* notification title */ "this contact" = "ce contact"; @@ -3551,14 +4958,20 @@ /* No comment provided by engineer. */ "This is your own SimpleX address!" = "Voici votre propre adresse SimpleX !"; +/* No comment provided by engineer. */ +"This link was used with another mobile device, please create a new link on the desktop." = "Ce lien a été utilisé avec un autre appareil mobile, veuillez créer un nouveau lien sur le bureau."; + /* No comment provided by engineer. */ "This setting applies to messages in your current chat profile **%@**." = "Ce paramètre s'applique aux messages de votre profil de chat actuel **%@**."; +/* No comment provided by engineer. */ +"Title" = "Titre"; + /* No comment provided by engineer. */ "To ask any questions and to receive updates:" = "Si vous avez des questions et que vous souhaitez des réponses :"; /* No comment provided by engineer. */ -"To connect, your contact can scan QR code or use the link in the app." = "Pour se connecter, votre contact peut scanner le code QR ou utiliser le lien dans l'application."; +"To connect, your contact can scan QR code or use the link in the app." = "Pour se connecter, votre contact peut scanner un code QR ou utiliser un lien dans l'app."; /* No comment provided by engineer. */ "To hide unwanted messages." = "Pour cacher les messages indésirables."; @@ -3567,7 +4980,7 @@ "To make a new connection" = "Pour établir une nouvelle connexion"; /* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Pour protéger votre vie privée, au lieu d’IDs utilisés par toutes les autres plateformes, SimpleX a des IDs pour les queues de messages, distinctes pour chacun de vos contacts."; +"To protect against your link being replaced, you can compare contact security codes." = "Pour vous protéger contre le remplacement de votre lien, vous pouvez comparer les codes de sécurité des contacts."; /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "Pour préserver le fuseau horaire, les fichiers image/voix utilisent le système UTC."; @@ -3575,24 +4988,57 @@ /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Pour protéger vos informations, activez la fonction SimpleX Lock.\nVous serez invité à confirmer l'authentification avant que cette fonction ne soit activée."; +/* No comment provided by engineer. */ +"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Pour protéger votre adresse IP, le routage privé utilise vos serveurs SMP pour délivrer les messages."; + +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "Pour protéger votre vie privée, au lieu d’IDs utilisés par toutes les autres plateformes, SimpleX a des IDs pour les queues de messages, distinctes pour chacun de vos contacts."; + +/* No comment provided by engineer. */ +"To receive" = "Pour recevoir"; + +/* No comment provided by engineer. */ +"To record speech please grant permission to use Microphone." = "Si vous souhaitez enregistrer une conversation, veuillez autoriser l'utilisation du microphone."; + +/* No comment provided by engineer. */ +"To record video please grant permission to use Camera." = "Si vous souhaitez enregistrer une vidéo, veuillez autoriser l'utilisation de la caméra."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Pour enregistrer un message vocal, veuillez accorder la permission d'utiliser le microphone."; /* No comment provided by engineer. */ "To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Pour révéler votre profil caché, entrez le mot de passe dans le champ de recherche de la page **Vos profils de chat**."; +/* No comment provided by engineer. */ +"To send" = "Pour envoyer"; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Pour prendre en charge les notifications push instantanées, la base de données du chat doit être migrée."; +/* No comment provided by engineer. */ +"To use the servers of **%@**, accept conditions of use." = "Pour utiliser les serveurs de **%@**, acceptez les conditions d'utilisation."; + /* No comment provided by engineer. */ "To verify end-to-end encryption with your contact compare (or scan) the code on your devices." = "Pour vérifier le chiffrement de bout en bout avec votre contact, comparez (ou scannez) le code sur vos appareils."; +/* No comment provided by engineer. */ +"Toggle chat list:" = "Afficher la liste des conversations :"; + /* No comment provided by engineer. */ "Toggle incognito when connecting." = "Basculer en mode incognito lors de la connexion."; +/* No comment provided by engineer. */ +"Toolbar opacity" = "Opacité de la barre d'outils"; + +/* No comment provided by engineer. */ +"Total" = "Total"; + /* No comment provided by engineer. */ "Transport isolation" = "Transport isolé"; +/* No comment provided by engineer. */ +"Transport sessions" = "Sessions de transport"; + /* No comment provided by engineer. */ "Trying to connect to the server used to receive messages from this contact (error: %@)." = "Tentative de connexion au serveur utilisé pour recevoir les messages de ce contact (erreur : %@)."; @@ -3629,13 +5075,13 @@ /* rcv group event chat item */ "unblocked %@" = "%@ débloqué"; -/* item status description */ -"Unexpected error: %@" = "Erreur inattendue : %@"; +/* No comment provided by engineer. */ +"Undelivered messages" = "Messages non distribués"; /* No comment provided by engineer. */ "Unexpected migration state" = "État de la migration inattendu"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "Unfav."; /* No comment provided by engineer. */ @@ -3662,6 +5108,12 @@ /* No comment provided by engineer. */ "Unknown error" = "Erreur inconnue"; +/* No comment provided by engineer. */ +"unknown servers" = "relais inconnus"; + +/* alert title */ +"Unknown servers!" = "Serveurs inconnus !"; + /* No comment provided by engineer. */ "unknown status" = "statut inconnu"; @@ -3683,10 +5135,13 @@ /* authentication reason */ "Unlock app" = "Déverrouiller l'app"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "Démute"; /* No comment provided by engineer. */ +"unprotected" = "non protégé"; + +/* swipe action */ "Unread" = "Non lu"; /* No comment provided by engineer. */ @@ -3695,9 +5150,6 @@ /* No comment provided by engineer. */ "Update" = "Mise à jour"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = "Mettre à jour le paramètre des hôtes .onion ?"; - /* No comment provided by engineer. */ "Update database passphrase" = "Mise à jour de la phrase secrète de la base de données"; @@ -3705,7 +5157,7 @@ "Update network settings?" = "Mettre à jour les paramètres réseau ?"; /* No comment provided by engineer. */ -"Update transport isolation mode?" = "Mettre à jour le mode d'isolement du transport ?"; +"Update settings?" = "Mettre à jour les paramètres ?"; /* rcv group event chat item */ "updated group profile" = "mise à jour du profil de groupe"; @@ -3716,24 +5168,45 @@ /* No comment provided by engineer. */ "Updating settings will re-connect the client to all servers." = "La mise à jour des ces paramètres reconnectera le client à tous les serveurs."; -/* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "La mise à jour de ce paramètre reconnectera le client à tous les serveurs."; - /* No comment provided by engineer. */ "Upgrade and open chat" = "Mettre à niveau et ouvrir le chat"; +/* No comment provided by engineer. */ +"Upload errors" = "Erreurs de téléversement"; + +/* No comment provided by engineer. */ +"Upload failed" = "Échec de l'envoi"; + /* server test step */ -"Upload file" = "Transférer le fichier"; +"Upload file" = "Téléverser le fichier"; + +/* No comment provided by engineer. */ +"Uploaded" = "Téléversé"; + +/* No comment provided by engineer. */ +"Uploaded files" = "Fichiers téléversés"; + +/* No comment provided by engineer. */ +"Uploading archive" = "Envoi de l'archive"; /* No comment provided by engineer. */ "Use .onion hosts" = "Utiliser les hôtes .onions"; +/* No comment provided by engineer. */ +"Use %@" = "Utiliser %@"; + /* No comment provided by engineer. */ "Use chat" = "Utiliser le chat"; /* No comment provided by engineer. */ "Use current profile" = "Utiliser le profil actuel"; +/* No comment provided by engineer. */ +"Use for files" = "Utiliser pour les fichiers"; + +/* No comment provided by engineer. */ +"Use for messages" = "Utiliser pour les messages"; + /* No comment provided by engineer. */ "Use for new connections" = "Utiliser pour les nouvelles connexions"; @@ -3749,17 +5222,35 @@ /* No comment provided by engineer. */ "Use only local notifications?" = "Utilisation de notifications locales uniquement ?"; +/* No comment provided by engineer. */ +"Use private routing with unknown servers when IP address is not protected." = "Utiliser le routage privé avec des serveurs inconnus lorsque l'adresse IP n'est pas protégée."; + +/* No comment provided by engineer. */ +"Use private routing with unknown servers." = "Utiliser le routage privé avec des serveurs inconnus."; + /* No comment provided by engineer. */ "Use server" = "Utiliser ce serveur"; +/* No comment provided by engineer. */ +"Use servers" = "Utiliser les serveurs"; + /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Utiliser les serveurs SimpleX Chat ?"; /* No comment provided by engineer. */ -"User profile" = "Profil d'utilisateur"; +"Use SOCKS proxy" = "Utiliser un proxy SOCKS"; /* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = "L'utilisation des hôtes .onion nécessite un fournisseur VPN compatible."; +"Use the app while in the call." = "Utiliser l'application pendant l'appel."; + +/* No comment provided by engineer. */ +"Use the app with one hand." = "Utiliser l'application d'une main."; + +/* No comment provided by engineer. */ +"User selection" = "Sélection de l'utilisateur"; + +/* No comment provided by engineer. */ +"Username" = "Nom d'utilisateur"; /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Vous utilisez les serveurs SimpleX."; @@ -3782,6 +5273,12 @@ /* No comment provided by engineer. */ "Verify connections" = "Vérifier les connexions"; +/* No comment provided by engineer. */ +"Verify database passphrase" = "Vérifier la phrase secrète de la base de données"; + +/* No comment provided by engineer. */ +"Verify passphrase" = "Vérifier la phrase secrète"; + /* No comment provided by engineer. */ "Verify security code" = "Vérifier le code de sécurité"; @@ -3803,6 +5300,9 @@ /* No comment provided by engineer. */ "Via secure quantum resistant protocol." = "Via un protocole sécurisé de cryptographie post-quantique."; +/* No comment provided by engineer. */ +"video" = "vidéo"; + /* No comment provided by engineer. */ "Video call" = "Appel vidéo"; @@ -3810,7 +5310,7 @@ "video call (not e2e encrypted)" = "appel vidéo (sans chiffrement)"; /* No comment provided by engineer. */ -"Video will be received when your contact completes uploading it." = "La vidéo ne sera reçue que lorsque votre contact aura fini de la transférer."; +"Video will be received when your contact completes uploading it." = "La vidéo ne sera reçue que lorsque votre contact aura fini la mettre en ligne."; /* No comment provided by engineer. */ "Video will be received when your contact is online, please wait or check later!" = "La vidéo ne sera reçue que lorsque votre contact sera en ligne. Veuillez patienter ou vérifier plus tard !"; @@ -3818,9 +5318,15 @@ /* No comment provided by engineer. */ "Videos and files up to 1gb" = "Vidéos et fichiers jusqu'à 1Go"; +/* No comment provided by engineer. */ +"View conditions" = "Voir les conditions"; + /* No comment provided by engineer. */ "View security code" = "Afficher le code de sécurité"; +/* No comment provided by engineer. */ +"View updated conditions" = "Voir les conditions mises à jour"; + /* chat feature */ "Visible history" = "Historique visible"; @@ -3834,7 +5340,10 @@ "Voice messages are prohibited in this chat." = "Les messages vocaux sont interdits dans ce chat."; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "Les messages vocaux sont interdits dans ce groupe."; +"Voice messages are prohibited." = "Les messages vocaux sont interdits dans ce groupe."; + +/* No comment provided by engineer. */ +"Voice messages not allowed" = "Les messages vocaux ne sont pas autorisés"; /* No comment provided by engineer. */ "Voice messages prohibited!" = "Messages vocaux interdits !"; @@ -3857,9 +5366,18 @@ /* No comment provided by engineer. */ "Waiting for video" = "En attente de la vidéo"; +/* No comment provided by engineer. */ +"Wallpaper accent" = "Accentuation du papier-peint"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Fond d'écran"; + /* No comment provided by engineer. */ "wants to connect to you!" = "veut établir une connexion !"; +/* No comment provided by engineer. */ +"Warning: starting chat on multiple devices is not supported and will cause message delivery failures" = "Attention : démarrer une session de chat sur plusieurs appareils n'est pas pris en charge et entraînera des dysfonctionnements au niveau de la transmission des messages"; + /* No comment provided by engineer. */ "Warning: you may lose some data!" = "Attention : vous risquez de perdre des données !"; @@ -3875,6 +5393,9 @@ /* No comment provided by engineer. */ "Welcome message" = "Message de bienvenue"; +/* No comment provided by engineer. */ +"Welcome message is too long" = "Le message de bienvenue est trop long"; + /* No comment provided by engineer. */ "What's new" = "Quoi de neuf ?"; @@ -3882,11 +5403,26 @@ "When available" = "Quand disponible"; /* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "Vous pouvez accepter ou refuser les demandes de contacts."; +"When connecting audio and video calls." = "Lors des appels audio et vidéo."; + +/* No comment provided by engineer. */ +"when IP hidden" = "lorsque l'IP est masquée"; + +/* No comment provided by engineer. */ +"When more than one operator is enabled, none of them has metadata to learn who communicates with whom." = "Lorsque plusieurs opérateurs sont activés, aucun d'entre eux ne dispose de métadonnées permettant de savoir qui communique avec qui."; /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Lorsque vous partagez un profil incognito avec quelqu'un, ce profil sera utilisé pour les groupes auxquels il vous invite."; +/* No comment provided by engineer. */ +"WiFi" = "WiFi"; + +/* No comment provided by engineer. */ +"Will be enabled in direct chats!" = "Activé dans les discussions directes !"; + +/* No comment provided by engineer. */ +"Wired ethernet" = "Ethernet câblé"; + /* No comment provided by engineer. */ "With encrypted files and media." = "Avec les fichiers et les médias chiffrés."; @@ -3896,20 +5432,35 @@ /* No comment provided by engineer. */ "With reduced battery usage." = "Consommation réduite de la batterie."; +/* No comment provided by engineer. */ +"Without Tor or VPN, your IP address will be visible to file servers." = "Sans Tor ou un VPN, votre adresse IP sera visible par les serveurs de fichiers."; + +/* alert message */ +"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Sans Tor ni VPN, votre adresse IP sera visible par ces relais XFTP : %@."; + /* No comment provided by engineer. */ "Wrong database passphrase" = "Mauvaise phrase secrète pour la base de données"; +/* snd error text */ +"Wrong key or unknown connection - most likely this connection is deleted." = "Clé erronée ou connexion non identifiée - il est très probable que cette connexion soit supprimée."; + +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Mauvaise clé ou adresse inconnue du bloc de données du fichier - le fichier est probablement supprimé."; + /* No comment provided by engineer. */ "Wrong passphrase!" = "Mauvaise phrase secrète !"; /* No comment provided by engineer. */ -"XFTP servers" = "Serveurs XFTP"; +"XFTP server" = "Serveur XFTP"; /* pref value */ "yes" = "oui"; /* No comment provided by engineer. */ -"You" = "Vous"; +"you" = "vous"; + +/* No comment provided by engineer. */ +"You **must not** use the same database on two devices." = "Vous **ne devez pas** utiliser la même base de données sur deux appareils."; /* No comment provided by engineer. */ "You accepted connection" = "Vous avez accepté la connexion"; @@ -3923,6 +5474,9 @@ /* No comment provided by engineer. */ "You are already connected to %@." = "Vous êtes déjà connecté·e à %@ via ce lien."; +/* No comment provided by engineer. */ +"You are already connected with %@." = "Vous êtes déjà connecté avec %@."; + /* No comment provided by engineer. */ "You are already connecting to %@." = "Vous êtes déjà en train de vous connecter à %@."; @@ -3953,6 +5507,9 @@ /* No comment provided by engineer. */ "You are invited to group" = "Vous êtes invité·e au groupe"; +/* No comment provided by engineer. */ +"You are not connected to these servers. Private routing is used to deliver messages to them." = "Vous n'êtes pas connecté à ces serveurs. Le routage privé est utilisé pour leur délivrer des messages."; + /* No comment provided by engineer. */ "you are observer" = "vous êtes observateur"; @@ -3962,6 +5519,12 @@ /* No comment provided by engineer. */ "You can accept calls from lock screen, without device and app authentication." = "Vous pouvez accepter des appels à partir de l'écran de verrouillage, sans authentification de l'appareil ou de l'application."; +/* No comment provided by engineer. */ +"You can change it in Appearance settings." = "Vous pouvez choisir de le modifier dans les paramètres d'apparence."; + +/* No comment provided by engineer. */ +"You can configure servers via settings." = "Vous pouvez configurer les serveurs via les paramètres."; + /* No comment provided by engineer. */ "You can create it later" = "Vous pouvez la créer plus tard"; @@ -3971,6 +5534,9 @@ /* No comment provided by engineer. */ "You can enable them later via app Privacy & Security settings." = "Vous pouvez les activer ultérieurement via les paramètres de Confidentialité et Sécurité de l'application."; +/* No comment provided by engineer. */ +"You can give another try." = "Vous pouvez faire un nouvel essai."; + /* No comment provided by engineer. */ "You can hide or mute a user profile - swipe it to the right." = "Vous pouvez masquer ou mettre en sourdine un profil d'utilisateur - faites-le glisser vers la droite."; @@ -3978,7 +5544,13 @@ "You can make it visible to your SimpleX contacts via Settings." = "Vous pouvez le rendre visible à vos contacts SimpleX via Paramètres."; /* notification body */ -"You can now send messages to %@" = "Vous pouvez maintenant envoyer des messages à %@"; +"You can now chat with %@" = "Vous pouvez maintenant envoyer des messages à %@"; + +/* No comment provided by engineer. */ +"You can send messages to %@ from Archived contacts." = "Vous pouvez envoyer des messages à %@ à partir des contacts archivés."; + +/* No comment provided by engineer. */ +"You can set connection name, to remember who the link was shared with." = "Vous pouvez définir un nom de connexion pour vous rappeler avec qui le lien a été partagé."; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "Vous pouvez configurer l'aperçu des notifications sur l'écran de verrouillage via les paramètres."; @@ -3990,10 +5562,10 @@ "You can share this address with your contacts to let them connect with **%@**." = "Vous pouvez partager cette adresse avec vos contacts pour leur permettre de se connecter avec **%@**."; /* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "Vous pouvez partager votre adresse sous la forme d'un lien ou d'un code QR - tout le monde peut l'utiliser pour vous contacter."; +"You can start chat via app Settings / Database or by restarting the app" = "Vous pouvez lancer le chat via Paramètres / Base de données ou en redémarrant l'app"; /* No comment provided by engineer. */ -"You can start chat via app Settings / Database or by restarting the app" = "Vous pouvez lancer le chat via Paramètres / Base de données ou en redémarrant l'app"; +"You can still view conversation with %@ in the list of chats." = "Vous pouvez toujours voir la conversation avec %@ dans la liste des discussions."; /* No comment provided by engineer. */ "You can turn on SimpleX Lock via Settings." = "Vous pouvez activer SimpleX Lock dans les Paramètres."; @@ -4001,7 +5573,7 @@ /* No comment provided by engineer. */ "You can use markdown to format messages:" = "Vous pouvez utiliser le format markdown pour mettre en forme les messages :"; -/* No comment provided by engineer. */ +/* alert message */ "You can view invitation link again in connection details." = "Vous pouvez à nouveau consulter le lien d'invitation dans les détails de la connexion."; /* No comment provided by engineer. */ @@ -4020,10 +5592,10 @@ "you changed role of %@ to %@" = "vous avez modifié le rôle de %1$@ pour %2$@"; /* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "Vous contrôlez par quel·s serveur·s vous pouvez **transmettre** ainsi que par quel·s serveur·s vous pouvez **recevoir** les messages de vos contacts."; +"You could not be verified; please try again." = "Vous n'avez pas pu être vérifié·e ; veuillez réessayer."; /* No comment provided by engineer. */ -"You could not be verified; please try again." = "Vous n'avez pas pu être vérifié·e ; veuillez réessayer."; +"You decide who can connect." = "Vous choisissez qui peut se connecter."; /* No comment provided by engineer. */ "You have already requested connection via this address!" = "Vous avez déjà demandé une connexion via cette adresse !"; @@ -4031,9 +5603,6 @@ /* No comment provided by engineer. */ "You have already requested connection!\nRepeat connection request?" = "Vous avez déjà demandé une connexion !\nRépéter la demande de connexion ?"; -/* No comment provided by engineer. */ -"You have no chats" = "Vous n'avez aucune discussion"; - /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "Vous devez saisir la phrase secrète à chaque fois que l'application démarre - elle n'est pas stockée sur l'appareil."; @@ -4049,9 +5618,18 @@ /* snd group event chat item */ "you left" = "vous avez quitté"; +/* No comment provided by engineer. */ +"You may migrate the exported database." = "Vous pouvez migrer la base de données exportée."; + +/* No comment provided by engineer. */ +"You may save the exported archive." = "Vous pouvez enregistrer l'archive exportée."; + /* No comment provided by engineer. */ "You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." = "Vous devez utiliser la version la plus récente de votre base de données de chat sur un seul appareil UNIQUEMENT, sinon vous risquez de ne plus recevoir les messages de certains contacts."; +/* No comment provided by engineer. */ +"You need to allow your contact to call to be able to call them." = "Vous devez autoriser votre contact à appeler pour pouvoir l'appeler."; + /* No comment provided by engineer. */ "You need to allow your contact to send voice messages to be able to send them." = "Vous devez autoriser votre contact à envoyer des messages vocaux pour pouvoir en envoyer."; @@ -4094,6 +5672,9 @@ /* No comment provided by engineer. */ "You will still receive calls and notifications from muted profiles when they are active." = "Vous continuerez à recevoir des appels et des notifications des profils mis en sourdine lorsqu'ils sont actifs."; +/* No comment provided by engineer. */ +"You will stop receiving messages from this chat. Chat history will be preserved." = "Vous ne recevrez plus de messages de cette discussion. L'historique sera préservé."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Vous ne recevrez plus de messages de ce groupe. L'historique du chat sera conservé."; @@ -4109,9 +5690,6 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Vous utilisez un profil incognito pour ce groupe - pour éviter de partager votre profil principal ; inviter des contacts n'est pas possible"; -/* No comment provided by engineer. */ -"Your %@ servers" = "Vos serveurs %@"; - /* No comment provided by engineer. */ "Your calls" = "Vos appels"; @@ -4121,11 +5699,14 @@ /* No comment provided by engineer. */ "Your chat database is not encrypted - set passphrase to encrypt it." = "Votre base de données de chat n'est pas chiffrée - définisez une phrase secrète."; +/* alert title */ +"Your chat preferences" = "Vos préférences de discussion"; + /* No comment provided by engineer. */ "Your chat profiles" = "Vos profils de chat"; /* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Votre contact a besoin d'être en ligne pour completer la connexion.\nVous pouvez annuler la connexion et supprimer le contact (et réessayer plus tard avec un autre lien)."; +"Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." = "Votre connexion a été déplacée vers %@ mais une erreur inattendue s'est produite lors de la redirection vers le profil."; /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "Votre contact a envoyé un fichier plus grand que la taille maximale supportée actuellement(%@)."; @@ -4136,6 +5717,9 @@ /* No comment provided by engineer. */ "Your contacts will remain connected." = "Vos contacts resteront connectés."; +/* No comment provided by engineer. */ +"Your credentials may be sent unencrypted." = "Vos informations d'identification peuvent être envoyées non chiffrées."; + /* No comment provided by engineer. */ "Your current chat database will be DELETED and REPLACED with the imported one." = "Votre base de données de chat actuelle va être SUPPRIMEE et REMPLACEE par celle importée."; @@ -4158,7 +5742,10 @@ "Your profile **%@** will be shared." = "Votre profil **%@** sera partagé."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Votre profil est stocké sur votre appareil et est seulement partagé avec vos contacts.\nLes serveurs SimpleX ne peuvent pas voir votre profil."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Votre profil est stocké sur votre appareil et est seulement partagé avec vos contacts. Les serveurs SimpleX ne peuvent pas voir votre profil."; + +/* alert message */ +"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Votre profil a été modifié. Si vous l'enregistrez, le profil mis à jour sera envoyé à tous vos contacts."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Votre profil, vos contacts et les messages reçus sont stockés sur votre appareil."; @@ -4167,10 +5754,10 @@ "Your random profile" = "Votre profil aléatoire"; /* No comment provided by engineer. */ -"Your server" = "Votre serveur"; +"Your server address" = "Votre adresse de serveur"; /* No comment provided by engineer. */ -"Your server address" = "Votre adresse de serveur"; +"Your servers" = "Vos serveurs"; /* No comment provided by engineer. */ "Your settings" = "Vos paramètres"; @@ -4178,9 +5765,3 @@ /* No comment provided by engineer. */ "Your SimpleX address" = "Votre adresse SimpleX"; -/* No comment provided by engineer. */ -"Your SMP servers" = "Vos serveurs SMP"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "Vos serveurs XFTP"; - diff --git a/apps/ios/hu.lproj/Localizable.strings b/apps/ios/hu.lproj/Localizable.strings index f9a470fbbc..5a9b6b4e38 100644 --- a/apps/ios/hu.lproj/Localizable.strings +++ b/apps/ios/hu.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (másolható)"; @@ -20,58 +5,40 @@ "_italic_" = "\\_dőlt_"; /* No comment provided by engineer. */ -"- connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)!\n- delivery receipts (up to 20 members).\n- faster and more stable." = "- kapcsolódás a [könyvtár szolgáltatáshoz] (simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2Ld3%3DWpxkKFeXSPv3pwp %2F%3Fv%3D1-2%26dh %3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6glco6bqjETA)4Beklco6bqj)\n- kézbesítési jelentések (legfeljebb 20 tag).\n- gyorsabb és stabilabb."; +"- connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)!\n- delivery receipts (up to 20 members).\n- faster and more stable." = "- kapcsolódás a [könyvtár szolgáltatáshoz](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)!\n- kézbesítési jelentések (legfeljebb 20 tag).\n- gyorsabb és stabilabb."; /* No comment provided by engineer. */ -"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- stabilabb üzenetkézbesítés.\n- valamivel jobb csoportok.\n- és még sok más!"; +"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- stabilabb üzenetkézbesítés.\n- picit továbbfejlesztett csoportok.\n- és még sok más!"; /* No comment provided by engineer. */ -"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- opcionális értesítés a törölt kapcsolatokról.\n- profilnevek szóközökkel.\n- és még sok más!"; +"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- partnerek értesítése a törlésről (nem kötelező)\n- profilnevek szóközökkel\n- és még sok más!"; /* No comment provided by engineer. */ -"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- hangüzenetek legfeljebb 5 perces időtartamig.\n- egyedi eltűnési időhatár megadása.\n- előzmények szerkesztése."; - -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; +"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- legfeljebb 5 perc hosszúságú hangüzenetek.\n- egyéni üzenet-eltűnési időkorlát.\n- előzmények szerkesztése."; /* No comment provided by engineer. */ "!1 colored!" = "!1 színezett!"; -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - /* No comment provided by engineer. */ "(new)" = "(új)"; /* No comment provided by engineer. */ -"(this device v%@)" = "(ez az eszköz v%@)"; +"(this device v%@)" = "(ez az eszköz: v%@)"; /* No comment provided by engineer. */ -")" = ")"; - -/* No comment provided by engineer. */ -"[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Hozzájárulás](https://github.com/simplex-chat/simplex-chat#contribute)"; +"[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Közreműködés](https://github.com/simplex-chat/simplex-chat#contribute)"; /* No comment provided by engineer. */ "[Send us email](mailto:chat@simplex.chat)" = "[Küldjön nekünk e-mailt](mailto:chat@simplex.chat)"; /* No comment provided by engineer. */ -"[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Csillag a GitHubon](https://github.com/simplex-chat/simplex-chat)"; +"[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Csillagozás a GitHubon](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add contact**: to create a new invitation link, or connect via a link you received." = "**Ismerős hozzáadása**: új meghívó hivatkozás létrehozásához, vagy egy kapott hivatkozáson keresztül történő csatlakozáshoz."; +"**Create 1-time link**: to create and share a new invitation link." = "**Partner hozzáadása:** új meghívási hivatkozás létrehozásához, vagy egy kapott hivatkozáson keresztül történő kapcsolódáshoz."; /* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Új ismerős hozzáadása**: egyszer használatos QR-kód vagy hivatkozás létrehozása a kapcsolattartóhoz."; - -/* No comment provided by engineer. */ -"**Create group**: to create a new group." = "**Csoport létrehozása**: új csoport létrehozásához."; +"**Create group**: to create a new group." = "**Csoport létrehozása:** új csoport létrehozásához."; /* No comment provided by engineer. */ "**e2e encrypted** audio call" = "**e2e titkosított** hanghívás"; @@ -80,19 +47,28 @@ "**e2e encrypted** video call" = "**e2e titkosított** videóhívás"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Privátabb**: 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken megosztásra kerül a SimpleX Chat kiszolgálóval, de az nem, hogy hány ismerőse vagy üzenete van."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Privátabb:** 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken meg lesz osztva a SimpleX Chat-kiszolgálóval, de az nem, hogy hány partnere vagy üzenete van."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Legprivátabb**: ne használja a SimpleX Chat értesítési szervert, rendszeresen ellenőrizze az üzeneteket a háttérben (attól függően, hogy milyen gyakran használja az alkalmazást)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**Legprivátabb:** ne használja a SimpleX Chat értesítési kiszolgálót, rendszeresen ellenőrizze az üzeneteket a háttérben (attól függően, hogy milyen gyakran használja az alkalmazást)."; /* No comment provided by engineer. */ -"**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Figyelem**: NEM tudja visszaállítani vagy megváltoztatni jelmondatát, ha elveszíti azt."; +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Megjegyzés:** ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja a partnereitől érkező üzenetek visszafejtését."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Javasolt**: az eszköztoken és az értesítések elküldésre kerülnek a SimpleX Chat értesítési szerverre, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik."; +"**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Megjegyzés:** NEM fogja tudni helyreállítani, vagy módosítani a jelmondatot abban az esetben, ha elveszíti."; /* No comment provided by engineer. */ -"**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Figyelmeztetés**: Az azonnali push-értesítésekhez a kulcstárolóban tárolt jelmondat megadása szükséges."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Megjegyzés:** az eszköztoken és az értesítések el lesznek küldve a SimpleX Chat értesítési kiszolgálóra, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik."; + +/* No comment provided by engineer. */ +"**Scan / Paste link**: to connect via a link you received." = "**Hivatkozás beolvasása / beillesztése**: egy kapott hivatkozáson keresztüli kapcsolódáshoz."; + +/* No comment provided by engineer. */ +"**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Figyelmeztetés:** Az azonnali push-értesítésekhez a kulcstartóban tárolt jelmondat megadása szükséges."; + +/* No comment provided by engineer. */ +"**Warning**: the archive will be removed." = "**Figyelmeztetés:** az archívum el lesz távolítva."; /* No comment provided by engineer. */ "*bold*" = "\\*félkövér*"; @@ -107,7 +83,7 @@ "## In reply to" = "## Válaszul erre"; /* No comment provided by engineer. */ -"#secret#" = "#titkos#"; +"#secret#" = "#titok#"; /* No comment provided by engineer. */ "%@" = "%@"; @@ -128,34 +104,46 @@ "%@ and %@" = "%@ és %@"; /* No comment provided by engineer. */ -"%@ and %@ connected" = "%@ és %@ csatlakozott"; +"%@ and %@ connected" = "%@ és %@ kapcsolódott"; /* copied message info, at

Hi!

\n

Connect to me via SimpleX Chat

" = "

Üdvözlöm!

\n

Csatlakozzon hozzám a SimpleX Chaten

"; +"

Hi!

\n

Connect to me via SimpleX Chat

" = "

Üdvözlöm!

\n

Csatlakozzon hozzám a SimpleX Chaten keresztül

"; /* No comment provided by engineer. */ "~strike~" = "\\~áthúzott~"; @@ -265,7 +268,8 @@ /* No comment provided by engineer. */ "0s" = "0s"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "1 nap"; /* time interval */ @@ -274,12 +278,23 @@ /* No comment provided by engineer. */ "1 minute" = "1 perc"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "1 hónap"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "1 hét"; +/* delete after time */ +"1 year" = "1 év"; + +/* No comment provided by engineer. */ +"1-time link" = "Egyszer használható meghívó"; + +/* No comment provided by engineer. */ +"1-time link can be used *with one contact only* - share in person or via any messenger." = "Az egyszer használható meghívó egy hivatkozás és *csak egyetlen partnerrel használható* – személyesen vagy bármilyen üzenetváltó-alkalmazáson keresztül megosztható."; + /* No comment provided by engineer. */ "5 minutes" = "5 perc"; @@ -290,236 +305,397 @@ "30 seconds" = "30 másodperc"; /* No comment provided by engineer. */ -"A few more things" = "Még néhány dolog"; +"A few more things" = "Néhány további dolog"; /* notification title */ -"A new contact" = "Egy új ismerős"; +"A new contact" = "Egy új partner"; /* No comment provided by engineer. */ -"A new random profile will be shared." = "Egy új, véletlenszerű profil kerül megosztásra."; +"A new random profile will be shared." = "Egy új, véletlenszerű profil lesz megosztva."; /* No comment provided by engineer. */ -"A separate TCP connection will be used **for each chat profile you have in the app**." = "A rendszer külön TCP-kapcsolatot fog használni **az alkalmazásban található minden csevegési profilhoz**."; +"A separate TCP connection will be used **for each chat profile you have in the app**." = "**Az összes csevegési profiljához az alkalmazásban** külön TCP-kapcsolat (és SOCKS-hitelesítőadat) lesz használva."; /* No comment provided by engineer. */ -"A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "A rendszer külön TCP-kapcsolatot fog használni **minden ismerőshöz és csoporttaghoz**.\n**Figyelem**: sok kapcsolódás esetén, az akkumulátor- és adatforgalom fogyasztás jelentősen megnőhet, és egyes kapcsolatok meghiúsulhatnak."; +"A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "**Az összes partneréhez és csoporttaghoz** külön TCP-kapcsolat (és SOCKS-hitelesítőadat) lesz használva.\n**Megjegyzés:** ha sok kapcsolata van, az akkumulátor-használat és az adatforgalom jelentősen megnövekedhet, és néhány kapcsolódási kísérlet sikertelen lehet."; /* No comment provided by engineer. */ "Abort" = "Megszakítás"; /* No comment provided by engineer. */ -"Abort changing address" = "Címváltoztatás megszakítása"; +"Abort changing address" = "Cím módosításának megszakítása"; /* No comment provided by engineer. */ -"Abort changing address?" = "Címváltoztatás megszakítása??"; +"Abort changing address?" = "Megszakítja a cím módosítását?"; /* No comment provided by engineer. */ -"About SimpleX" = "A SimpleX névjegye"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "A SimpleX azonosítóról"; +"About operators" = "Az üzemeltetőkről"; /* No comment provided by engineer. */ "About SimpleX Chat" = "A SimpleX Chat névjegye"; /* No comment provided by engineer. */ -"above, then choose:" = "fent, majd válassza ki:"; +"above, then choose:" = "gombra fent, majd válassza ki:"; /* No comment provided by engineer. */ -"Accent color" = "Kiemelő szín"; +"Accent" = "Kiemelőszín"; /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "Elfogadás"; /* No comment provided by engineer. */ -"Accept connection request?" = "Kapcsolatfelvétel elfogadása?"; +"Accept conditions" = "Feltételek elfogadása"; + +/* No comment provided by engineer. */ +"Accept connection request?" = "Elfogadja a meghívási kérést?"; /* notification body */ -"Accept contact request from %@?" = "Elfogadja %@ kapcsolat kérését?"; +"Accept contact request from %@?" = "Elfogadja %@ meghívási kérését?"; -/* accept contact request via notification */ -"Accept incognito" = "Fogadás inkognítóban"; +/* accept contact request via notification +swipe action */ +"Accept incognito" = "Elfogadás inkognitóban"; /* call status */ -"accepted call" = "elfogadott hívás"; +"accepted call" = "fogadott hívás"; /* No comment provided by engineer. */ -"Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Azonosító hozzáadása a profilhoz, hogy az ismerősök megoszthassák másokkal. A profilfrissítés elküldésre kerül ismerősők számára."; +"Accepted conditions" = "Elfogadott feltételek"; + +/* chat list item title */ +"accepted invitation" = "elfogadott meghívó"; /* No comment provided by engineer. */ -"Add contact" = "Ismerős hozzáadása"; +"Acknowledged" = "Visszaigazolt"; /* No comment provided by engineer. */ -"Add preset servers" = "Előre beállított kiszolgálók hozzáadása"; +"Acknowledgement errors" = "Visszaigazolási hibák"; + +/* token status text */ +"Active" = "Aktív"; + +/* No comment provided by engineer. */ +"Active connections" = "Aktív kapcsolatok száma"; + +/* No comment provided by engineer. */ +"Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Cím hozzáadása a profilhoz, hogy a partnerei megoszthassák másokkal. A profilfrissítés el lesz küldve partnerei számára."; + +/* No comment provided by engineer. */ +"Add friends" = "Barátok hozzáadása"; + +/* No comment provided by engineer. */ +"Add list" = "Lista hozzáadása"; /* No comment provided by engineer. */ "Add profile" = "Profil hozzáadása"; /* No comment provided by engineer. */ -"Add server…" = "Kiszolgáló hozzáadása…"; +"Add server" = "Kiszolgáló hozzáadása"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "Kiszolgáló hozzáadása QR-kód beolvasásával."; +/* No comment provided by engineer. */ +"Add team members" = "Munkatársak hozzáadása"; + /* No comment provided by engineer. */ "Add to another device" = "Hozzáadás egy másik eszközhöz"; /* No comment provided by engineer. */ -"Add welcome message" = "Üdvözlő üzenet hozzáadása"; +"Add to list" = "Hozzáadás listához"; + +/* No comment provided by engineer. */ +"Add welcome message" = "Üdvözlőüzenet hozzáadása"; + +/* No comment provided by engineer. */ +"Add your team members to the conversations." = "Adja hozzá a munkatársait a beszélgetésekhez."; + +/* No comment provided by engineer. */ +"Added media & file servers" = "Hozzáadott média- és fájlkiszolgálók"; + +/* No comment provided by engineer. */ +"Added message servers" = "Hozzáadott üzenetkiszolgálók"; + +/* No comment provided by engineer. */ +"Additional accent" = "További kiemelőszín"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "További kiemelőszín 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "További másodlagos szín"; /* No comment provided by engineer. */ "Address" = "Cím"; /* No comment provided by engineer. */ -"Address change will be aborted. Old receiving address will be used." = "A cím módosítása megszakad. A régi fogadási cím kerül felhasználásra."; - -/* member role */ -"admin" = "admin"; +"Address change will be aborted. Old receiving address will be used." = "A cím módosítása meg fog szakadni. A régi fogadási cím lesz használva."; /* No comment provided by engineer. */ -"Admins can create the links to join groups." = "Az adminok hivatkozásokat hozhatnak létre a csoportokhoz való csatlakozáshoz."; +"Address or 1-time link?" = "Cím vagy egyszer használható meghívó?"; + +/* No comment provided by engineer. */ +"Address settings" = "Címbeállítások"; + +/* member role */ +"admin" = "adminisztrátor"; + +/* feature role */ +"admins" = "adminisztrátorok"; + +/* No comment provided by engineer. */ +"Admins can block a member for all." = "Az adminisztrátorok egy tagot a csoport összes tagja számára letilthatnak."; + +/* No comment provided by engineer. */ +"Admins can create the links to join groups." = "Az adminisztrátorok hivatkozásokat hozhatnak létre a csoportokhoz való csatlakozáshoz."; /* No comment provided by engineer. */ "Advanced network settings" = "Speciális hálózati beállítások"; +/* No comment provided by engineer. */ +"Advanced settings" = "Speciális beállítások"; + /* chat item text */ -"agreeing encryption for %@…" = "titkosítás jóváhagyása %@ számára…"; +"agreeing encryption for %@…" = "titkosítás elfogadása %@ számára…"; /* chat item text */ "agreeing encryption…" = "titkosítás elfogadása…"; /* No comment provided by engineer. */ -"All app data is deleted." = "Minden alkalmazásadat törölve."; +"All" = "Összes"; /* No comment provided by engineer. */ -"All chats and messages will be deleted - this cannot be undone!" = "Minden csevegés és üzenet törlésre kerül - ez nem vonható vissza!"; +"All app data is deleted." = "Az összes alkalmazásadat törölve."; /* No comment provided by engineer. */ -"All data is erased when it is entered." = "A jelkód megadása után minden adat törlésre kerül."; +"All chats and messages will be deleted - this cannot be undone!" = "Az összes csevegés és üzenet törölve lesz – ez a művelet nem vonható vissza!"; + +/* alert message */ +"All chats will be removed from the list %@, and the list deleted." = "Az összes csevegés el lesz távolítva a(z) %@ nevű listáról, és a lista is törölve lesz."; /* No comment provided by engineer. */ -"All group members will remain connected." = "Minden csoporttag csatlakoztatva marad."; +"All data is erased when it is entered." = "A jelkód megadása után az összes adat törölve lesz."; /* No comment provided by engineer. */ -"All messages will be deleted - this cannot be undone!" = "Minden üzenet törlésre kerül – ez nem vonható vissza!"; +"All data is kept private on your device." = "Az összes adat privát módon van tárolva az eszközén."; /* No comment provided by engineer. */ -"All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Minden üzenet törlésre kerül - ezt nem vonható vissza! Az üzenetek CSAK az ön számára törlődnek."; +"All group members will remain connected." = "Az összes csoporttag kapcsolatban marad."; + +/* feature role */ +"all members" = "összes tag"; /* No comment provided by engineer. */ -"All new messages from %@ will be hidden!" = "Minden új üzenet elrejtésre kerül tőle: %@!"; +"All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages." = "Az összes üzenet és fájl **végpontok közötti titkosítással**, a közvetlen üzenetek továbbá kvantumbiztos titkosítással is rendelkeznek."; /* No comment provided by engineer. */ -"All your contacts will remain connected." = "Minden ismerős csatlakoztatva marad."; +"All messages will be deleted - this cannot be undone!" = "Az összes üzenet törölve lesz – ez a művelet nem vonható vissza!"; /* No comment provided by engineer. */ -"All your contacts will remain connected. Profile update will be sent to your contacts." = "Ismerőseivel kapcsolatban marad. A profil változtatások frissítésre kerülnek az ismerősöknél."; +"All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Az összes üzenet törölve lesz – ez a művelet nem vonható vissza! Az üzenetek CSAK az Ön számára törlődnek."; + +/* No comment provided by engineer. */ +"All new messages from %@ will be hidden!" = "%@ összes új üzenete el lesz rejtve!"; + +/* profile dropdown */ +"All profiles" = "Összes profil"; + +/* No comment provided by engineer. */ +"All reports will be archived for you." = "Az összes jelentés archiválva lesz az Ön számára."; + +/* No comment provided by engineer. */ +"All servers" = "Összes kiszolgáló"; + +/* No comment provided by engineer. */ +"All your contacts will remain connected." = "Az összes partnerével kapcsolatban marad."; + +/* No comment provided by engineer. */ +"All your contacts will remain connected. Profile update will be sent to your contacts." = "A partnereivel kapcsolatban marad. A profilfrissítés el lesz küldve a partnerei számára."; + +/* No comment provided by engineer. */ +"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Az összes partnere, -beszélgetése és -fájlja biztonságosan titkosítva lesz, majd töredékekre bontva feltöltődnek a beállított XFTP-továbbítókiszolgálókra."; /* No comment provided by engineer. */ "Allow" = "Engedélyezés"; /* No comment provided by engineer. */ -"Allow calls only if your contact allows them." = "Hívások engedélyezése kizárólag abban az esetben, ha ismerőse is engedélyezi."; +"Allow calls only if your contact allows them." = "A hívások kezdeményezése csak abban az esetben van engedélyezve, ha a partnere is engedélyezi."; /* No comment provided by engineer. */ -"Allow disappearing messages only if your contact allows it to you." = "Eltűnő üzenetek engedélyezése kizárólag abban az esetben, ha ismerőse is engedélyezi az ön számára."; +"Allow calls?" = "Engedélyezi a hívásokat?"; /* No comment provided by engineer. */ -"Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Üzenet végleges törlésének engedélyezése kizárólag abban az esetben, ha ismerőse is engedélyezi. (24 óra)"; +"Allow disappearing messages only if your contact allows it to you." = "Az eltűnő üzenetek küldése csak abban az esetben van engedélyezve, ha a partnere is engedélyezi az Ön számára."; /* No comment provided by engineer. */ -"Allow message reactions only if your contact allows them." = "Üzenetreakciók engedélyezése kizárólag abban az esetben, ha ismerőse is engedélyezi."; +"Allow downgrade" = "Visszafejlesztés engedélyezése"; /* No comment provided by engineer. */ -"Allow message reactions." = "Üzenetreakciók engedélyezése."; +"Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Az üzenetek végleges törlése csak abban az esetben van engedélyezve, ha a partnere is engedélyezi. (24 óra)"; /* No comment provided by engineer. */ -"Allow sending direct messages to members." = "Közvetlen üzenetek küldésének engedélyezése tagok részére."; +"Allow message reactions only if your contact allows them." = "A reakciók hozzáadása az üzenetekhez csak abban az esetben van engedélyezve, ha a partnere is engedélyezi."; /* No comment provided by engineer. */ -"Allow sending disappearing messages." = "Eltűnő üzenetek küldésének engedélyezése."; +"Allow message reactions." = "A reakciók hozzáadása az üzenetekhez engedélyezve van."; /* No comment provided by engineer. */ -"Allow to irreversibly delete sent messages. (24 hours)" = "Elküldött üzenetek visszafordíthatatlan törlésének engedélyezése. (24 óra)"; +"Allow sending direct messages to members." = "A közvetlen üzenetek küldése a tagok között engedélyezve van."; /* No comment provided by engineer. */ -"Allow to send files and media." = "Fájlok és médiatartalom küldésének engedélyezése."; +"Allow sending disappearing messages." = "Az eltűnő üzenetek küldése engedélyezve van."; /* No comment provided by engineer. */ -"Allow to send voice messages." = "Hangüzenetek küldésének engedélyezése."; +"Allow sharing" = "Megosztás engedélyezése"; /* No comment provided by engineer. */ -"Allow voice messages only if your contact allows them." = "Hangüzenetek küldésének engedélyezése kizárólag abban az esetben, ha ismerőse is engedélyezi."; +"Allow to irreversibly delete sent messages. (24 hours)" = "Az elküldött üzenetek végleges törlése engedélyezve van. (24 óra)"; /* No comment provided by engineer. */ -"Allow voice messages?" = "Hangüzenetek engedélyezése?"; +"Allow to report messsages to moderators." = "Az üzenetek jelentése a moderátorok felé engedélyezve van."; /* No comment provided by engineer. */ -"Allow your contacts adding message reactions." = "Ismerősök általi üzenetreakciók hozzáadásának engedélyezése."; +"Allow to send files and media." = "A fájlok- és a médiatartalmak küldése engedélyezve van."; /* No comment provided by engineer. */ -"Allow your contacts to call you." = "Hívások engedélyezése ismerősök számára."; +"Allow to send SimpleX links." = "A SimpleX-hivatkozások küldése engedélyezve van."; /* No comment provided by engineer. */ -"Allow your contacts to irreversibly delete sent messages. (24 hours)" = "Elküldött üzenetek visszafordíthatatlan törlésének engedélyezése ismerősök számára. (24 óra)"; +"Allow to send voice messages." = "A hangüzenetek küldése engedélyezve van."; /* No comment provided by engineer. */ -"Allow your contacts to send disappearing messages." = "Eltűnő üzenetek engedélyezése ismerősök számára."; +"Allow voice messages only if your contact allows them." = "A hangüzenetek küldése csak abban az esetben van engedélyezve, ha a partnere is engedélyezi."; /* No comment provided by engineer. */ -"Allow your contacts to send voice messages." = "Hangüzenetek küldésének engedélyezése ismerősök számára."; +"Allow voice messages?" = "Engedélyezi a hangüzeneteket?"; /* No comment provided by engineer. */ -"Already connected?" = "Csatlakoztatva?"; +"Allow your contacts adding message reactions." = "A reakciók hozzáadása az üzenetekhez engedélyezve van a partnerei számára."; + +/* No comment provided by engineer. */ +"Allow your contacts to call you." = "A hívások kezdeményezése engedélyezve van a partnerei számára."; + +/* No comment provided by engineer. */ +"Allow your contacts to irreversibly delete sent messages. (24 hours)" = "Az elküldött üzenetek végleges törlése engedélyezve van a partnerei számára. (24 óra)"; + +/* No comment provided by engineer. */ +"Allow your contacts to send disappearing messages." = "Az eltűnő üzenetek küldésének engedélyezése a partnerei számára."; + +/* No comment provided by engineer. */ +"Allow your contacts to send voice messages." = "A hangüzenetek küldése engedélyezve van a partnerei számára."; + +/* No comment provided by engineer. */ +"Already connected?" = "Már kapcsolódott?"; /* No comment provided by engineer. */ "Already connecting!" = "Kapcsolódás folyamatban!"; /* No comment provided by engineer. */ -"Already joining the group!" = "Csatlakozás folyamatban!"; +"Already joining the group!" = "A csatlakozás folyamatban van a csoporthoz!"; /* pref value */ "always" = "mindig"; /* No comment provided by engineer. */ -"Always use relay" = "Mindig használjon átjátszó kiszolgálót"; +"Always use private routing." = "Mindig használjon privát útválasztást."; /* No comment provided by engineer. */ -"An empty chat profile with the provided name is created, and the app opens as usual." = "Egy üres csevegési profil jön létre a megadott névvel, és az alkalmazás a szokásos módon megnyílik."; +"Always use relay" = "Mindig használjon továbbítókiszolgálót"; /* No comment provided by engineer. */ -"and %lld other events" = "és %lld egyéb esemény"; +"An empty chat profile with the provided name is created, and the app opens as usual." = "Egy üres csevegési profil lesz létrehozva a megadott névvel, és az alkalmazás a szokásos módon megnyílik."; + +/* No comment provided by engineer. */ +"and %lld other events" = "és további %lld esemény"; + +/* report reason */ +"Another reason" = "Egyéb indoklás"; /* No comment provided by engineer. */ "Answer call" = "Hívás fogadása"; /* No comment provided by engineer. */ -"App build: %@" = "Az alkalmazás build száma: %@"; +"Anybody can host servers." = "Bárki üzemeltethet kiszolgálókat."; + +/* No comment provided by engineer. */ +"App build: %@" = "Az alkalmazás összeállítási száma: %@"; + +/* No comment provided by engineer. */ +"App data migration" = "Alkalmazásadatok átköltöztetése"; /* No comment provided by engineer. */ "App encrypts new local files (except videos)." = "Az alkalmazás titkosítja a helyi fájlokat (a videók kivételével)."; /* No comment provided by engineer. */ -"App icon" = "Alkalmazás ikon"; +"App group:" = "Alkalmazáscsoport:"; + +/* No comment provided by engineer. */ +"App icon" = "Alkalmazásikon"; /* No comment provided by engineer. */ "App passcode" = "Alkalmazás jelkód"; /* No comment provided by engineer. */ -"App passcode is replaced with self-destruct passcode." = "Az alkalmazás jelkód helyettesítésre kerül egy önmegsemmisítő jelkóddal."; +"App passcode is replaced with self-destruct passcode." = "Az alkalmazás-jelkód helyettesítve lesz egy önmegsemmisítő-jelkóddal."; /* No comment provided by engineer. */ -"App version" = "Alkalmazás verzió"; +"App session" = "Alkalmazás munkamenete"; /* No comment provided by engineer. */ -"App version: v%@" = "Alkalmazás verzió: v%@"; +"App version" = "Az alkalmazás verziója"; + +/* No comment provided by engineer. */ +"App version: v%@" = "Az alkalmazás verziója: v%@"; /* No comment provided by engineer. */ "Appearance" = "Megjelenés"; /* No comment provided by engineer. */ -"Attach" = "Csatolás"; +"Apply" = "Alkalmaz"; + +/* No comment provided by engineer. */ +"Apply to" = "Alkalmazás erre"; + +/* No comment provided by engineer. */ +"Archive" = "Archívum"; + +/* No comment provided by engineer. */ +"Archive %lld reports?" = "Archivál %lld jelentést?"; + +/* No comment provided by engineer. */ +"Archive all reports?" = "Archiválja az összes jelentést?"; + +/* No comment provided by engineer. */ +"Archive and upload" = "Archiválás és feltöltés"; + +/* No comment provided by engineer. */ +"Archive contacts to chat later." = "A partnerek archiválása a későbbi csevegéshez."; + +/* No comment provided by engineer. */ +"Archive report" = "Jelentés archiválása"; + +/* No comment provided by engineer. */ +"Archive report?" = "Archiválja a jelentést?"; + +/* swipe action */ +"Archive reports" = "Jelentések archiválása"; + +/* No comment provided by engineer. */ +"Archived contacts" = "Archivált partnerek"; + +/* No comment provided by engineer. */ +"archived report" = "archivált jelentés"; + +/* No comment provided by engineer. */ +"Archiving database" = "Adatbázis archiválása"; + +/* No comment provided by engineer. */ +"Attach" = "Mellékelés"; + +/* No comment provided by engineer. */ +"attempts" = "próbálkozások"; /* No comment provided by engineer. */ "Audio & video calls" = "Hang- és videóhívások"; @@ -531,19 +707,19 @@ "audio call (not e2e encrypted)" = "hanghívás (nem e2e titkosított)"; /* chat feature */ -"Audio/video calls" = "Hang-/videóhívások"; +"Audio/video calls" = "Hang- és videóhívások"; /* No comment provided by engineer. */ -"Audio/video calls are prohibited." = "A hang- és videóhívások le vannak tiltva."; +"Audio/video calls are prohibited." = "A hívások kezdeményezése le van tiltva ebben a csevegésben."; /* PIN entry */ -"Authentication cancelled" = "Hitelesítés megszakítva"; +"Authentication cancelled" = "Hitelesítés visszavonva"; /* No comment provided by engineer. */ -"Authentication failed" = "Hitelesítés sikertelen"; +"Authentication failed" = "Sikertelen hitelesítés"; /* No comment provided by engineer. */ -"Authentication is required before the call is connected, but you may miss calls." = "A hívás csatlakoztatása előtt hitelesítésre van szükség, de előfordulhat, hogy nem tud hívásokat fogadni."; +"Authentication is required before the call is connected, but you may miss calls." = "A hívás összekapcsolása előtt hitelesítésre van szükség, de előfordulhat, hogy lemarad a hívásokról."; /* No comment provided by engineer. */ "Authentication unavailable" = "Hitelesítés elérhetetlen"; @@ -555,22 +731,28 @@ "Auto-accept" = "Automatikus elfogadás"; /* No comment provided by engineer. */ -"Auto-accept contact requests" = "Ismerős jelölések automatikus elfogadása"; +"Auto-accept contact requests" = "Meghívási kérések automatikus elfogadása"; /* No comment provided by engineer. */ -"Auto-accept images" = "Fotók automatikus elfogadása"; +"Auto-accept images" = "Képek automatikus elfogadása"; + +/* alert title */ +"Auto-accept settings" = "Beállítások automatikus elfogadása"; /* No comment provided by engineer. */ "Back" = "Vissza"; /* No comment provided by engineer. */ -"Bad desktop address" = "Hibás számítógép azonosító"; - -/* integrity error chat item */ -"bad message hash" = "téves üzenet hash"; +"Background" = "Háttér"; /* No comment provided by engineer. */ -"Bad message hash" = "Téves üzenet hash"; +"Bad desktop address" = "Érvénytelen számítógépcím"; + +/* integrity error chat item */ +"bad message hash" = "érvénytelen az üzenet hasítóértéke"; + +/* No comment provided by engineer. */ +"Bad message hash" = "Érvénytelen az üzenet hasítóértéke"; /* integrity error chat item */ "bad message ID" = "téves üzenet ID"; @@ -579,52 +761,86 @@ "Bad message ID" = "Téves üzenet ID"; /* No comment provided by engineer. */ -"Better groups" = "Javított csoportok"; +"Better calls" = "Továbbfejlesztett hívásélmény"; /* No comment provided by engineer. */ -"Better messages" = "Jobb üzenetek"; +"Better groups" = "Továbbfejlesztett csoportok"; /* No comment provided by engineer. */ -"Block" = "Blokkolás"; +"Better groups performance" = "Továbbfejlesztett, gyorsabb csoportok"; /* No comment provided by engineer. */ -"Block for all" = "Mindenki számára letiltva"; +"Better message dates." = "Továbbfejlesztett üzenetdátumok."; /* No comment provided by engineer. */ -"Block group members" = "Csoporttagok blokkolása"; +"Better messages" = "Továbbfejlesztett üzenetek"; /* No comment provided by engineer. */ -"Block member" = "Tag blokkolása"; +"Better networking" = "Jobb hálózatkezelés"; /* No comment provided by engineer. */ -"Block member for all?" = "Tag letiltása mindenki számára?"; +"Better notifications" = "Továbbfejlesztett értesítések"; /* No comment provided by engineer. */ -"Block member?" = "Tag blokkolása?"; +"Better privacy and security" = "Továbbfejlesztett adatvédelem és biztonság"; + +/* No comment provided by engineer. */ +"Better security ✅" = "Továbbfejlesztett biztonság ✅"; + +/* No comment provided by engineer. */ +"Better user experience" = "Továbbfejlesztett felhasználói élmény"; + +/* No comment provided by engineer. */ +"Black" = "Fekete"; + +/* No comment provided by engineer. */ +"Block" = "Letiltás"; + +/* No comment provided by engineer. */ +"Block for all" = "Letiltás"; + +/* No comment provided by engineer. */ +"Block group members" = "Csoporttagok letiltása"; + +/* No comment provided by engineer. */ +"Block member" = "Letiltás"; + +/* No comment provided by engineer. */ +"Block member for all?" = "Az összes tag számára letiltja a tagot?"; + +/* No comment provided by engineer. */ +"Block member?" = "Letiltja a tagot?"; /* marked deleted chat item preview text */ -"blocked" = "blokkolva"; +"blocked" = "letiltva"; /* rcv group event chat item */ -"blocked %@" = "%@ letiltva"; +"blocked %@" = "letiltotta őt: %@"; -/* marked deleted chat item preview text */ -"blocked by admin" = "letiltva az admin által"; +/* blocked chat item +marked deleted chat item preview text */ +"blocked by admin" = "letiltva az adminisztrátor által"; /* No comment provided by engineer. */ -"Blocked by admin" = "Letiltva az admin által"; +"Blocked by admin" = "Letiltva az adminisztrátor által"; + +/* No comment provided by engineer. */ +"Blur for better privacy." = "Elhomályosítás a jobb adatvédelemért."; + +/* No comment provided by engineer. */ +"Blur media" = "Médiatartalom elhomályosítása"; /* No comment provided by engineer. */ "bold" = "félkövér"; /* No comment provided by engineer. */ -"Both you and your contact can add message reactions." = "Mindkét fél is hozzáadhat üzenetreakciókat."; +"Both you and your contact can add message reactions." = "Mindkét fél hozzáadhat az üzenetekhez reakciókat."; /* No comment provided by engineer. */ -"Both you and your contact can irreversibly delete sent messages. (24 hours)" = "Mindkét fél visszafordíthatatlanul törölheti az elküldött üzeneteket. (24 óra)"; +"Both you and your contact can irreversibly delete sent messages. (24 hours)" = "Mindkét fél véglegesen törölheti az elküldött üzeneteket. (24 óra)"; /* No comment provided by engineer. */ -"Both you and your contact can make calls." = "Mindkét fél tud hívásokat indítani."; +"Both you and your contact can make calls." = "Mindkét fél tud hívásokat kezdeményezni."; /* No comment provided by engineer. */ "Both you and your contact can send disappearing messages." = "Mindkét fél küldhet eltűnő üzeneteket."; @@ -636,13 +852,28 @@ "Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Bolgár, finn, thai és ukrán – köszönet a felhasználóknak és a [Weblate-nek](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"; /* No comment provided by engineer. */ -"By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Csevegési profil (alapértelmezett) vagy [kapcsolat alapján] (https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BÉTA)."; +"Business address" = "Üzleti cím"; + +/* No comment provided by engineer. */ +"Business chats" = "Üzleti csevegések"; + +/* No comment provided by engineer. */ +"Businesses" = "Üzleti"; + +/* No comment provided by engineer. */ +"By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "A csevegési profillal (alapértelmezett), vagy a [kapcsolattal] (https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BÉTA)."; + +/* No comment provided by engineer. */ +"By using SimpleX Chat you agree to:\n- send only legal content in public groups.\n- respect other users – no spam." = "A SimpleX Chat használatával Ön elfogadja, hogy:\n- csak elfogadott tartalmakat tesz közzé a nyilvános csoportokban.\n- tiszteletben tartja a többi felhasználót, és nem küld kéretlen tartalmat senkinek."; + +/* No comment provided by engineer. */ +"call" = "hívás"; /* No comment provided by engineer. */ "Call already ended!" = "A hívás már befejeződött!"; /* call status */ -"call error" = "hiba a hívásban"; +"call error" = "híváshiba"; /* call status */ "call in progress" = "hívás folyamatban"; @@ -654,74 +885,114 @@ "Calls" = "Hívások"; /* No comment provided by engineer. */ -"Camera not available" = "A fényképező nem elérhető"; +"Calls prohibited!" = "A hívások le vannak tiltva!"; /* No comment provided by engineer. */ -"Can't invite contact!" = "Ismerősök meghívása le van tiltva!"; +"Camera not available" = "A kamera nem elérhető"; /* No comment provided by engineer. */ -"Can't invite contacts!" = "Ismerősök meghívása nem lehetséges!"; +"Can't call contact" = "Nem lehet felhívni a partnert"; /* No comment provided by engineer. */ -"Cancel" = "Megszakítás"; +"Can't call member" = "Nem lehet felhívni a tagot"; + +/* No comment provided by engineer. */ +"Can't invite contact!" = "Nem lehet meghívni a partnert!"; + +/* No comment provided by engineer. */ +"Can't invite contacts!" = "Nem lehet meghívni a partnereket!"; + +/* No comment provided by engineer. */ +"Can't message member" = "Nem lehet üzenetet küldeni a tagnak"; + +/* alert action +alert button */ +"Cancel" = "Mégse"; + +/* No comment provided by engineer. */ +"Cancel migration" = "Átköltöztetés visszavonása"; /* feature offered item */ -"cancelled %@" = "%@ törölve"; +"cancelled %@" = "%@ visszavonva"; /* No comment provided by engineer. */ -"Cannot access keychain to save database password" = "Nem lehet hozzáférni a kulcstartóhoz az adatbázis jelszavának mentéséhez"; +"Cannot access keychain to save database password" = "Nem lehet hozzáférni a kulcstartóhoz az adatbázisjelszó mentéséhez"; /* No comment provided by engineer. */ +"Cannot forward message" = "Nem lehet továbbítani az üzenetet"; + +/* alert title */ "Cannot receive file" = "Nem lehet fogadni a fájlt"; -/* No comment provided by engineer. */ -"Change" = "Változtatás"; +/* snd error text */ +"Capacity exceeded - recipient did not receive previously sent messages." = "Kapacitás túllépés – a címzett nem kapta meg a korábban elküldött üzeneteket."; /* No comment provided by engineer. */ -"Change database passphrase?" = "Adatbázis jelmondat megváltoztatása?"; +"Cellular" = "Mobilhálózat"; + +/* No comment provided by engineer. */ +"Change" = "Módosítás"; + +/* alert title */ +"Change automatic message deletion?" = "Módosítja az automatikus üzenettörlést?"; /* authentication reason */ -"Change lock mode" = "Zárolási mód megváltoztatása"; +"Change chat profiles" = "Csevegési profilok módosítása"; /* No comment provided by engineer. */ -"Change member role?" = "Tag szerepkörének megváltoztatása?"; +"Change database passphrase?" = "Módosítja az adatbázis jelmondatát?"; /* authentication reason */ -"Change passcode" = "Jelkód megváltoztatása"; +"Change lock mode" = "Zárolási mód módosítása"; /* No comment provided by engineer. */ -"Change receiving address" = "A fogadó cím megváltoztatása"; - -/* No comment provided by engineer. */ -"Change receiving address?" = "Megváltoztatja a fogadó címet?"; - -/* No comment provided by engineer. */ -"Change role" = "Szerepkör megváltoztatása"; +"Change member role?" = "Módosítja a tag szerepkörét?"; /* authentication reason */ -"Change self-destruct mode" = "Önmegsemmisítő mód megváltoztatása"; +"Change passcode" = "Jelkód módosítása"; + +/* No comment provided by engineer. */ +"Change receiving address" = "Fogadási cím módosítása"; + +/* No comment provided by engineer. */ +"Change receiving address?" = "Módosítja a fogadási címet?"; + +/* No comment provided by engineer. */ +"Change role" = "Szerepkör módosítása"; + +/* authentication reason */ +"Change self-destruct mode" = "Önmegsemmisítő-mód módosítása"; /* authentication reason - set passcode view */ -"Change self-destruct passcode" = "Önmegsemmisító jelkód megváltoztatása"; +set passcode view */ +"Change self-destruct passcode" = "Önmegsemmisítő-jelkód módosítása"; /* chat item text */ -"changed address for you" = "Cím megváltoztatva"; +"changed address for you" = "módosította a címet az Ön számára"; /* rcv group event chat item */ -"changed role of %@ to %@" = "%1$@ szerepköre megváltozott erre: %2$@"; +"changed role of %@ to %@" = "a következőre módosította %1$@ szerepkörét: „%2$@”"; /* rcv group event chat item */ -"changed your role to %@" = "megváltoztatta a szerepkörét erre: %@"; +"changed your role to %@" = "a következőre módosította az Ön szerepkörét: „%@”"; /* chat item text */ "changing address for %@…" = "cím módosítása %@ számára…"; /* chat item text */ -"changing address…" = "azonosító megváltoztatása…"; +"changing address…" = "cím módosítása…"; /* No comment provided by engineer. */ -"Chat archive" = "Csevegési archívum"; +"Chat" = "Csevegés"; + +/* No comment provided by engineer. */ +"Chat already exists" = "A csevegés már létezik"; + +/* No comment provided by engineer. */ +"Chat already exists!" = "A csevegés már létezik!"; + +/* No comment provided by engineer. */ +"Chat colors" = "Csevegés színei"; /* No comment provided by engineer. */ "Chat console" = "Csevegési konzol"; @@ -732,6 +1003,9 @@ /* No comment provided by engineer. */ "Chat database deleted" = "Csevegési adatbázis törölve"; +/* No comment provided by engineer. */ +"Chat database exported" = "Csevegési adatbázis exportálva"; + /* No comment provided by engineer. */ "Chat database imported" = "Csevegési adatbázis importálva"; @@ -739,23 +1013,53 @@ "Chat is running" = "A csevegés fut"; /* No comment provided by engineer. */ -"Chat is stopped" = "A csevegés leállt"; +"Chat is stopped" = "A csevegés megállt"; /* No comment provided by engineer. */ -"Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "A csevegés leállt. Ha már használta ezt az adatbázist egy másik eszközön, úgy visszaállítás szükséges a csevegés megkezdése előtt."; +"Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "A csevegés megállt. Ha már használta ezt az adatbázist egy másik eszközön, úgy visszaállítás szükséges a csevegés elindítása előtt."; + +/* No comment provided by engineer. */ +"Chat list" = "Csevegési lista"; + +/* No comment provided by engineer. */ +"Chat migrated!" = "A csevegés átköltöztetve!"; /* No comment provided by engineer. */ "Chat preferences" = "Csevegési beállítások"; +/* alert message */ +"Chat preferences were changed." = "A csevegési beállítások módosultak."; + +/* No comment provided by engineer. */ +"Chat profile" = "Csevegési profil"; + +/* No comment provided by engineer. */ +"Chat theme" = "Csevegés témája"; + +/* No comment provided by engineer. */ +"Chat will be deleted for all members - this cannot be undone!" = "A csevegés minden tag számára törölve lesz – ez a művelet nem vonható vissza!"; + +/* No comment provided by engineer. */ +"Chat will be deleted for you - this cannot be undone!" = "A csevegés törölve lesz az Ön számára – ez a művelet nem vonható vissza!"; + /* No comment provided by engineer. */ "Chats" = "Csevegések"; /* No comment provided by engineer. */ +"Check messages every 20 min." = "Üzenetek ellenőrzése 20 percenként."; + +/* No comment provided by engineer. */ +"Check messages when allowed." = "Üzenetek ellenőrzése, amikor engedélyezett."; + +/* alert title */ "Check server address and try again." = "Kiszolgáló címének ellenőrzése és újrapróbálkozás."; /* No comment provided by engineer. */ "Chinese and Spanish interface" = "Kínai és spanyol kezelőfelület"; +/* No comment provided by engineer. */ +"Choose _Migrate from another device_ on the new device and scan QR code." = "Válassza az _Átköltöztetés egy másik eszközről_ opciót az új eszközén és olvassa be a QR-kódot."; + /* No comment provided by engineer. */ "Choose file" = "Fájl kiválasztása"; @@ -763,43 +1067,100 @@ "Choose from library" = "Választás a könyvtárból"; /* No comment provided by engineer. */ +"Chunks deleted" = "Törölt töredékek"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "Letöltött töredékek"; + +/* No comment provided by engineer. */ +"Chunks uploaded" = "Feltöltött töredékek"; + +/* swipe action */ "Clear" = "Kiürítés"; /* No comment provided by engineer. */ -"Clear conversation" = "Beszélgetés kiürítése"; +"Clear conversation" = "Üzenetek kiürítése"; /* No comment provided by engineer. */ -"Clear conversation?" = "Beszélgetés kiürítése?"; +"Clear conversation?" = "Kiüríti az üzeneteket?"; /* No comment provided by engineer. */ -"Clear private notes?" = "Privát jegyzetek törlése?"; +"Clear group?" = "Kiüríti a csoportot?"; + +/* No comment provided by engineer. */ +"Clear or delete group?" = "Csoport kiürítése vagy törlése?"; + +/* No comment provided by engineer. */ +"Clear private notes?" = "Kiüríti a privát jegyzeteket?"; /* No comment provided by engineer. */ "Clear verification" = "Hitelesítés törlése"; /* No comment provided by engineer. */ -"colored" = "színes"; +"Color chats with the new themes." = "Csevegések színezése új témákkal."; /* No comment provided by engineer. */ -"Colors" = "Színek"; +"Color mode" = "Színmód"; + +/* No comment provided by engineer. */ +"colored" = "színezett"; + +/* report reason */ +"Community guidelines violation" = "Közösségi irányelvek megsértése"; /* server test step */ -"Compare file" = "Fájl összehasonlítás"; +"Compare file" = "Fájl-összehasonlítás"; /* No comment provided by engineer. */ -"Compare security codes with your contacts." = "Biztonsági kódok összehasonlítása az ismerősökkel."; +"Compare security codes with your contacts." = "Biztonsági kódok összehasonlítása a partnerekével."; /* No comment provided by engineer. */ "complete" = "befejezett"; /* No comment provided by engineer. */ -"Configure ICE servers" = "ICE kiszolgálók beállítása"; +"Completed" = "Elkészült"; + +/* No comment provided by engineer. */ +"Conditions accepted on: %@." = "Feltételek elfogadásának ideje: %@."; + +/* No comment provided by engineer. */ +"Conditions are accepted for the operator(s): **%@**." = "A következő üzemeltető(k) számára elfogadott feltételek: **%@**."; + +/* No comment provided by engineer. */ +"Conditions are already accepted for these operator(s): **%@**." = "A feltételek már el lettek fogadva a következő üzemeltető(k) számára: **%@**."; + +/* No comment provided by engineer. */ +"Conditions of use" = "Használati feltételek"; + +/* No comment provided by engineer. */ +"Conditions will be accepted for the operator(s): **%@**." = "A feltételek el lesznek fogadva a következő üzemeltető(k) számára: **%@**."; + +/* No comment provided by engineer. */ +"Conditions will be accepted on: %@." = "A feltételek el lesznek fogadva a következő időpontban: %@."; + +/* No comment provided by engineer. */ +"Conditions will be automatically accepted for enabled operators on: %@." = "A feltételek automatikusan el lesznek fogadva az engedélyezett üzemeltetők számára a következő időpontban: %@."; + +/* No comment provided by engineer. */ +"Configure ICE servers" = "ICE-kiszolgálók beállítása"; + +/* No comment provided by engineer. */ +"Configure server operators" = "Kiszolgálóüzemeltetők beállítása"; /* No comment provided by engineer. */ "Confirm" = "Megerősítés"; /* No comment provided by engineer. */ -"Confirm database upgrades" = "Adatbázis frissítés megerősítése"; +"Confirm contact deletion?" = "Biztosan törli a partnert?"; + +/* No comment provided by engineer. */ +"Confirm database upgrades" = "Adatbázis fejlesztésének megerősítése"; + +/* No comment provided by engineer. */ +"Confirm files from unknown servers." = "Ismeretlen kiszolgálókról származó fájlok megerősítése."; + +/* No comment provided by engineer. */ +"Confirm network settings" = "Hálózati beállítások megerősítése"; /* No comment provided by engineer. */ "Confirm new passphrase…" = "Új jelmondat megerősítése…"; @@ -810,6 +1171,15 @@ /* No comment provided by engineer. */ "Confirm password" = "Jelszó megerősítése"; +/* No comment provided by engineer. */ +"Confirm that you remember database passphrase to migrate it." = "Az átköltöztetéshez erősítse meg, hogy emlékszik az adatbázis jelmondatára."; + +/* No comment provided by engineer. */ +"Confirm upload" = "Feltöltés megerősítése"; + +/* token status text */ +"Confirmed" = "Megerősítve"; + /* server test step */ "Connect" = "Kapcsolódás"; @@ -817,50 +1187,62 @@ "Connect automatically" = "Kapcsolódás automatikusan"; /* No comment provided by engineer. */ -"Connect incognito" = "Inkognítóban csatlakozva"; +"Connect incognito" = "Kapcsolódás inkognitóban"; /* No comment provided by engineer. */ -"Connect to desktop" = "Kapcsolódás számítógéphez"; +"Connect to desktop" = "Társítás számítógéppel"; /* No comment provided by engineer. */ -"connect to SimpleX Chat developers." = "Csatlakozás a SimpleX Chat fejlesztőkhöz."; +"connect to SimpleX Chat developers." = "kapcsolódás a SimpleX Chat fejlesztőkhöz."; /* No comment provided by engineer. */ -"Connect to yourself?" = "Kapcsolódás saját magához?"; +"Connect to your friends faster." = "Kapcsolódjon gyorsabban a partnereihez."; /* No comment provided by engineer. */ -"Connect to yourself?\nThis is your own one-time link!" = "Kapcsolódás saját magához?\nEz az egyszer használatos hivatkozása!"; +"Connect to yourself?" = "Kapcsolódik saját magához?"; /* No comment provided by engineer. */ -"Connect to yourself?\nThis is your own SimpleX address!" = "Kapcsolódás saját magához?\nEz a SimpleX azonosítója!"; +"Connect to yourself?\nThis is your own one-time link!" = "Kapcsolódik saját magához?\nEz a saját egyszer használható meghívója!"; /* No comment provided by engineer. */ -"Connect via contact address" = "Kapcsolódás ismerős azonosítója által"; +"Connect to yourself?\nThis is your own SimpleX address!" = "Kapcsolódik saját magához?\nEz a saját SimpleX-címe!"; + +/* No comment provided by engineer. */ +"Connect via contact address" = "Kapcsolódás a kapcsolattartási címen keresztül"; /* No comment provided by engineer. */ "Connect via link" = "Kapcsolódás egy hivatkozáson keresztül"; /* No comment provided by engineer. */ -"Connect via one-time link" = "Kapcsolódás egyszer használatos hivatkozáson keresztül"; +"Connect via one-time link" = "Kapcsolódás egyszer használható meghívón keresztül"; /* No comment provided by engineer. */ -"Connect with %@" = "Kapcsolódás ezzel: %@"; +"Connect with %@" = "Kapcsolódás a következővel: %@"; /* No comment provided by engineer. */ -"connected" = "kapcsolódva"; +"connected" = "kapcsolódott"; /* No comment provided by engineer. */ -"Connected desktop" = "Csatlakoztatott számítógép"; +"Connected" = "Kapcsolódott"; + +/* No comment provided by engineer. */ +"Connected desktop" = "Társított számítógép"; /* rcv group event chat item */ -"connected directly" = "közvetlenül kapcsolódva"; +"connected directly" = "közvetlenül kapcsolódott"; /* No comment provided by engineer. */ -"Connected to desktop" = "Csatlakozva a számítógéphez"; +"Connected servers" = "Kapcsolódott kiszolgálók"; + +/* No comment provided by engineer. */ +"Connected to desktop" = "Kapcsolódva a számítógéphez"; /* No comment provided by engineer. */ "connecting" = "kapcsolódás"; +/* No comment provided by engineer. */ +"Connecting" = "Kapcsolódás"; + /* No comment provided by engineer. */ "connecting (accepted)" = "kapcsolódás (elfogadva)"; @@ -868,29 +1250,38 @@ "connecting (announced)" = "kapcsolódás (bejelentve)"; /* No comment provided by engineer. */ -"connecting (introduced)" = "kapcsolódás (bejelentve)"; +"connecting (introduced)" = "kapcsolódás (bemutatkozva)"; /* No comment provided by engineer. */ -"connecting (introduction invitation)" = "csatlakozás (bemutatkozás meghívás)"; +"connecting (introduction invitation)" = "kapcsolódás (bemutatkozó meghívó)"; /* call status */ -"connecting call" = "hívás kapcsolódik…"; +"connecting call" = "kapcsolódási hívás…"; /* No comment provided by engineer. */ "Connecting server…" = "Kapcsolódás a kiszolgálóhoz…"; /* No comment provided by engineer. */ -"Connecting server… (error: %@)" = "Kapcsolódás a kiszolgálóhoz... (hiba: %@)"; +"Connecting server… (error: %@)" = "Kapcsolódás a kiszolgálóhoz… (hiba: %@)"; + +/* No comment provided by engineer. */ +"Connecting to contact, please wait or check later!" = "Kapcsolódás a partnerhez, várjon vagy ellenőrizze később!"; /* No comment provided by engineer. */ "Connecting to desktop" = "Kapcsolódás a számítógéphez"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "kapcsolódás…"; /* No comment provided by engineer. */ "Connection" = "Kapcsolat"; +/* No comment provided by engineer. */ +"Connection and servers status." = "Kapcsolatok- és kiszolgálók állapotának megjelenítése."; + +/* No comment provided by engineer. */ +"Connection blocked" = "A kapcsolat le van tiltva"; + /* No comment provided by engineer. */ "Connection error" = "Kapcsolódási hiba"; @@ -898,76 +1289,115 @@ "Connection error (AUTH)" = "Kapcsolódási hiba (AUTH)"; /* chat list item title (it should not be shown */ -"connection established" = "Kapcsolat létrehozva"; +"connection established" = "kapcsolat létrehozva"; /* No comment provided by engineer. */ -"Connection request sent!" = "Kapcsolódási kérés elküldve!"; +"Connection is blocked by server operator:\n%@" = "A kiszolgáló üzemeltetője letiltotta a kapcsolatot:\n%@"; + +/* No comment provided by engineer. */ +"Connection not ready." = "A kapcsolat nem áll készen."; + +/* No comment provided by engineer. */ +"Connection notifications" = "Kapcsolódási értesítések"; + +/* No comment provided by engineer. */ +"Connection request sent!" = "Meghívási kérés elküldve!"; + +/* No comment provided by engineer. */ +"Connection requires encryption renegotiation." = "A kapcsolat titkosítása újraegyeztetést igényel."; + +/* No comment provided by engineer. */ +"Connection security" = "Kapcsolatbiztonság"; /* No comment provided by engineer. */ "Connection terminated" = "Kapcsolat megszakítva"; /* No comment provided by engineer. */ -"Connection timeout" = "Kapcsolat időtúllépés"; +"Connection timeout" = "Időtúllépés kapcsolódáskor"; + +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "A kapcsolat a számítógéppel megszakadt"; /* connection information */ "connection:%@" = "kapcsolat: %@"; +/* No comment provided by engineer. */ +"Connections" = "Kapcsolatok"; + /* profile update event chat item */ -"contact %@ changed to %@" = "%1$@ ismerősének neve megváltozott erre: %2$@"; +"contact %@ changed to %@" = "%1$@ a következőre módosította a nevét: %2$@"; /* No comment provided by engineer. */ -"Contact allows" = "Ismerős engedélyezi"; +"Contact allows" = "Partner engedélyezi"; /* No comment provided by engineer. */ -"Contact already exists" = "Létező ismerős"; +"Contact already exists" = "A partner már létezik"; /* No comment provided by engineer. */ -"contact has e2e encryption" = "az ismerősnél az e2e titkosítás elérhető"; +"Contact deleted!" = "Partner törölve!"; /* No comment provided by engineer. */ -"contact has no e2e encryption" = "az ismerősnél az e2e titkosítás nem elérhető"; +"contact has e2e encryption" = "a partner e2e titkosítással rendelkezik"; + +/* No comment provided by engineer. */ +"contact has no e2e encryption" = "a partner nem rendelkezik e2e titkosítással"; /* notification */ -"Contact hidden:" = "Ismerős elrejtve:"; +"Contact hidden:" = "Rejtett név:"; /* notification */ -"Contact is connected" = "Ismerős csatlakozott"; +"Contact is connected" = "Partnere kapcsolódott"; /* No comment provided by engineer. */ -"Contact is not connected yet!" = "Az ismerős még nem csatlakozott!"; +"Contact is deleted." = "Törölt partner."; /* No comment provided by engineer. */ -"Contact name" = "Ismerős neve"; +"Contact name" = "Csak név"; /* No comment provided by engineer. */ -"Contact preferences" = "Ismerős beállításai"; +"Contact preferences" = "Partnerbeállítások"; /* No comment provided by engineer. */ -"Contacts" = "Ismerősök"; +"Contact will be deleted - this cannot be undone!" = "A partner törölve lesz – ez a művelet nem vonható vissza!"; /* No comment provided by engineer. */ -"Contacts can mark messages for deletion; you will be able to view them." = "Az ismerősök törlésre jelölhetnek üzeneteket ; megtekintheti őket."; +"Contacts" = "Partnerek"; + +/* No comment provided by engineer. */ +"Contacts can mark messages for deletion; you will be able to view them." = "A partnerei törlésre jelölhetnek üzeneteket; Ön majd meg tudja nézni azokat."; + +/* blocking reason */ +"Content violates conditions of use" = "A tartalom sérti a használati feltételeket"; /* No comment provided by engineer. */ "Continue" = "Folytatás"; -/* chat item action */ +/* No comment provided by engineer. */ +"Conversation deleted!" = "Beszélgetés törölve!"; + +/* No comment provided by engineer. */ "Copy" = "Másolás"; /* No comment provided by engineer. */ -"Core version: v%@" = "Alapverziószám: v%@"; +"Copy error" = "Másolási hiba"; /* No comment provided by engineer. */ -"Correct name to %@?" = "Név javítása erre: %@?"; +"Core version: v%@" = "Fő verzió: v%@"; + +/* No comment provided by engineer. */ +"Corner" = "Sarok"; + +/* No comment provided by engineer. */ +"Correct name to %@?" = "Helyesbíti a nevet a következőre: %@?"; /* No comment provided by engineer. */ "Create" = "Létrehozás"; /* No comment provided by engineer. */ -"Create a group using a random profile." = "Csoport létrehozása véletlenszerűen létrehozott profillal."; +"Create 1-time link" = "Egyszer használható meghívó létrehozása"; /* No comment provided by engineer. */ -"Create an address to let people connect with you." = "Azonosító létrehozása, hogy az emberek kapcsolatba léphessenek önnel."; +"Create a group using a random profile." = "Csoport létrehozása véletlenszerű profillal."; /* server test step */ "Create file" = "Fájl létrehozása"; @@ -976,43 +1406,52 @@ "Create group" = "Csoport létrehozása"; /* No comment provided by engineer. */ -"Create group link" = "Csoportos hivatkozás létrehozása"; +"Create group link" = "Csoporthivatkozás létrehozása"; /* No comment provided by engineer. */ "Create link" = "Hivatkozás létrehozása"; /* No comment provided by engineer. */ -"Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" = "Új profil létrehozása az [asztali kliensben](https://simplex.chat/downloads/). 💻"; +"Create list" = "Lista létrehozása"; + +/* No comment provided by engineer. */ +"Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" = "Új profil létrehozása a [számítógép-alkalmazásban](https://simplex.chat/downloads/). 💻"; /* No comment provided by engineer. */ "Create profile" = "Profil létrehozása"; /* server test step */ -"Create queue" = "Várólista létrehozása"; +"Create queue" = "Sorba állítás létrehozása"; /* No comment provided by engineer. */ "Create secret group" = "Titkos csoport létrehozása"; /* No comment provided by engineer. */ -"Create SimpleX address" = "SimpleX azonosító létrehozása"; +"Create SimpleX address" = "SimpleX-cím létrehozása"; /* No comment provided by engineer. */ "Create your profile" = "Saját profil létrehozása"; /* No comment provided by engineer. */ -"Created at" = "Létrehozva ekkor"; - -/* copied message info */ -"Created at: %@" = "Létrehozva ekkor: %@"; +"Created" = "Létrehozva"; /* No comment provided by engineer. */ -"Created on %@" = "Létrehozva %@"; +"Created at" = "Létrehozva"; + +/* copied message info */ +"Created at: %@" = "Létrehozva: %@"; + +/* No comment provided by engineer. */ +"Creating archive link" = "Archívum hivatkozás létrehozása"; /* No comment provided by engineer. */ "Creating link…" = "Hivatkozás létrehozása…"; /* No comment provided by engineer. */ -"creator" = "szerző"; +"creator" = "készítő"; + +/* No comment provided by engineer. */ +"Current conditions text couldn't be loaded, you can review conditions via this link:" = "A jelenlegi feltételek szövegét nem lehetett betölteni, a feltételeket a következő hivatkozáson keresztül vizsgálhatja felül:"; /* No comment provided by engineer. */ "Current Passcode" = "Jelenlegi jelkód"; @@ -1021,84 +1460,103 @@ "Current passphrase…" = "Jelenlegi jelmondat…"; /* No comment provided by engineer. */ -"Currently maximum supported file size is %@." = "Jelenleg a maximális támogatott fájlméret %@."; - -/* dropdown time picker choice */ -"custom" = "egyedi"; +"Current profile" = "Jelenlegi profil"; /* No comment provided by engineer. */ -"Custom time" = "Személyreszabott idő"; +"Currently maximum supported file size is %@." = "Jelenleg támogatott legnagyobb fájl méret: %@."; + +/* dropdown time picker choice */ +"custom" = "egyéni"; + +/* No comment provided by engineer. */ +"Custom time" = "Egyéni időköz"; + +/* No comment provided by engineer. */ +"Customizable message shape." = "Személyre szabható üzenetbuborékok."; + +/* No comment provided by engineer. */ +"Customize theme" = "Téma személyre szabása"; /* No comment provided by engineer. */ "Dark" = "Sötét"; /* No comment provided by engineer. */ -"Database downgrade" = "Visszatérés a korábbi adatbázis verzióra"; +"Dark mode colors" = "Sötét mód színei"; + +/* No comment provided by engineer. */ +"Database downgrade" = "Adatbázis visszafejlesztése"; /* No comment provided by engineer. */ "Database encrypted!" = "Adatbázis titkosítva!"; /* No comment provided by engineer. */ -"Database encryption passphrase will be updated and stored in the keychain.\n" = "Az adatbázis titkosítási jelmondata frissül és tárolódik a kulcstárolóban.\n"; +"Database encryption passphrase will be updated and stored in the keychain.\n" = "Az adatbázis titkosítási jelmondata frissülni fog és a kulcstartóban lesz tárolva.\n"; /* No comment provided by engineer. */ -"Database encryption passphrase will be updated.\n" = "Adatbázis titkosítási jelmondat frissítve lesz.\n"; +"Database encryption passphrase will be updated.\n" = "Az adatbázis titkosítási jelmondata frissítve lesz.\n"; /* No comment provided by engineer. */ -"Database error" = "Adatbázis hiba"; +"Database error" = "Adatbázishiba"; /* No comment provided by engineer. */ -"Database ID" = "Adatbázis ID"; +"Database ID" = "Adatbázis-azonosító"; /* copied message info */ -"Database ID: %d" = "Adatbázis azonosító: %d"; +"Database ID: %d" = "Adatbázis-azonosító: %d"; /* No comment provided by engineer. */ -"Database IDs and Transport isolation option." = "Adatbázis azonosítók és átviteli izolációs beállítások."; +"Database IDs and Transport isolation option." = "Adatbázis-azonosítók és átvitel-izolációs beállítások."; /* No comment provided by engineer. */ -"Database is encrypted using a random passphrase, you can change it." = "Az adatbázis egy véletlenszerű jelmondattal van titkosítva, megváltoztatható."; +"Database is encrypted using a random passphrase, you can change it." = "Az adatbázis egy véletlenszerű jelmondattal van titkosítva, amelyet szabadon módosíthat."; /* No comment provided by engineer. */ -"Database is encrypted using a random passphrase. Please change it before exporting." = "Az adatbázis egy véletlenszerű jelmondattal van titkosítva. Exportálás előtti módosítás szükséges."; +"Database is encrypted using a random passphrase. Please change it before exporting." = "Az adatbázis egy véletlenszerű jelmondattal van titkosítva. Exportálás előtt módosítsa."; /* No comment provided by engineer. */ -"Database passphrase" = "Adatbázis jelmondat"; +"Database passphrase" = "Adatbázis-jelmondat"; /* No comment provided by engineer. */ -"Database passphrase & export" = "Adatbázis jelmondat és exportálás"; +"Database passphrase & export" = "Adatbázis-jelmondat és -exportálás"; /* No comment provided by engineer. */ -"Database passphrase is different from saved in the keychain." = "Az adatbázis jelmondata eltér a kulcstárlóban mentettől."; +"Database passphrase is different from saved in the keychain." = "Az adatbázis jelmondata nem egyezik a kulcstartóba mentettől."; /* No comment provided by engineer. */ -"Database passphrase is required to open chat." = "Adatbázis jelmondat szükséges a csevegés megnyitásához."; +"Database passphrase is required to open chat." = "A csevegés megnyitásához adja meg az adatbázis jelmondatát."; /* No comment provided by engineer. */ "Database upgrade" = "Adatbázis fejlesztése"; /* No comment provided by engineer. */ -"database version is newer than the app, but no down migration for: %@" = "az adatbázis verziója újabb, mint az alkalmazásé, de nincs visszafelé migráció: %@"; +"database version is newer than the app, but no down migration for: %@" = "az adatbázis verziója újabb, mint az alkalmazásé, de a visszafelé történő átköltöztetés viszont nem lehetséges a következőhöz: %@"; /* No comment provided by engineer. */ -"Database will be encrypted and the passphrase stored in the keychain.\n" = "Az adatbázis titkosítva lesz, a jelmondat pedig a kulcstárolóban lesz tárolva.\n"; +"Database will be encrypted and the passphrase stored in the keychain.\n" = "Az adatbázis titkosítva lesz, a jelmondat pedig a kulcstartóban lesz tárolva.\n"; /* No comment provided by engineer. */ -"Database will be encrypted.\n" = "Az adatbázis titkosításra kerül.\n"; +"Database will be encrypted.\n" = "Az adatbázis titkosítva lesz.\n"; /* No comment provided by engineer. */ -"Database will be migrated when the app restarts" = "Az adatbázis az alkalmazás újraindításakor migrálásra kerül"; +"Database will be migrated when the app restarts" = "Az adatbázis az alkalmazás újraindításakor lesz átköltöztetve"; /* time unit */ "days" = "nap"; +/* No comment provided by engineer. */ +"Debug delivery" = "Kézbesítési hibák felderítése"; + /* No comment provided by engineer. */ "Decentralized" = "Decentralizált"; /* message decrypt error item */ "Decryption error" = "Titkosítás visszafejtési hiba"; -/* pref value */ +/* No comment provided by engineer. */ +"decryption errors" = "visszafejtési hibák"; + +/* delete after time +pref value */ "default (%@)" = "alapértelmezett (%@)"; /* No comment provided by engineer. */ @@ -1107,156 +1565,202 @@ /* No comment provided by engineer. */ "default (yes)" = "alapértelmezett (igen)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "Törlés"; +/* No comment provided by engineer. */ +"Delete %lld messages of members?" = "Törli a tagok %lld üzenetét?"; + /* No comment provided by engineer. */ "Delete %lld messages?" = "Töröl %lld üzenetet?"; /* No comment provided by engineer. */ -"Delete address" = "Azonosító törlése"; +"Delete address" = "Cím törlése"; /* No comment provided by engineer. */ -"Delete address?" = "Azonosító törlése?"; +"Delete address?" = "Törli a címet?"; /* No comment provided by engineer. */ -"Delete after" = "Törlés miután"; +"Delete after" = "Törlés ennyi idő után"; /* No comment provided by engineer. */ -"Delete all files" = "Minden fájl törlése"; +"Delete all files" = "Az összes fájl törlése"; /* No comment provided by engineer. */ -"Delete and notify contact" = "Törlés és ismerős értesítése"; +"Delete and notify contact" = "Törlés, és a partner értesítése"; /* No comment provided by engineer. */ -"Delete archive" = "Archívum törlése"; +"Delete chat" = "Csevegés törlése"; /* No comment provided by engineer. */ -"Delete chat archive?" = "Csevegési archívum törlése?"; +"Delete chat messages from your device." = "Csevegési üzenetek törlése a saját eszközéről."; /* No comment provided by engineer. */ "Delete chat profile" = "Csevegési profil törlése"; /* No comment provided by engineer. */ -"Delete chat profile?" = "Csevegési profil törlése?"; +"Delete chat profile?" = "Törli a csevegési profilt?"; + +/* No comment provided by engineer. */ +"Delete chat?" = "Törli a csevegést?"; /* No comment provided by engineer. */ "Delete connection" = "Kapcsolat törlése"; /* No comment provided by engineer. */ -"Delete contact" = "Ismerős törlése"; +"Delete contact" = "Partner törlése"; /* No comment provided by engineer. */ -"Delete Contact" = "Ismerős törlése"; - -/* No comment provided by engineer. */ -"Delete contact?\nThis cannot be undone!" = "Ismerős törlése?\nEzt nem vonható vissza!"; +"Delete contact?" = "Törli a partnert?"; /* No comment provided by engineer. */ "Delete database" = "Adatbázis törlése"; +/* No comment provided by engineer. */ +"Delete database from this device" = "Adatbázis törlése erről az eszközről"; + /* server test step */ "Delete file" = "Fájl törlése"; /* No comment provided by engineer. */ -"Delete files and media?" = "Fájlok és a médiatartalmak törlése?"; +"Delete files and media?" = "Törli a fájl- és a médiatartalmakat?"; /* No comment provided by engineer. */ -"Delete files for all chat profiles" = "Fájlok törlése minden csevegési profilból"; +"Delete files for all chat profiles" = "Fájlok törlése az összes csevegési profilból"; /* chat feature */ -"Delete for everyone" = "Törlés mindenkinél"; +"Delete for everyone" = "Törlés az összes tagnál"; /* No comment provided by engineer. */ -"Delete for me" = "Törlés nálam"; +"Delete for me" = "Csak nálam"; /* No comment provided by engineer. */ "Delete group" = "Csoport törlése"; /* No comment provided by engineer. */ -"Delete group?" = "Csoport törlése?"; +"Delete group?" = "Törli a csoportot?"; /* No comment provided by engineer. */ "Delete invitation" = "Meghívó törlése"; /* No comment provided by engineer. */ -"Delete link" = "Hivatkozás törlése"; +"Delete link" = "Törlés"; /* No comment provided by engineer. */ -"Delete link?" = "Hivatkozás törlése?"; +"Delete link?" = "Törli a hivatkozást?"; + +/* alert title */ +"Delete list?" = "Törli a listát?"; /* No comment provided by engineer. */ -"Delete member message?" = "Csoporttag üzenet törlése?"; +"Delete member message?" = "Törli a tag üzenetét?"; /* No comment provided by engineer. */ -"Delete message?" = "Üzenet törlése?"; +"Delete message?" = "Törli az üzenetet?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "Üzenetek törlése"; /* No comment provided by engineer. */ -"Delete messages after" = "Üzenetek törlése miután"; +"Delete messages after" = "Üzenetek törlése ennyi idő után"; /* No comment provided by engineer. */ "Delete old database" = "Régi adatbázis törlése"; /* No comment provided by engineer. */ -"Delete old database?" = "Régi adatbázis törlése?"; +"Delete old database?" = "Törli a régi adatbázist?"; /* No comment provided by engineer. */ -"Delete pending connection" = "Függőben lévő kapcsolat törlése"; +"Delete or moderate up to 200 messages." = "Legfeljebb 200 üzenet egyszerre való törlése, vagy moderálása."; /* No comment provided by engineer. */ -"Delete pending connection?" = "Függő kapcsolatfelvételi kérések törlése?"; +"Delete pending connection?" = "Törli a függőben lévő meghívót?"; /* No comment provided by engineer. */ "Delete profile" = "Profil törlése"; /* server test step */ -"Delete queue" = "Várólista törlése"; +"Delete queue" = "Sorba állítás törlése"; /* No comment provided by engineer. */ -"Delete user profile?" = "Felhasználói profil törlése?"; +"Delete report" = "Jelentés törlése"; + +/* No comment provided by engineer. */ +"Delete up to 20 messages at once." = "Legfeljebb 20 üzenet egyszerre való törlése."; + +/* No comment provided by engineer. */ +"Delete user profile?" = "Törli a felhasználói profilt?"; + +/* No comment provided by engineer. */ +"Delete without notification" = "Törlés értesítés nélkül"; /* deleted chat item */ "deleted" = "törölve"; /* No comment provided by engineer. */ -"Deleted at" = "Törölve ekkor"; +"Deleted" = "Törölve"; + +/* No comment provided by engineer. */ +"Deleted at" = "Törölve"; /* copied message info */ -"Deleted at: %@" = "Törölve ekkor: %@"; +"Deleted at: %@" = "Törölve: %@"; /* rcv direct event chat item */ -"deleted contact" = "törölt ismerős"; +"deleted contact" = "törölt partner"; /* rcv group event chat item */ "deleted group" = "törölt csoport"; +/* No comment provided by engineer. */ +"Deletion errors" = "Törlési hibák"; + +/* No comment provided by engineer. */ +"Delivered even when Apple drops them." = "Kézbesítés akkor is, amikor az Apple eldobja őket."; + /* No comment provided by engineer. */ "Delivery" = "Kézbesítés"; /* No comment provided by engineer. */ -"Delivery receipts are disabled!" = "Kézbesítési igazolások kikapcsolva!"; +"Delivery receipts are disabled!" = "A kézbesítési jelentések le vannak tiltva!"; /* No comment provided by engineer. */ -"Delivery receipts!" = "Kézbesítési igazolások!"; +"Delivery receipts!" = "Kézbesítési jelentések!"; /* No comment provided by engineer. */ "Description" = "Leírás"; /* No comment provided by engineer. */ -"Desktop address" = "Számítógép azonosítója"; +"Desktop address" = "Számítógép címe"; /* No comment provided by engineer. */ -"Desktop app version %@ is not compatible with this app." = "Az asztali kliens verziója %@ nem kompatibilis ezzel az alkalmazással."; +"Desktop app version %@ is not compatible with this app." = "A számítógép-alkalmazás verziója (%@) nem kompatibilis ezzel az alkalmazással."; /* No comment provided by engineer. */ "Desktop devices" = "Számítógépek"; +/* No comment provided by engineer. */ +"Destination server address of %@ is incompatible with forwarding server %@ settings." = "A(z) %@ célkiszolgáló címe nem kompatibilis a(z) %@ továbbítókiszolgáló beállításaival."; + +/* snd error text */ +"Destination server error: %@" = "Célkiszolgáló-hiba: %@"; + +/* No comment provided by engineer. */ +"Destination server version of %@ is incompatible with forwarding server %@." = "A(z) %@ célkiszolgáló verziója nem kompatibilis a(z) %@ továbbítókiszolgálóval."; + +/* No comment provided by engineer. */ +"Detailed statistics" = "Részletes statisztikák"; + +/* No comment provided by engineer. */ +"Details" = "További részletek"; + /* No comment provided by engineer. */ "Develop" = "Fejlesztés"; +/* No comment provided by engineer. */ +"Developer options" = "Fejlesztői beállítások"; + /* No comment provided by engineer. */ "Developer tools" = "Fejlesztői eszközök"; @@ -1264,16 +1768,16 @@ "Device" = "Eszköz"; /* No comment provided by engineer. */ -"Device authentication is disabled. Turning off SimpleX Lock." = "Eszközhitelesítés kikapcsolva. SimpleX zárolás kikapcsolása."; +"Device authentication is disabled. Turning off SimpleX Lock." = "Az eszközön nincs beállítva a képernyőzár. A SimpleX-zár ki van kapcsolva."; /* No comment provided by engineer. */ -"Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication." = "Eszközhitelesítés nem engedélyezett.A SimpleX zárolás bekapcsolható a Beállításokon keresztül, miután az eszköz hitelesítés engedélyezésre került."; +"Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication." = "Az eszközön nincs beállítva a képernyőzár. A SimpleX-zár az „Adatvédelem és biztonság” menüben kapcsolható be, miután beállította a képernyőzárat az eszközén."; /* No comment provided by engineer. */ -"different migration in the app/database: %@ / %@" = "különböző migrációk az alkalmazásban/adatbázisban: %@ / %@"; +"different migration in the app/database: %@ / %@" = "különböző átköltöztetés az alkalmazásban/adatbázisban: %@ / %@"; /* No comment provided by engineer. */ -"Different names, avatars and transport isolation." = "Különböző nevek, avatarok és átviteli izoláció."; +"Different names, avatars and transport isolation." = "Különböző nevek, profilképek és átvitel-izoláció."; /* connection level description */ "direct" = "közvetlen"; @@ -1282,20 +1786,32 @@ "Direct messages" = "Közvetlen üzenetek"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "Ebben a csoportban tiltott a tagok közötti közvetlen üzenetek küldése."; +"Direct messages between members are prohibited in this chat." = "A tagok közötti közvetlen üzenetek le vannak tiltva ebben a csevegésben."; + +/* No comment provided by engineer. */ +"Direct messages between members are prohibited." = "A tagok közötti közvetlen üzenetek le vannak tiltva."; /* No comment provided by engineer. */ "Disable (keep overrides)" = "Letiltás (felülírások megtartásával)"; +/* alert title */ +"Disable automatic message deletion?" = "Letiltja az automatikus üzenettörlést?"; + +/* alert button */ +"Disable delete messages" = "Üzenetek törlésének letiltása"; + /* No comment provided by engineer. */ -"Disable for all" = "Letiltás mindenki számára"; +"Disable for all" = "Letiltás"; /* authentication reason */ -"Disable SimpleX Lock" = "SimpleX zárolás kikapcsolása"; +"Disable SimpleX Lock" = "SimpleX-zár kikapcsolása"; /* No comment provided by engineer. */ "disabled" = "letiltva"; +/* No comment provided by engineer. */ +"Disabled" = "Letiltva"; + /* No comment provided by engineer. */ "Disappearing message" = "Eltűnő üzenet"; @@ -1303,58 +1819,104 @@ "Disappearing messages" = "Eltűnő üzenetek"; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this chat." = "Az eltűnő üzenetek le vannak tiltva ebben a csevegésben."; +"Disappearing messages are prohibited in this chat." = "Az eltűnő üzenetek küldése le van tiltva ebben a csevegésben."; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "Az eltűnő üzenetek küldése le van tiltva ebben a csoportban."; +"Disappearing messages are prohibited." = "Az eltűnő üzenetek küldése le van tiltva."; /* No comment provided by engineer. */ -"Disappears at" = "Eltűnik ekkor"; +"Disappears at" = "Eltűnik"; /* copied message info */ -"Disappears at: %@" = "Eltűnik ekkor: %@"; +"Disappears at: %@" = "Eltűnik: %@"; /* server test step */ "Disconnect" = "Kapcsolat bontása"; /* No comment provided by engineer. */ -"Disconnect desktop?" = "Számítógép leválasztása?"; +"Disconnect desktop?" = "Leválasztja a számítógépet?"; /* No comment provided by engineer. */ -"Discover and join groups" = "Helyi csoportok felfedezése és csatlakozás"; +"Discover and join groups" = "Csoportok felfedezése és csatlakozás"; /* No comment provided by engineer. */ "Discover via local network" = "Felfedezés helyi hálózaton keresztül"; /* No comment provided by engineer. */ -"Do it later" = "Későbbre halaszt"; +"Do it later" = "Befejezés később"; /* No comment provided by engineer. */ -"Do not send history to new members." = "Ne küldjön előzményeket új tagok részére."; +"Do not send history to new members." = "Az előzmények ne legyenek elküldve az új tagok számára."; /* No comment provided by engineer. */ -"Do NOT use SimpleX for emergency calls." = "NE használja a SimpleX-et segélyhívásokhoz."; +"Do NOT send messages directly, even if your or destination server does not support private routing." = "NE küldjön üzeneteket közvetlenül, még akkor sem, ha a saját kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást."; /* No comment provided by engineer. */ -"Don't create address" = "Ne hozzon létre azonosítót"; +"Do not use credentials with proxy." = "Ne használja a hitelesítőadatokat proxyval."; + +/* No comment provided by engineer. */ +"Do NOT use private routing." = "NE használjon privát útválasztást."; + +/* No comment provided by engineer. */ +"Do NOT use SimpleX for emergency calls." = "NE használja a SimpleXet segélyhívásokhoz."; + +/* No comment provided by engineer. */ +"Documents:" = "Dokumentumok:"; + +/* No comment provided by engineer. */ +"Don't create address" = "Ne hozzon létre címet"; /* No comment provided by engineer. */ "Don't enable" = "Ne engedélyezze"; +/* No comment provided by engineer. */ +"Don't miss important messages." = "Ne maradjon le a fontos üzenetekről."; + /* No comment provided by engineer. */ "Don't show again" = "Ne mutasd újra"; /* No comment provided by engineer. */ -"Downgrade and open chat" = "Visszatérés a korábbi verzióra és a csevegés megnyitása"; +"Done" = "Kész"; + +/* No comment provided by engineer. */ +"Downgrade and open chat" = "Visszafejlesztés és a csevegés megnyitása"; + +/* alert button +chat item action */ +"Download" = "Letöltés"; + +/* No comment provided by engineer. */ +"Download errors" = "Letöltési hibák"; + +/* No comment provided by engineer. */ +"Download failed" = "Sikertelen letöltés"; /* server test step */ "Download file" = "Fájl letöltése"; +/* alert action */ +"Download files" = "Fájlok letöltése"; + /* No comment provided by engineer. */ -"Duplicate display name!" = "Duplikált megjelenítési név!"; +"Downloaded" = "Letöltve"; + +/* No comment provided by engineer. */ +"Downloaded files" = "Letöltött fájlok"; + +/* No comment provided by engineer. */ +"Downloading archive" = "Archívum letöltése"; + +/* No comment provided by engineer. */ +"Downloading link details" = "Letöltési hivatkozás részletei"; + +/* No comment provided by engineer. */ +"Duplicate display name!" = "Duplikált megjelenítendő név!"; /* integrity error chat item */ -"duplicate message" = "duplikálódott üzenet"; +"duplicate message" = "duplikált üzenet"; + +/* No comment provided by engineer. */ +"duplicates" = "duplikációk"; /* No comment provided by engineer. */ "Duration" = "Időtartam"; @@ -1362,11 +1924,14 @@ /* No comment provided by engineer. */ "e2e encrypted" = "e2e titkosított"; +/* No comment provided by engineer. */ +"E2E encrypted notifications." = "Végpontok közötti titkosított értesítések."; + /* chat item action */ "Edit" = "Szerkesztés"; /* No comment provided by engineer. */ -"Edit group profile" = "A csoport profiljának szerkesztése"; +"Edit group profile" = "Csoportprofil szerkesztése"; /* No comment provided by engineer. */ "Enable" = "Engedélyezés"; @@ -1374,17 +1939,23 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "Engedélyezés (felülírások megtartásával)"; -/* No comment provided by engineer. */ -"Enable automatic message deletion?" = "Automatikus üzenet törlés engedélyezése?"; +/* alert title */ +"Enable automatic message deletion?" = "Engedélyezi az automatikus üzenettörlést?"; /* No comment provided by engineer. */ "Enable camera access" = "Kamera hozzáférés engedélyezése"; /* No comment provided by engineer. */ -"Enable for all" = "Engedélyezés mindenki részére"; +"Enable Flux in Network & servers settings for better metadata privacy." = "A Flux kiszolgálókat engedélyezheti a beállításokban, a „Hálózat és kiszolgálók” menüben, a metaadatok jobb védelme érdekében."; /* No comment provided by engineer. */ -"Enable instant notifications?" = "Azonnali értesítések engedélyezése?"; +"Enable for all" = "Engedélyezés az összes tag számára"; + +/* No comment provided by engineer. */ +"Enable in direct chats (BETA)!" = "Engedélyezés a közvetlen csevegésekben (BÉTA)!"; + +/* No comment provided by engineer. */ +"Enable instant notifications?" = "Engedélyezi az azonnali értesítéseket?"; /* No comment provided by engineer. */ "Enable lock" = "Zárolás engedélyezése"; @@ -1393,40 +1964,46 @@ "Enable notifications" = "Értesítések engedélyezése"; /* No comment provided by engineer. */ -"Enable periodic notifications?" = "Időszakos értesítések engedélyezése?"; +"Enable periodic notifications?" = "Engedélyezi az időszakos értesítéseket?"; /* No comment provided by engineer. */ "Enable self-destruct" = "Önmegsemmisítés engedélyezése"; /* set passcode view */ -"Enable self-destruct passcode" = "Önmegsemmisítő jelkód engedélyezése"; +"Enable self-destruct passcode" = "Önmegsemmisítő-jelkód engedélyezése"; /* authentication reason */ -"Enable SimpleX Lock" = "SimpleX zárolás engedélyezése"; +"Enable SimpleX Lock" = "SimpleX-zár bekapcsolása"; /* No comment provided by engineer. */ -"Enable TCP keep-alive" = "TCP életben tartásának engedélyezése"; +"Enable TCP keep-alive" = "TCP életben tartása"; /* enabled status */ "enabled" = "engedélyezve"; -/* enabled status */ -"enabled for contact" = "engedélyezve ismerős részére"; +/* No comment provided by engineer. */ +"Enabled" = "Engedélyezve"; + +/* No comment provided by engineer. */ +"Enabled for" = "Számukra engedélyezve"; /* enabled status */ -"enabled for you" = "engedélyezve az ön számára"; +"enabled for contact" = "engedélyezve a partner számára"; + +/* enabled status */ +"enabled for you" = "engedélyezve az Ön számára"; /* No comment provided by engineer. */ "Encrypt" = "Titkosít"; /* No comment provided by engineer. */ -"Encrypt database?" = "Adatbázis titkosítása?"; +"Encrypt database?" = "Titkosítja az adatbázist?"; /* No comment provided by engineer. */ "Encrypt local files" = "Helyi fájlok titkosítása"; /* No comment provided by engineer. */ -"Encrypt stored files & media" = "Tárolt fájlok és médiatartalmak titkosítása"; +"Encrypt stored files & media" = "A tárolt fájlok- és a médiatartalmak titkosítása"; /* No comment provided by engineer. */ "Encrypted database" = "Titkosított adatbázis"; @@ -1435,16 +2012,16 @@ "Encrypted message or another event" = "Titkosított üzenet vagy más esemény"; /* notification */ -"Encrypted message: app is stopped" = "Titkosított üzenet: az alkalmazás leállt"; +"Encrypted message: app is stopped" = "Titkosított üzenet: az alkalmazás megállt"; /* notification */ -"Encrypted message: database error" = "Titkosított üzenet: adatbázis hiba"; +"Encrypted message: database error" = "Titkosított üzenet: adatbázishiba"; /* notification */ -"Encrypted message: database migration error" = "Titkosított üzenet: adatbázis-migrációs hiba"; +"Encrypted message: database migration error" = "Titkosított üzenet: adatbázis-átköltöztetési hiba"; /* notification */ -"Encrypted message: keychain error" = "Titkosított üzenet: kulcstároló hiba"; +"Encrypted message: keychain error" = "Titkosított üzenet: kulcstartó hiba"; /* notification */ "Encrypted message: no passphrase" = "Titkosított üzenet: nincs jelmondat"; @@ -1453,7 +2030,7 @@ "Encrypted message: unexpected error" = "Titkosított üzenet: váratlan hiba"; /* chat item text */ -"encryption agreed" = "titkosítás egyeztetve"; +"encryption agreed" = "titkosítás elfogadva"; /* chat item text */ "encryption agreed for %@" = "titkosítás elfogadva %@ számára"; @@ -1462,25 +2039,28 @@ "encryption ok" = "titkosítás rendben"; /* chat item text */ -"encryption ok for %@" = "titkosítás rendben vele: %@"; +"encryption ok for %@" = "titkosítás rendben %@ számára"; /* chat item text */ -"encryption re-negotiation allowed" = "titkosítás újraegyeztetés engedélyezve"; +"encryption re-negotiation allowed" = "a titkosítás újraegyeztetése engedélyezve van"; /* chat item text */ -"encryption re-negotiation allowed for %@" = "titkosítás újraegyeztetés engedélyezve vele: %@"; +"encryption re-negotiation allowed for %@" = "a titkosítás újraegyeztetése engedélyezve van %@ számára"; /* message decrypt error item */ -"Encryption re-negotiation error" = "Titkosítás újraegyeztetési hiba"; +"Encryption re-negotiation error" = "Hiba történt a titkosítás újraegyeztetésekor"; /* No comment provided by engineer. */ -"Encryption re-negotiation failed." = "Titkosítás újraegyeztetése sikertelen."; +"Encryption re-negotiation failed." = "Nem sikerült a titkosítást újraegyeztetni."; /* chat item text */ -"encryption re-negotiation required" = "titkosítás újraegyeztetés szükséges"; +"encryption re-negotiation required" = "a titkosítás újraegyeztetése szükséges"; /* chat item text */ -"encryption re-negotiation required for %@" = "titkosítás újraegyeztetés szükséges %@ számára"; +"encryption re-negotiation required for %@" = "a titkosítás újraegyeztetése szükséges %@ számára"; + +/* No comment provided by engineer. */ +"Encryption renegotiation in progress." = "A titkosítás újraegyeztetése folyamatban van."; /* No comment provided by engineer. */ "ended" = "befejeződött"; @@ -1489,34 +2069,37 @@ "ended call %@" = "%@ hívása befejeződött"; /* No comment provided by engineer. */ -"Enter correct passphrase." = "Helyes jelmondat bevitele."; +"Enter correct passphrase." = "Adja meg a helyes jelmondatot."; /* No comment provided by engineer. */ -"Enter group name…" = "Csoportnév megadása…"; +"Enter group name…" = "Adja meg a csoport nevét…"; /* No comment provided by engineer. */ -"Enter Passcode" = "Jelkód megadása"; +"Enter Passcode" = "Adja meg a jelkódot"; /* No comment provided by engineer. */ -"Enter passphrase…" = "Jelmondat megadása…"; +"Enter passphrase" = "Adja meg a jelmondatot"; /* No comment provided by engineer. */ -"Enter password above to show!" = "Jelszó megadása a megjelenítéshez!"; +"Enter passphrase…" = "Adja meg a jelmondatot…"; /* No comment provided by engineer. */ -"Enter server manually" = "Kiszolgáló megadása kézzel"; +"Enter password above to show!" = "Adja meg a jelszót fentebb a megjelenítéshez!"; /* No comment provided by engineer. */ -"Enter this device name…" = "Eszköznév megadása…"; +"Enter server manually" = "Adja meg a kiszolgálót kézzel"; + +/* No comment provided by engineer. */ +"Enter this device name…" = "Adja meg ennek az eszköznek a nevét…"; /* placeholder */ -"Enter welcome message…" = "Üdvözlő üzenetet megadása…"; +"Enter welcome message…" = "Adja meg az üdvözlőüzenetet…"; /* placeholder */ -"Enter welcome message… (optional)" = "Üdvözlő üzenetet megadása… (opcionális)"; +"Enter welcome message… (optional)" = "Adja meg az üdvözlőüzenetet… (nem kötelező)"; /* No comment provided by engineer. */ -"Enter your name…" = "Adja meg nevét…"; +"Enter your name…" = "Adjon meg egy nevet…"; /* No comment provided by engineer. */ "error" = "hiba"; @@ -1525,193 +2108,273 @@ "Error" = "Hiba"; /* No comment provided by engineer. */ -"Error aborting address change" = "Hiba az azonosító megváltoztatásának megszakításakor"; +"Error aborting address change" = "Hiba történt a cím módosításának megszakításakor"; + +/* alert title */ +"Error accepting conditions" = "Hiba történt a feltételek elfogadásakor"; /* No comment provided by engineer. */ -"Error accepting contact request" = "Hiba történt a kapcsolatfelvételi kérelem elfogadásakor"; +"Error accepting contact request" = "Hiba történt a meghívási kérés elfogadásakor"; /* No comment provided by engineer. */ -"Error accessing database file" = "Hiba az adatbázisfájl elérésekor"; +"Error adding member(s)" = "Hiba történt a tag(ok) hozzáadásakor"; + +/* alert title */ +"Error adding server" = "Hiba történt a kiszolgáló hozzáadásakor"; /* No comment provided by engineer. */ -"Error adding member(s)" = "Hiba a tag(-ok) hozzáadásakor"; +"Error changing address" = "Hiba történt a cím módosításakor"; /* No comment provided by engineer. */ -"Error changing address" = "Hiba az azonosító megváltoztatásakor"; +"Error changing connection profile" = "Hiba történt a kapcsolati profilra való váltáskor"; /* No comment provided by engineer. */ -"Error changing role" = "Hiba a szerepkör megváltoztatásakor"; +"Error changing role" = "Hiba történt a szerepkör módosításakor"; /* No comment provided by engineer. */ -"Error changing setting" = "Hiba a beállítás megváltoztatásakor"; +"Error changing setting" = "Hiba történt a beállítás módosításakor"; /* No comment provided by engineer. */ -"Error creating address" = "Hiba az azonosító létrehozásakor"; +"Error changing to incognito!" = "Hiba történt az inkognitóprofilra való váltáskor!"; /* No comment provided by engineer. */ -"Error creating group" = "Hiba a csoport létrehozásakor"; +"Error checking token status" = "Hiba történt a token állapotának ellenőrzésekor"; /* No comment provided by engineer. */ -"Error creating group link" = "Hiba a csoport hivatkozásának létrehozásakor"; +"Error connecting to forwarding server %@. Please try later." = "Hiba történt a(z) %@ továbbítókiszolgálóhoz való kapcsolódáskor. Próbálja meg később."; /* No comment provided by engineer. */ -"Error creating member contact" = "Hiba az ismerőssel történő kapcsolat létrehozásában"; +"Error creating address" = "Hiba történt a cím létrehozásakor"; /* No comment provided by engineer. */ -"Error creating message" = "Hiba az üzenet létrehozásakor"; +"Error creating group" = "Hiba történt a csoport létrehozásakor"; /* No comment provided by engineer. */ -"Error creating profile!" = "Hiba a profil létrehozásakor!"; +"Error creating group link" = "Hiba történt a csoporthivatkozás létrehozásakor"; + +/* alert title */ +"Error creating list" = "Hiba történt a lista létrehozásakor"; /* No comment provided by engineer. */ -"Error decrypting file" = "Hiba a fájl visszafejtésekor"; +"Error creating member contact" = "Hiba történt a partnerrel történő kapcsolat létrehozásában"; /* No comment provided by engineer. */ -"Error deleting chat database" = "Hiba a csevegési adatbázis törlésekor"; +"Error creating message" = "Hiba történt az üzenet létrehozásakor"; /* No comment provided by engineer. */ -"Error deleting chat!" = "Hiba a csevegés törlésekor!"; +"Error creating profile!" = "Hiba történt a profil létrehozásakor!"; /* No comment provided by engineer. */ -"Error deleting connection" = "Hiba a kapcsolat törlésekor"; +"Error creating report" = "Hiba történt a jelentés létrehozásakor"; /* No comment provided by engineer. */ -"Error deleting contact" = "Hiba az ismerős törlésekor"; +"Error decrypting file" = "Hiba történt a fájl visszafejtésekor"; /* No comment provided by engineer. */ -"Error deleting database" = "Hiba az adatbázis törlésekor"; +"Error deleting chat database" = "Hiba történt a csevegési adatbázis törlésekor"; /* No comment provided by engineer. */ -"Error deleting old database" = "Hiba a régi adatbázis törlésekor"; +"Error deleting chat!" = "Hiba történt a csevegés törlésekor!"; /* No comment provided by engineer. */ -"Error deleting token" = "Hiba a token törlésekor"; +"Error deleting connection" = "Hiba történt a kapcsolat törlésekor"; /* No comment provided by engineer. */ -"Error deleting user profile" = "Hiba a felhasználói profil törlésekor"; +"Error deleting database" = "Hiba történt az adatbázis törlésekor"; /* No comment provided by engineer. */ -"Error enabling delivery receipts!" = "Hiba a kézbesítési jelentések engedélyezésekor!"; +"Error deleting old database" = "Hiba történt a régi adatbázis törlésekor"; /* No comment provided by engineer. */ -"Error enabling notifications" = "Hiba az értesítések engedélyezésekor"; +"Error deleting token" = "Hiba történt a token törlésekor"; /* No comment provided by engineer. */ -"Error encrypting database" = "Hiba az adatbázis titkosításakor"; +"Error deleting user profile" = "Hiba történt a felhasználó-profil törlésekor"; /* No comment provided by engineer. */ -"Error exporting chat database" = "Hiba a csevegési adatbázis exportálásakor"; +"Error downloading the archive" = "Hiba történt az archívum letöltésekor"; /* No comment provided by engineer. */ -"Error importing chat database" = "Hiba a csevegési adatbázis importálásakor"; +"Error enabling delivery receipts!" = "Hiba történt a kézbesítési jelentések engedélyezésekor!"; /* No comment provided by engineer. */ -"Error joining group" = "Hiba a csoporthoz való csatlakozáskor"; +"Error enabling notifications" = "Hiba történt az értesítések engedélyezésekor"; /* No comment provided by engineer. */ -"Error loading %@ servers" = "Hiba a %@ kiszolgálók betöltésekor"; +"Error encrypting database" = "Hiba történt az adatbázis titkosításakor"; /* No comment provided by engineer. */ -"Error opening chat" = "Hiba a csevegés megnyitásakor"; +"Error exporting chat database" = "Hiba történt a csevegési adatbázis exportálásakor"; /* No comment provided by engineer. */ -"Error receiving file" = "Hiba a fájl fogadásakor"; +"Error exporting theme: %@" = "Hiba történt a téma exportálásakor: %@"; /* No comment provided by engineer. */ -"Error removing member" = "Hiba a tag eltávolításakor"; +"Error importing chat database" = "Hiba történt a csevegési adatbázis importálásakor"; /* No comment provided by engineer. */ -"Error saving %@ servers" = "Hiba történt a %@ kiszolgálók mentése közben"; +"Error joining group" = "Hiba történt a csoporthoz való csatlakozáskor"; + +/* alert title */ +"Error loading servers" = "Hiba történt a kiszolgálók betöltésekor"; /* No comment provided by engineer. */ -"Error saving group profile" = "Hiba a csoport profil mentésekor"; +"Error migrating settings" = "Hiba történt a beállítások átköltöztetésekor"; /* No comment provided by engineer. */ -"Error saving ICE servers" = "Hiba az ICE kiszolgálók mentésekor"; +"Error opening chat" = "Hiba történt a csevegés megnyitásakor"; + +/* alert title */ +"Error receiving file" = "Hiba történt a fájl fogadásakor"; /* No comment provided by engineer. */ -"Error saving passcode" = "Hiba a jelkód mentése közben"; +"Error reconnecting server" = "Hiba történt a kiszolgálóhoz való újrakapcsolódáskor"; /* No comment provided by engineer. */ -"Error saving passphrase to keychain" = "Hiba a jelmondat kulcstárolóba történő mentésekor"; +"Error reconnecting servers" = "Hiba történt a kiszolgálókhoz való újrakapcsolódáskor"; + +/* alert title */ +"Error registering for notifications" = "Hiba történt az értesítések regisztrálásakor"; /* No comment provided by engineer. */ -"Error saving user password" = "Hiba a felhasználó jelszavának mentésekor"; +"Error removing member" = "Hiba történt a tag eltávolításakor"; + +/* alert title */ +"Error reordering lists" = "Hiba történt a listák újrarendezésekor"; /* No comment provided by engineer. */ -"Error scanning code: %@" = "Hiba a kód beolvasása közben: %@"; +"Error resetting statistics" = "Hiba történt a statisztikák visszaállításakor"; + +/* alert title */ +"Error saving chat list" = "Hiba történt a csevegési lista mentésekor"; /* No comment provided by engineer. */ -"Error sending email" = "Hiba az e-mail küldésekor"; +"Error saving group profile" = "Hiba történt a csoportprofil mentésekor"; + +/* No comment provided by engineer. */ +"Error saving ICE servers" = "Hiba történt az ICE-kiszolgálók mentésekor"; + +/* No comment provided by engineer. */ +"Error saving passcode" = "Hiba történt a jelkód mentésekor"; + +/* No comment provided by engineer. */ +"Error saving passphrase to keychain" = "Hiba történt a jelmondat kulcstartóba történő mentésekor"; + +/* alert title */ +"Error saving servers" = "Hiba történt a kiszolgálók mentésekor"; + +/* when migrating */ +"Error saving settings" = "Hiba történt a beállítások mentésekor"; + +/* No comment provided by engineer. */ +"Error saving user password" = "Hiba történt a felhasználó jelszavának mentésekor"; + +/* No comment provided by engineer. */ +"Error scanning code: %@" = "Hiba történt a kód beolvasásakor: %@"; + +/* No comment provided by engineer. */ +"Error sending email" = "Hiba történt az e-mail elküldésekor"; /* No comment provided by engineer. */ "Error sending member contact invitation" = "Hiba történt a tag kapcsolatfelvételi meghívójának elküldésekor"; /* No comment provided by engineer. */ -"Error sending message" = "Hiba az üzenet küldésekor"; +"Error sending message" = "Hiba történt az üzenet elküldésekor"; /* No comment provided by engineer. */ -"Error setting delivery receipts!" = "Hiba történt a kézbesítési igazolások beállításakor!"; +"Error setting delivery receipts!" = "Hiba történt a kézbesítési jelentések beállításakor!"; /* No comment provided by engineer. */ -"Error starting chat" = "Hiba a csevegés elindításakor"; +"Error starting chat" = "Hiba történt a csevegés elindításakor"; /* No comment provided by engineer. */ -"Error stopping chat" = "Hiba a csevegés megállításakor"; +"Error stopping chat" = "Hiba történt a csevegés megállításakor"; /* No comment provided by engineer. */ -"Error switching profile!" = "Hiba a profil váltásakor!"; +"Error switching profile" = "Hiba történt a profilváltáskor"; + +/* alertTitle */ +"Error switching profile!" = "Hiba történt a profilváltáskor!"; /* No comment provided by engineer. */ -"Error synchronizing connection" = "Hiba a kapcsolat szinkronizálása során"; +"Error synchronizing connection" = "Hiba történt a kapcsolat szinkronizálásakor"; /* No comment provided by engineer. */ -"Error updating group link" = "Hiba a csoport hivatkozás frissítésekor"; +"Error testing server connection" = "Hiba történt a kiszolgáló kapcsolatának tesztelésekor"; /* No comment provided by engineer. */ -"Error updating message" = "Hiba az üzenet frissítésekor"; +"Error updating group link" = "Hiba történt a csoporthivatkozás frissítésekor"; + +/* No comment provided by engineer. */ +"Error updating message" = "Hiba történt az üzenet frissítésekor"; + +/* alert title */ +"Error updating server" = "Hiba történt a kiszolgáló frissítésekor"; /* No comment provided by engineer. */ "Error updating settings" = "Hiba történt a beállítások frissítésekor"; /* No comment provided by engineer. */ -"Error updating user privacy" = "Hiba a felhasználói beállítások frissítésekor"; +"Error updating user privacy" = "Hiba történt a felhasználói adatvédelem frissítésekor"; + +/* No comment provided by engineer. */ +"Error uploading the archive" = "Hiba történt az archívum feltöltésekor"; + +/* No comment provided by engineer. */ +"Error verifying passphrase:" = "Hiba történt a jelmondat hitelesítésekor:"; /* No comment provided by engineer. */ "Error: " = "Hiba: "; -/* No comment provided by engineer. */ +/* alert message +file error text +snd error text */ "Error: %@" = "Hiba: %@"; /* No comment provided by engineer. */ -"Error: no database file" = "Hiba: nincs adatbázis fájl"; +"Error: no database file" = "Hiba: nincs adatbázisfájl"; /* No comment provided by engineer. */ -"Error: URL is invalid" = "Hiba: az URL érvénytelen"; +"Error: URL is invalid" = "Hiba: a webcím érvénytelen"; + +/* No comment provided by engineer. */ +"Errors" = "Hibák"; + +/* servers error */ +"Errors in servers configuration." = "Hibák a kiszolgálók konfigurációjában."; /* No comment provided by engineer. */ "Even when disabled in the conversation." = "Akkor is, ha le van tiltva a beszélgetésben."; -/* No comment provided by engineer. */ -"event happened" = "esemény történt"; - /* No comment provided by engineer. */ "Exit without saving" = "Kilépés mentés nélkül"; /* chat item action */ "Expand" = "Kibontás"; +/* No comment provided by engineer. */ +"expired" = "lejárt"; + +/* token status text */ +"Expired" = "Lejárt"; + /* No comment provided by engineer. */ "Export database" = "Adatbázis exportálása"; /* No comment provided by engineer. */ "Export error:" = "Exportálási hiba:"; +/* No comment provided by engineer. */ +"Export theme" = "Téma exportálása"; + /* No comment provided by engineer. */ "Exported database archive." = "Exportált adatbázis-archívum."; /* No comment provided by engineer. */ -"Exporting database archive…" = "Adatbázis archívum exportálása…"; +"Exported file doesn't exist" = "Az exportált fájl nem létezik"; + +/* No comment provided by engineer. */ +"Exporting database archive…" = "Adatbázis-archívum exportálása…"; /* No comment provided by engineer. */ "Failed to remove passphrase" = "Nem sikerült eltávolítani a jelmondatot"; @@ -1720,38 +2383,80 @@ "Fast and no wait until the sender is online!" = "Gyors és nem kell várni, amíg a feladó online lesz!"; /* No comment provided by engineer. */ -"Faster joining and more reliable messages." = "Gyorsabb csatlakozás és megbízhatóbb üzenet kézbesítés."; +"Faster deletion of groups." = "Gyorsabb csoporttörlés."; /* No comment provided by engineer. */ +"Faster joining and more reliable messages." = "Gyorsabb csatlakozás és megbízhatóbb üzenetkézbesítés."; + +/* No comment provided by engineer. */ +"Faster sending messages." = "Gyorsabb üzenetküldés."; + +/* swipe action */ "Favorite" = "Kedvenc"; /* No comment provided by engineer. */ -"File will be deleted from servers." = "A fájl törölve lesz a kiszolgálóról."; +"Favorites" = "Kedvencek"; + +/* file error alert title */ +"File error" = "Fájlhiba"; + +/* alert message */ +"File errors:\n%@" = "Fájlhiba:\n%@"; + +/* file error text */ +"File is blocked by server operator:\n%@." = "A kiszolgáló üzemeltetője letiltotta a fájlt:\n%@."; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "A fájl nem található – valószínűleg a fájlt törölték vagy visszavonták."; + +/* file error text */ +"File server error: %@" = "Fájlkiszolgáló-hiba: %@"; /* No comment provided by engineer. */ -"File will be received when your contact completes uploading it." = "A fájl akkor érkezik meg, amikor ismerőse befejezte annak feltöltést."; +"File status" = "Fájl állapota"; + +/* copied message info */ +"File status: %@" = "Fájl állapota: %@"; /* No comment provided by engineer. */ -"File will be received when your contact is online, please wait or check later!" = "A fájl akkor érkezik meg, amint ismerőse online lesz, várjon, vagy ellenőrizze később!"; +"File will be deleted from servers." = "A fájl törölve lesz a kiszolgálókról."; + +/* No comment provided by engineer. */ +"File will be received when your contact completes uploading it." = "A fájl akkor érkezik meg, amikor a küldője befejezte annak feltöltését."; + +/* No comment provided by engineer. */ +"File will be received when your contact is online, please wait or check later!" = "A fájl akkor érkezik meg, amikor a küldője elérhető lesz, várjon, vagy ellenőrizze később!"; /* No comment provided by engineer. */ "File: %@" = "Fájl: %@"; /* No comment provided by engineer. */ -"Files & media" = "Fájlok és média"; +"Files" = "Fájlok"; + +/* No comment provided by engineer. */ +"Files & media" = "Fájlok és médiatartalmak"; /* chat feature */ -"Files and media" = "Fájlok és médiatartalom"; +"Files and media" = "Fájlok és médiatartalmak"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "A fájlok- és a médiatartalom küldése le van tiltva ebben a csoportban."; +"Files and media are prohibited." = "A fájlok- és a médiatartalmak küldése le van tiltva."; /* No comment provided by engineer. */ -"Files and media prohibited!" = "A fájlok- és a médiatartalom küldése le van tiltva!"; +"Files and media not allowed" = "A fájlok- és médiatartalmak nincsenek engedélyezve"; + +/* No comment provided by engineer. */ +"Files and media prohibited!" = "A fájlok- és a médiatartalmak küldése le van tiltva!"; /* No comment provided by engineer. */ "Filter unread and favorite chats." = "Olvasatlan és kedvenc csevegésekre való szűrés."; +/* No comment provided by engineer. */ +"Finalize migration" = "Átköltöztetés véglegesítése"; + +/* No comment provided by engineer. */ +"Finalize migration on another device." = "Átköltöztetés véglegesítése egy másik eszközön."; + /* No comment provided by engineer. */ "Finally, we have them! 🚀" = "Végre, megvannak! 🚀"; @@ -1771,14 +2476,77 @@ "Fix encryption after restoring backups." = "Titkosítás javítása az adatmentések helyreállítása után."; /* No comment provided by engineer. */ -"Fix not supported by contact" = "Ismerős általi javítás nem támogatott"; +"Fix not supported by contact" = "Partner általi javítás nem támogatott"; /* No comment provided by engineer. */ "Fix not supported by group member" = "Csoporttag általi javítás nem támogatott"; +/* No comment provided by engineer. */ +"For all moderators" = "Az összes moderátor számára"; + +/* servers error */ +"For chat profile %@:" = "A(z) %@ nevű csevegési profilhoz:"; + /* No comment provided by engineer. */ "For console" = "Konzolhoz"; +/* No comment provided by engineer. */ +"For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server." = "Például, ha a partnere egy SimpleX Chat-kiszolgálón keresztül fogadja az üzeneteket, akkor az Ön alkalmazása egy Flux-kiszolgálón keresztül fogja azokat kézbesíteni."; + +/* No comment provided by engineer. */ +"For me" = "Csak magamnak"; + +/* No comment provided by engineer. */ +"For private routing" = "A privát útválasztáshoz"; + +/* No comment provided by engineer. */ +"For social media" = "A közösségi médiához"; + +/* chat item action */ +"Forward" = "Továbbítás"; + +/* alert title */ +"Forward %d message(s)?" = "Továbbít %d üzenetet?"; + +/* No comment provided by engineer. */ +"Forward and save messages" = "Üzenetek továbbítása és mentése"; + +/* alert action */ +"Forward messages" = "Üzenetek továbbítása"; + +/* alert message */ +"Forward messages without files?" = "Továbbítja az üzeneteket fájlok nélkül?"; + +/* No comment provided by engineer. */ +"Forward up to 20 messages at once." = "Legfeljebb 20 üzenet egyszerre való továbbítása."; + +/* No comment provided by engineer. */ +"forwarded" = "továbbított"; + +/* No comment provided by engineer. */ +"Forwarded" = "Továbbított"; + +/* No comment provided by engineer. */ +"Forwarded from" = "Továbbítva innen"; + +/* No comment provided by engineer. */ +"Forwarding %lld messages" = "%lld üzenet továbbítása"; + +/* No comment provided by engineer. */ +"Forwarding server %@ failed to connect to destination server %@. Please try later." = "A(z) %@ továbbítókiszolgáló nem tudott kapcsolódni a(z) %@ célkiszolgálóhoz. Próbálja meg később."; + +/* No comment provided by engineer. */ +"Forwarding server address is incompatible with network settings: %@." = "A továbbítókiszolgáló címe nem kompatibilis a hálózati beállításokkal: %@."; + +/* No comment provided by engineer. */ +"Forwarding server version is incompatible with network settings: %@." = "A továbbítókiszolgáló verziója nem kompatibilis a hálózati beállításokkal: %@."; + +/* snd error text */ +"Forwarding server: %@\nDestination server error: %@" = "Továbbítókiszolgáló: %1$@\nCélkiszolgáló-hiba: %2$@"; + +/* snd error text */ +"Forwarding server: %@\nError: %@" = "Továbbítókiszolgáló: %1$@\nHiba: %2$@"; + /* No comment provided by engineer. */ "Found desktop" = "Megtalált számítógép"; @@ -1789,23 +2557,29 @@ "Full link" = "Teljes hivatkozás"; /* No comment provided by engineer. */ -"Full name (optional)" = "Teljes név (opcionális)"; +"Full name (optional)" = "Teljes név (nem kötelező)"; /* No comment provided by engineer. */ -"Full name:" = "Teljes név:"; +"Fully decentralized – visible only to members." = "Teljesen decentralizált – csak a tagok számára látható."; /* No comment provided by engineer. */ -"Fully decentralized – visible only to members." = "Teljesen decentralizált - kizárólag tagok számára látható."; +"Fully re-implemented - work in background!" = "Teljesen újra implementálva – háttérben történő működés!"; /* No comment provided by engineer. */ -"Fully re-implemented - work in background!" = "Teljesen újra implementálva - háttérben történő működés!"; +"Further reduced battery usage" = "Tovább csökkentett akkumulátor-használat"; /* No comment provided by engineer. */ -"Further reduced battery usage" = "Tovább csökkentett akkumulátor használat"; +"Get notified when mentioned." = "Kapjon értesítést, ha megemlítik."; /* No comment provided by engineer. */ "GIFs and stickers" = "GIF-ek és matricák"; +/* message preview */ +"Good afternoon!" = "Jó napot!"; + +/* message preview */ +"Good morning!" = "Jó reggelt!"; + /* No comment provided by engineer. */ "Group" = "Csoport"; @@ -1822,85 +2596,73 @@ "Group display name" = "A csoport megjelenített neve"; /* No comment provided by engineer. */ -"Group full name (optional)" = "Csoport teljes neve (opcionális)"; +"Group full name (optional)" = "A csoport teljes neve (nem kötelező)"; /* No comment provided by engineer. */ -"Group image" = "Csoportkép"; +"Group image" = "Csoport profilképe"; /* No comment provided by engineer. */ -"Group invitation" = "Csoportos meghívó"; +"Group invitation" = "Csoportmeghívó"; /* No comment provided by engineer. */ -"Group invitation expired" = "A csoport meghívó lejárt"; +"Group invitation expired" = "A csoportmeghívó lejárt"; /* No comment provided by engineer. */ -"Group invitation is no longer valid, it was removed by sender." = "A csoport meghívó már nem érvényes, el lett távolítva a küldője által."; +"Group invitation is no longer valid, it was removed by sender." = "A csoportmeghívó már nem érvényes, a küldője eltávolította."; /* No comment provided by engineer. */ -"Group link" = "Csoport hivatkozás"; +"Group link" = "Csoporthivatkozás"; /* No comment provided by engineer. */ -"Group links" = "Csoport hivatkozások"; - -/* No comment provided by engineer. */ -"Group members can add message reactions." = "Csoporttagok üzenetreakciókat adhatnak hozzá."; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "Csoporttagok visszafordíthatatlanul törölhetik az elküldött üzeneteket. (24 óra)"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "Csoporttagok küldhetnek közvetlen üzeneteket."; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "Csoporttagok küldhetnek eltűnő üzeneteket."; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "Csoporttagok küldhetnek fájlokat és médiatartalmakat."; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "Csoporttagok küldhetnek hangüzeneteket."; +"Group links" = "Csoporthivatkozások"; /* notification */ "Group message:" = "Csoport üzenet:"; /* No comment provided by engineer. */ -"Group moderation" = "Csoport moderáció"; +"Group moderation" = "Csoport moderálása"; /* No comment provided by engineer. */ -"Group preferences" = "Csoport beállítások"; +"Group preferences" = "Csoportbeállítások"; /* No comment provided by engineer. */ -"Group profile" = "Csoport profil"; +"Group profile" = "Csoportprofil"; /* No comment provided by engineer. */ "Group profile is stored on members' devices, not on the servers." = "A csoport profilja a tagok eszközein tárolódik, nem a kiszolgálókon."; /* snd group event chat item */ -"group profile updated" = "csoport profil frissítve"; +"group profile updated" = "csoportprofil frissítve"; /* No comment provided by engineer. */ -"Group welcome message" = "Csoport üdvözlő üzenete"; +"Group welcome message" = "A csoport üdvözlőüzenete"; /* No comment provided by engineer. */ -"Group will be deleted for all members - this cannot be undone!" = "Csoport törlésre kerül minden tag számára - ez nem vonható vissza!"; +"Group will be deleted for all members - this cannot be undone!" = "A csoport törölve lesz az összes tag számára – ez a művelet nem vonható vissza!"; /* No comment provided by engineer. */ -"Group will be deleted for you - this cannot be undone!" = "A csoport törlésre kerül az ön részére - ez nem vonható vissza!"; +"Group will be deleted for you - this cannot be undone!" = "A csoport törölve lesz az Ön számára – ez a művelet nem vonható vissza!"; /* No comment provided by engineer. */ -"Help" = "Segítség"; +"Groups" = "Csoportok"; /* No comment provided by engineer. */ -"Hidden" = "Rejtett"; +"Help" = "Súgó"; + +/* No comment provided by engineer. */ +"Help admins moderating their groups." = "Segítsen az adminisztrátoroknak a csoportjaik moderálásában."; + +/* No comment provided by engineer. */ +"Hidden" = "Se név, se üzenet"; /* No comment provided by engineer. */ "Hidden chat profiles" = "Rejtett csevegési profilok"; /* No comment provided by engineer. */ -"Hidden profile password" = "Rejtett profil jelszó"; +"Hidden profile password" = "Rejtett profiljelszó"; /* chat item action */ -"Hide" = "Elrejt"; +"Hide" = "Összecsukás"; /* No comment provided by engineer. */ "Hide app screen in the recent apps." = "Alkalmazás képernyőjének elrejtése a gyakran használt alkalmazások között."; @@ -1909,18 +2671,24 @@ "Hide profile" = "Profil elrejtése"; /* No comment provided by engineer. */ -"Hide:" = "Elrejt:"; +"Hide:" = "Elrejtve:"; /* No comment provided by engineer. */ "History" = "Előzmények"; /* No comment provided by engineer. */ -"History is not sent to new members." = "Az előzmények nem kerülnek elküldésre új tagok részére."; +"History is not sent to new members." = "Az előzmények nem lesznek elküldve az új tagok számára."; /* time unit */ "hours" = "óra"; /* No comment provided by engineer. */ +"How it affects privacy" = "Hogyan érinti az adatvédelmet"; + +/* No comment provided by engineer. */ +"How it helps privacy" = "Hogyan segíti az adatvédelmet"; + +/* alert button */ "How it works" = "Hogyan működik"; /* No comment provided by engineer. */ @@ -1930,52 +2698,67 @@ "How to" = "Hogyan"; /* No comment provided by engineer. */ -"How to use it" = "Hogyan használja"; +"How to use it" = "Használati útmutató"; /* No comment provided by engineer. */ -"How to use your servers" = "Kiszolgálók használata"; +"How to use your servers" = "Hogyan használja a saját kiszolgálóit"; + +/* No comment provided by engineer. */ +"Hungarian interface" = "Magyar kezelőfelület"; /* No comment provided by engineer. */ "ICE servers (one per line)" = "ICE-kiszolgálók (soronként egy)"; /* No comment provided by engineer. */ -"If you can't meet in person, show QR code in a video call, or share the link." = "Ha nem tud személyesen találkozni, mutassa meg a QR-kódot egy videohívás során, vagy ossza meg a hivatkozást."; +"If you can't meet in person, show QR code in a video call, or share the link." = "Ha nem tud személyesen találkozni, mutassa meg a QR-kódot egy videohívás közben, vagy ossza meg a hivatkozást."; /* No comment provided by engineer. */ -"If you enter this passcode when opening the app, all app data will be irreversibly removed!" = "Ha az alkalmazás megnyitásakor megadja ezt a jelkódot, az összes alkalmazásadat visszafordíthatatlanul törlődik!"; +"If you enter this passcode when opening the app, all app data will be irreversibly removed!" = "Ha az alkalmazás megnyitásakor megadja ezt a jelkódot, az összes alkalmazásadat véglegesen el lesz távolítva!"; /* No comment provided by engineer. */ -"If you enter your self-destruct passcode while opening the app:" = "Ha az alkalmazás megnyitásakor az önmegsemmisítő jelkódot megadásra kerül:"; +"If you enter your self-destruct passcode while opening the app:" = "Ha az alkalmazás megnyitásakor megadja az önmegsemmisítő-jelkódot:"; /* No comment provided by engineer. */ -"If you need to use the chat now tap **Do it later** below (you will be offered to migrate the database when you restart the app)." = "Ha most kell használnia a csevegést, koppintson a ** Csináld később** elemre (az alkalmazás újraindításakor felajánlásra kerül az adatbázis áttelepítése)."; +"If you need to use the chat now tap **Do it later** below (you will be offered to migrate the database when you restart the app)." = "Ha most kell használnia a csevegést, koppintson alább a **Befejezés később** lehetőségre (az alkalmazás újraindításakor fel lesz ajánlva az adatbázis átköltöztetése)."; /* No comment provided by engineer. */ -"Ignore" = "Figyelmen kívül hagyás"; +"Ignore" = "Mellőzés"; /* No comment provided by engineer. */ -"Image will be received when your contact completes uploading it." = "A kép akkor érkezik meg, amikor ismerőse befejezte annak feltöltését."; +"Image will be received when your contact completes uploading it." = "A kép akkor érkezik meg, amikor a küldője befejezte annak feltöltését."; /* No comment provided by engineer. */ -"Image will be received when your contact is online, please wait or check later!" = "A kép akkor érkezik meg, amikor ismerős elérhető lesz, várjon vagy ellenőrizze később!"; +"Image will be received when your contact is online, please wait or check later!" = "A kép akkor érkezik meg, amikor a küldője elérhető lesz, várjon, vagy ellenőrizze később!"; /* No comment provided by engineer. */ "Immediately" = "Azonnal"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "Spam és visszaélések elleni védelem"; +"Immune to spam" = "Védett a kéretlen tartalommal szemben"; /* No comment provided by engineer. */ "Import" = "Importálás"; /* No comment provided by engineer. */ -"Import chat database?" = "Csevegési adatbázis importálása?"; +"Import chat database?" = "Importálja a csevegési adatbázist?"; /* No comment provided by engineer. */ "Import database" = "Adatbázis importálása"; /* No comment provided by engineer. */ -"Improved message delivery" = "Továbbfejlesztett üzenetküldés"; +"Import failed" = "Sikertelen importálás"; + +/* No comment provided by engineer. */ +"Import theme" = "Téma importálása"; + +/* No comment provided by engineer. */ +"Importing archive" = "Archívum importálása"; + +/* No comment provided by engineer. */ +"Improved delivery, reduced traffic usage.\nMore improvements are coming soon!" = "Továbbfejlesztett kézbesítés, csökkentett adatforgalom-használat.\nTovábbi fejlesztések hamarosan!"; + +/* No comment provided by engineer. */ +"Improved message delivery" = "Továbbfejlesztett üzenetkézbesítés"; /* No comment provided by engineer. */ "Improved privacy and security" = "Fejlesztett adatvédelem és biztonság"; @@ -1984,28 +2767,43 @@ "Improved server configuration" = "Javított kiszolgáló konfiguráció"; /* No comment provided by engineer. */ -"In reply to" = "Válasz neki"; +"In order to continue, chat should be stopped." = "A folytatáshoz a csevegést meg kell szakítani."; + +/* No comment provided by engineer. */ +"In reply to" = "Válaszul erre"; + +/* No comment provided by engineer. */ +"In-call sounds" = "Bejövő hívás csengőhangja"; + +/* No comment provided by engineer. */ +"inactive" = "inaktív"; + +/* report reason */ +"Inappropriate content" = "Kifogásolt tartalom"; + +/* report reason */ +"Inappropriate profile" = "Kifogásolt profil"; /* No comment provided by engineer. */ "Incognito" = "Inkognitó"; /* No comment provided by engineer. */ -"Incognito groups" = "Inkognitó csoportok"; +"Incognito groups" = "Inkognitócsoportok"; /* No comment provided by engineer. */ -"Incognito mode" = "Inkognitó mód"; +"Incognito mode" = "Inkognitómód"; /* No comment provided by engineer. */ -"Incognito mode protects your privacy by using a new random profile for each contact." = "Az inkognitómód védi személyes adatait azáltal, hogy minden ismerőshöz új véletlenszerű profilt használ."; +"Incognito mode protects your privacy by using a new random profile for each contact." = "Az inkognitómód úgy védi a személyes adatait, hogy az összes partneréhez új, véletlenszerű profilt használ."; /* chat list item description */ -"incognito via contact address link" = "inkognitó a kapcsolattartási hivatkozáson keresztül"; +"incognito via contact address link" = "inkognitó a kapcsolattartási címhivatkozáson keresztül"; /* chat list item description */ -"incognito via group link" = "inkognitó a csoportos hivatkozáson keresztül"; +"incognito via group link" = "inkognitó a csoporthivatkozáson keresztül"; /* chat list item description */ -"incognito via one-time link" = "inkognitó egyszer használatos hivatkozáson keresztül"; +"incognito via one-time link" = "inkognitó egy egyszer használható meghívón keresztül"; /* notification */ "Incoming audio call" = "Bejövő hanghívás"; @@ -2017,7 +2815,7 @@ "Incoming video call" = "Bejövő videóhívás"; /* No comment provided by engineer. */ -"Incompatible database version" = "Nem kompatibilis adatbázis verzió"; +"Incompatible database version" = "Nem kompatibilis adatbázis-verzió"; /* No comment provided by engineer. */ "Incompatible version" = "Nem kompatibilis verzió"; @@ -2041,32 +2839,53 @@ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "A [SimpleX Chat terminálhoz] telepítése (https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"Instant push notifications will be hidden!\n" = "Az azonnali push értesítések elrejtésre kerülnek!\n"; +"Instant" = "Azonnali"; /* No comment provided by engineer. */ -"Instantly" = "Azonnal"; +"Instant push notifications will be hidden!\n" = "Az azonnali push-értesítések el lesznek rejtve!\n"; /* No comment provided by engineer. */ -"Interface" = "Felület"; +"Interface" = "Kezelőfelület"; + +/* No comment provided by engineer. */ +"Interface colors" = "Kezelőfelület színei"; + +/* token status text */ +"Invalid" = "Érvénytelen"; + +/* token status text */ +"Invalid (bad token)" = "Érvénytelen (hibás token)"; + +/* token status text */ +"Invalid (expired)" = "Érvénytelen (lejárt)"; + +/* token status text */ +"Invalid (unregistered)" = "Érvénytelen (nincs regisztrálva)"; + +/* token status text */ +"Invalid (wrong topic)" = "Érvénytelen (rossz topic)"; /* invalid chat data */ "invalid chat" = "érvénytelen csevegés"; /* No comment provided by engineer. */ -"invalid chat data" = "érvénytelen csevegés adat"; +"invalid chat data" = "érvénytelen csevegésadat"; /* No comment provided by engineer. */ -"Invalid connection link" = "Érvénytelen kapcsolati hivatkozás"; +"Invalid connection link" = "Érvénytelen kapcsolattartási hivatkozás"; /* invalid chat item */ "invalid data" = "érvénytelen adat"; /* No comment provided by engineer. */ -"Invalid display name!" = "Érvénytelen megjelenítendő felhaszálónév!"; +"Invalid display name!" = "Érvénytelen megjelenítendő név!"; /* No comment provided by engineer. */ "Invalid link" = "Érvénytelen hivatkozás"; +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Érvénytelen átköltöztetési visszaigazolás"; + /* No comment provided by engineer. */ "Invalid name!" = "Érvénytelen név!"; @@ -2076,7 +2895,7 @@ /* No comment provided by engineer. */ "Invalid response" = "Érvénytelen válasz"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "Érvénytelen kiszolgálócím!"; /* item status text */ @@ -2088,53 +2907,65 @@ /* group name */ "invitation to group %@" = "meghívás a(z) %@ csoportba"; +/* No comment provided by engineer. */ +"invite" = "meghívás"; + /* No comment provided by engineer. */ "Invite friends" = "Barátok meghívása"; /* No comment provided by engineer. */ "Invite members" = "Tagok meghívása"; +/* No comment provided by engineer. */ +"Invite to chat" = "Meghívás a csevegésbe"; + /* No comment provided by engineer. */ "Invite to group" = "Meghívás a csoportba"; /* No comment provided by engineer. */ -"invited" = "meghívott"; +"invited" = "meghíva"; /* rcv group event chat item */ -"invited %@" = "%@ meghívott"; +"invited %@" = "meghívta őt: %@"; /* chat list item title */ -"invited to connect" = "meghívott, hogy csatlakozzon"; +"invited to connect" = "Függőben lévő meghívó"; /* rcv group event chat item */ -"invited via your group link" = "meghívott a csoport hivatkozásán keresztül"; +"invited via your group link" = "meghíva a saját csoporthivatkozásán keresztül"; /* No comment provided by engineer. */ -"iOS Keychain is used to securely store passphrase - it allows receiving push notifications." = "Az iOS kulcstár a jelmondat biztonságos tárolására szolgál - lehetővé teszi a push-értesítések fogadását."; +"iOS Keychain is used to securely store passphrase - it allows receiving push notifications." = "Az iOS kulcstartó a jelmondat biztonságos tárolására szolgál – lehetővé teszi a push-értesítések fogadását."; /* No comment provided by engineer. */ -"iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "Az iOS kulcstár az alkalmazás újraindítása, vagy a jelmondat módosítása után a jelmondat biztonságos tárolására szolgál - lehetővé teszi a push-értesítések fogadását."; +"iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "Az iOS kulcstartó biztonságosan fogja tárolni a jelmondatot az alkalmazás újraindítása, vagy a jelmondat módosítása után – lehetővé teszi a push-értesítések fogadását."; /* No comment provided by engineer. */ -"Irreversible message deletion" = "Visszafordíthatatlan üzenettörlés"; +"IP address" = "IP-cím"; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this chat." = "Ebben a csevegésben az üzenetek visszafordíthatatlan törlése le van tiltva."; +"Irreversible message deletion" = "Végleges üzenettörlés"; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "Ebben a csoportban az üzenetek visszafordíthatatlan törlése le van tiltva."; +"Irreversible message deletion is prohibited in this chat." = "Az üzenetek végleges törlése le van tiltva ebben a csevegésben."; /* No comment provided by engineer. */ -"It allows having many anonymous connections without any shared data between them in a single chat profile." = "Lehetővé teszi, hogy egyetlen csevegőprofilon belül több anonim kapcsolat legyen, anélkül, hogy megosztott adatok lennének közöttük."; +"Irreversible message deletion is prohibited." = "Az üzenetek végleges törlése le van tiltva."; /* No comment provided by engineer. */ -"It can happen when you or your connection used the old database backup." = "Ez akkor fordulhat elő, ha ön vagy a kapcsolata régi adatbázis biztonsági mentést használt."; +"It allows having many anonymous connections without any shared data between them in a single chat profile." = "Lehetővé teszi, hogy egyetlen csevegési profilon belül több névtelen kapcsolat legyen, anélkül, hogy megosztott adatok lennének közöttük."; /* No comment provided by engineer. */ -"It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Ez akkor fordulhat elő, ha:\n1. Az üzenetek 2 nap után, vagy a kiszolgálón 30 nap után lejártak.\n2. Az üzenet visszafejtése sikertelen volt, mert vagy az ismerőse régebbi adatbázis biztonsági mentést használt.\n3. A kapcsolat sérült."; +"It can happen when you or your connection used the old database backup." = "Ez akkor fordulhat elő, ha Ön vagy a partnere régi adatbázis biztonsági mentést használt."; /* No comment provided by engineer. */ -"It seems like you are already connected via this link. If it is not the case, there was an error (%@)." = "Úgy tűnik, már csatlakozott ezen a hivatkozáson keresztül. Ha ez nem így van, akkor hiba történt (%@)."; +"It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Ez akkor fordulhat elő, ha:\n1. Az üzenetek 2 nap után, vagy a kiszolgálón 30 nap után lejártak.\n2. Nem sikerült az üzenetet visszafejteni, mert Ön, vagy a partnere régebbi adatbázis biztonsági mentést használt.\n3. A kapcsolat sérült."; + +/* No comment provided by engineer. */ +"It protects your IP address and connections." = "Védi az IP-címét és a kapcsolatait."; + +/* No comment provided by engineer. */ +"It seems like you are already connected via this link. If it is not the case, there was an error (%@)." = "Úgy tűnik, már kapcsolódott ezen a hivatkozáson keresztül. Ha ez nem így van, akkor hiba történt (%@)."; /* No comment provided by engineer. */ "Italian interface" = "Olasz kezelőfelület"; @@ -2145,7 +2976,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "Japán kezelőfelület"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "Csatlakozás"; /* No comment provided by engineer. */ @@ -2167,28 +2998,31 @@ "Join with current profile" = "Csatlakozás a jelenlegi profillal"; /* No comment provided by engineer. */ -"Join your group?\nThis is your link for group %@!" = "Csatlakozik a csoportjához?\nEz az ön hivatkozása a(z) %@ csoporthoz!"; +"Join your group?\nThis is your link for group %@!" = "Csatlakozik a csoportjához?\nEz a saját hivatkozása a(z) %@ nevű csoporthoz!"; /* No comment provided by engineer. */ "Joining group" = "Csatlakozás a csoporthoz"; +/* alert action */ +"Keep" = "Megtartás"; + /* No comment provided by engineer. */ -"Keep" = "Megtart"; +"Keep conversation" = "Beszélgetés megtartása"; /* No comment provided by engineer. */ "Keep the app open to use it from desktop" = "A számítógépről való használathoz tartsd nyitva az alkalmazást"; -/* No comment provided by engineer. */ -"Keep unused invitation?" = "Fel nem használt meghívó megtartása?"; +/* alert title */ +"Keep unused invitation?" = "Megtartja a fel nem használt meghívót?"; /* No comment provided by engineer. */ "Keep your connections" = "Kapcsolatok megtartása"; /* No comment provided by engineer. */ -"Keychain error" = "Kulcstároló hiba"; +"Keychain error" = "Kulcstartóhiba"; /* No comment provided by engineer. */ -"KeyChain error" = "Kulcstároló hiba"; +"KeyChain error" = "Kulcstartóhiba"; /* No comment provided by engineer. */ "Large file!" = "Nagy fájl!"; @@ -2196,20 +3030,26 @@ /* No comment provided by engineer. */ "Learn more" = "Tudjon meg többet"; +/* swipe action */ +"Leave" = "Elhagyás"; + /* No comment provided by engineer. */ -"Leave" = "Elhagy"; +"Leave chat" = "Csevegés elhagyása"; + +/* No comment provided by engineer. */ +"Leave chat?" = "Elhagyja a csevegést?"; /* No comment provided by engineer. */ "Leave group" = "Csoport elhagyása"; /* No comment provided by engineer. */ -"Leave group?" = "Csoport elhagyása?"; +"Leave group?" = "Elhagyja a csoportot?"; /* rcv group event chat item */ -"left" = "elhagyta"; +"left" = "elhagyta a csoportot"; /* email subject */ -"Let's talk in SimpleX Chat" = "Beszélgessünk a SimpleX Chat-ben"; +"Let's talk in SimpleX Chat" = "Beszélgessünk a SimpleX Chatben"; /* No comment provided by engineer. */ "Light" = "Világos"; @@ -2218,13 +3058,22 @@ "Limitations" = "Korlátozások"; /* No comment provided by engineer. */ -"Link mobile and desktop apps! 🔗" = "Társítsa össze a mobil és az asztali alkalmazásokat! 🔗"; +"Link mobile and desktop apps! 🔗" = "Társítsa össze a hordozható eszköz- és számítógépes alkalmazásokat! 🔗"; /* No comment provided by engineer. */ -"Linked desktop options" = "Összekapcsolt számítógép beállítások"; +"Linked desktop options" = "Társított számítógép beállítások"; /* No comment provided by engineer. */ -"Linked desktops" = "Összekapcsolt számítógépek"; +"Linked desktops" = "Társított számítógépek"; + +/* swipe action */ +"List" = "Lista"; + +/* No comment provided by engineer. */ +"List name and emoji should be different for all lists." = "Az összes lista nevének és emodzsijának különbözőnek kell lennie."; + +/* No comment provided by engineer. */ +"List name..." = "Lista neve…"; /* No comment provided by engineer. */ "LIVE" = "ÉLŐ"; @@ -2235,9 +3084,6 @@ /* No comment provided by engineer. */ "Live messages" = "Élő üzenetek"; -/* No comment provided by engineer. */ -"Local" = "Helyi"; - /* No comment provided by engineer. */ "Local name" = "Helyi név"; @@ -2250,42 +3096,39 @@ /* No comment provided by engineer. */ "Lock mode" = "Zárolási mód"; -/* No comment provided by engineer. */ -"Make a private connection" = "Privát kapcsolat létrehozása"; - /* No comment provided by engineer. */ "Make one message disappear" = "Egy üzenet eltüntetése"; /* No comment provided by engineer. */ -"Make profile private!" = "Tegye priváttá profilját!"; +"Make profile private!" = "Tegye priváttá a profilját!"; /* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Győződjön meg arról, hogy a %@ szervercímek megfelelő formátumúak, sorszeparáltak és nem duplikáltak (%@)."; +"Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Győződjön meg arról, hogy a megadott WebRTC ICE-kiszolgálók címei megfelelő formátumúak, soronként elkülönítettek, és nincsenek duplikálva."; /* No comment provided by engineer. */ -"Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Győződjön meg arról, hogy a WebRTC ICE-kiszolgáló címei megfelelő formátumúak, sorszeparáltak és nem duplikáltak."; - -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Sokan kérdezték: *ha a SimpleX-nek nincsenek felhasználói azonosítói, akkor hogyan tud üzeneteket kézbesíteni?*"; - -/* No comment provided by engineer. */ -"Mark deleted for everyone" = "Jelölje meg mindenki számára töröltként"; +"Mark deleted for everyone" = "Jelölje meg az összes tag számára töröltként"; /* No comment provided by engineer. */ "Mark read" = "Megjelölés olvasottként"; /* No comment provided by engineer. */ -"Mark verified" = "Ellenőrzöttként jelölve"; +"Mark verified" = "Hitelesítés"; /* No comment provided by engineer. */ "Markdown in messages" = "Markdown az üzenetekben"; /* marked deleted chat item preview text */ -"marked deleted" = "töröltnek jelölve"; +"marked deleted" = "törlésre jelölve"; /* No comment provided by engineer. */ "Max 30 seconds, received instantly." = "Max. 30 másodperc, azonnal érkezett."; +/* No comment provided by engineer. */ +"Media & file servers" = "Média- és fájlkiszolgálók"; + +/* blur media */ +"Medium" = "Közepes"; + /* member role */ "member" = "tag"; @@ -2293,43 +3136,121 @@ "Member" = "Tag"; /* profile update event chat item */ -"member %@ changed to %@" = "%1$@ tag megváltoztatta a nevét erre: %2$@"; +"member %@ changed to %@" = "%1$@ a következőre módosította a nevét: %2$@"; /* rcv group event chat item */ -"member connected" = "kapcsolódva"; +"member connected" = "kapcsolódott"; + +/* item status text */ +"Member inactive" = "Inaktív tag"; + +/* chat feature */ +"Member reports" = "Tagok jelentései"; /* No comment provided by engineer. */ -"Member role will be changed to \"%@\". All group members will be notified." = "A tag szerepköre meg fog változni erre: \"%@\". A csoport minden tagja értesítést kap róla."; +"Member role will be changed to \"%@\". All chat members will be notified." = "A tag szerepköre a következőre fog módosulni: „%@”. A csevegés összes tagja értesítést fog kapni."; /* No comment provided by engineer. */ -"Member role will be changed to \"%@\". The member will receive a new invitation." = "A tag szerepköre meg fog változni erre: \"%@\". A tag új meghívást fog kapni."; +"Member role will be changed to \"%@\". All group members will be notified." = "A tag szerepköre a következőre fog módosulni: „%@”. A csoport az összes tagja értesítést fog kapni."; /* No comment provided by engineer. */ -"Member will be removed from group - this cannot be undone!" = "A tag eltávolítása a csoportból - ez nem vonható vissza!"; +"Member role will be changed to \"%@\". The member will receive a new invitation." = "A tag szerepköre a következőre fog módosulni: „%@”. A tag új meghívást fog kapni."; + +/* No comment provided by engineer. */ +"Member will be removed from chat - this cannot be undone!" = "A tag el lesz távolítva a csevegésből – ez a művelet nem vonható vissza!"; + +/* No comment provided by engineer. */ +"Member will be removed from group - this cannot be undone!" = "A tag el lesz távolítva a csoportból – ez a művelet nem vonható vissza!"; + +/* No comment provided by engineer. */ +"Members can add message reactions." = "A tagok reakciókat adhatnak hozzá az üzenetekhez."; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "A tagok véglegesen törölhetik az elküldött üzeneteiket. (24 óra)"; + +/* No comment provided by engineer. */ +"Members can report messsages to moderators." = "A tagok jelenthetik az üzeneteket a moderátorok felé."; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "A tagok küldhetnek egymásnak közvetlen üzeneteket."; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "A tagok küldhetnek eltűnő üzeneteket."; + +/* No comment provided by engineer. */ +"Members can send files and media." = "A tagok küldhetnek fájlokat és médiatartalmakat."; + +/* No comment provided by engineer. */ +"Members can send SimpleX links." = "A tagok küldhetnek SimpleX-hivatkozásokat."; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "A tagok küldhetnek hangüzeneteket."; + +/* No comment provided by engineer. */ +"Mention members 👋" = "Tagok említése 👋"; + +/* No comment provided by engineer. */ +"Menus" = "Menük"; + +/* No comment provided by engineer. */ +"message" = "üzenet"; /* item status text */ "Message delivery error" = "Üzenetkézbesítési hiba"; /* No comment provided by engineer. */ -"Message delivery receipts!" = "Üzenetkézbesítési bizonylatok!"; +"Message delivery receipts!" = "Üzenetkézbesítési jelentések!"; + +/* item status text */ +"Message delivery warning" = "Üzenetkézbesítési figyelmeztetés"; /* No comment provided by engineer. */ "Message draft" = "Üzenetvázlat"; +/* item status text */ +"Message forwarded" = "Továbbított üzenet"; + +/* item status description */ +"Message may be delivered later if member becomes active." = "Az üzenet később is kézbesíthető, ha a tag aktívvá válik."; + +/* No comment provided by engineer. */ +"Message queue info" = "Üzenetsorbaállítási információ"; + /* chat feature */ "Message reactions" = "Üzenetreakciók"; /* No comment provided by engineer. */ -"Message reactions are prohibited in this chat." = "Az üzenetreakciók ebben a csevegésben le vannak tiltva."; +"Message reactions are prohibited in this chat." = "A reakciók hozzáadása az üzenetekhez le van tiltva ebben a csevegésben."; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "Ebben a csoportban az üzenetreakciók le vannak tiltva."; +"Message reactions are prohibited." = "A reakciók hozzáadása az üzenetekhez le van tiltva."; /* notification */ "message received" = "üzenet érkezett"; /* No comment provided by engineer. */ -"Message text" = "Üzenet szövege"; +"Message reception" = "Üzenetjelentés"; + +/* No comment provided by engineer. */ +"Message servers" = "Üzenetkiszolgálók"; + +/* No comment provided by engineer. */ +"Message shape" = "Üzenetbuborék alakja"; + +/* No comment provided by engineer. */ +"Message source remains private." = "Az üzenet forrása titokban marad."; + +/* No comment provided by engineer. */ +"Message status" = "Üzenet állapota"; + +/* copied message info */ +"Message status: %@" = "Üzenet állapota: %@"; + +/* No comment provided by engineer. */ +"Message text" = "Név és üzenet"; + +/* No comment provided by engineer. */ +"Message too large" = "Az üzenet túl nagy"; /* No comment provided by engineer. */ "Messages" = "Üzenetek"; @@ -2338,22 +3259,61 @@ "Messages & files" = "Üzenetek és fájlok"; /* No comment provided by engineer. */ -"Messages from %@ will be shown!" = "A(z) %@ által írt üzenetek megjelennek!"; +"Messages from %@ will be shown!" = "%@ összes üzenete meg fog jelenni!"; + +/* alert message */ +"Messages in this chat will never be deleted." = "Az ebben a csevegésben lévő üzenetek soha nem lesznek törölve."; /* No comment provided by engineer. */ -"Migrating database archive…" = "Adatbázis archívum migrálása…"; +"Messages received" = "Fogadott üzenetek"; /* No comment provided by engineer. */ -"Migration error:" = "Migrációs hiba:"; +"Messages sent" = "Elküldött üzenetek"; + +/* alert message */ +"Messages were deleted after you selected them." = "Az üzeneteket törölték miután kijelölte őket."; /* No comment provided by engineer. */ -"Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat)." = "Sikertelen migráció. Koppintson a **Kihagyás** lehetőségre az aktuális adatbázis használatának folytatásához. Kérjük, jelentse a problémát az alkalmazás fejlesztőinek csevegésben vagy e-mailben [chat@simplex.chat](mailto:chat@simplex.chat)."; +"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Az üzenetek, a fájlok és a hívások **végpontok közötti titkosítással**, sérülés utáni titkosságvédelemmel és -helyreállítással, továbbá letagadhatósággal vannak védve."; /* No comment provided by engineer. */ -"Migration is completed" = "A migráció befejeződött"; +"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Az üzenetek, a fájlok és a hívások **végpontok közötti kvantumbiztos titkosítással**, sérülés utáni titkosságvédelemmel és -helyreállítással, továbbá letagadhatósággal vannak védve."; /* No comment provided by engineer. */ -"Migrations: %@" = "Migrációk: %@"; +"Migrate device" = "Eszköz átköltöztetése"; + +/* No comment provided by engineer. */ +"Migrate from another device" = "Átköltöztetés egy másik eszközről"; + +/* No comment provided by engineer. */ +"Migrate here" = "Átköltöztetés ide"; + +/* No comment provided by engineer. */ +"Migrate to another device" = "Átköltöztetés egy másik eszközre"; + +/* No comment provided by engineer. */ +"Migrate to another device via QR code." = "Átköltöztetés egy másik eszközre QR-kód használatával."; + +/* No comment provided by engineer. */ +"Migrating" = "Átköltöztetés"; + +/* No comment provided by engineer. */ +"Migrating database archive…" = "Adatbázis-archívum átköltöztetése…"; + +/* No comment provided by engineer. */ +"Migration complete" = "Átköltöztetés befejezve"; + +/* No comment provided by engineer. */ +"Migration error:" = "Átköltöztetési hiba:"; + +/* No comment provided by engineer. */ +"Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat)." = "Sikertelen átköltöztetés. Koppintson a **Kihagyás** lehetőségre a jelenlegi adatbázis használatának folytatásához. Jelentse a problémát az alkalmazás fejlesztőinek csevegésben vagy e-mailben [chat@simplex.chat](mailto:chat@simplex.chat)."; + +/* No comment provided by engineer. */ +"Migration is completed" = "Az átköltöztetés befejeződött"; + +/* No comment provided by engineer. */ +"Migrations:" = "Átköltöztetések:"; /* time unit */ "minutes" = "perc"; @@ -2368,71 +3328,107 @@ "moderated" = "moderált"; /* No comment provided by engineer. */ -"Moderated at" = "Moderálva ekkor"; +"Moderated at" = "Moderálva"; /* copied message info */ -"Moderated at: %@" = "Moderálva ekkor: %@"; +"Moderated at: %@" = "Moderálva: %@"; /* marked deleted chat item preview text */ -"moderated by %@" = "%@ által moderálva"; +"moderated by %@" = "moderálva lett %@ által"; + +/* member role */ +"moderator" = "moderátor"; /* time unit */ "months" = "hónap"; +/* swipe action */ +"More" = "Továbbiak"; + /* No comment provided by engineer. */ "More improvements are coming soon!" = "Hamarosan további fejlesztések érkeznek!"; +/* No comment provided by engineer. */ +"More reliable network connection." = "Megbízhatóbb hálózati kapcsolat."; + +/* No comment provided by engineer. */ +"More reliable notifications" = "Megbízhatóbb értesítések"; + /* item status description */ -"Most likely this connection is deleted." = "Valószínűleg ez a kapcsolat törlésre került."; +"Most likely this connection is deleted." = "Valószínűleg ez a kapcsolat törölve lett."; /* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "Valószínűleg ez az ismerős törölte önnel a kapcsolatot."; +"Multiple chat profiles" = "Több csevegési profil"; -/* No comment provided by engineer. */ -"Multiple chat profiles" = "Több csevegőprofil"; +/* notification label action */ +"Mute" = "Némítás"; -/* No comment provided by engineer. */ -"Mute" = "Elnémítás"; +/* notification label action */ +"Mute all" = "Összes némítása"; /* No comment provided by engineer. */ "Muted when inactive!" = "Némítás, ha inaktív!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "Név"; /* No comment provided by engineer. */ "Network & servers" = "Hálózat és kiszolgálók"; +/* No comment provided by engineer. */ +"Network connection" = "Hálózati kapcsolat"; + +/* No comment provided by engineer. */ +"Network decentralization" = "Hálózati decentralizáció"; + +/* snd error text */ +"Network issues - message expired after many attempts to send it." = "Hálózati problémák – az üzenet többszöri elküldési kísérlet után lejárt."; + +/* No comment provided by engineer. */ +"Network management" = "Hálózatkezelés"; + +/* No comment provided by engineer. */ +"Network operator" = "Hálózatüzemeltető"; + /* No comment provided by engineer. */ "Network settings" = "Hálózati beállítások"; /* No comment provided by engineer. */ "Network status" = "Hálózat állapota"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "soha"; +/* token status text */ +"New" = "Új"; + /* No comment provided by engineer. */ -"New chat" = "Új beszélgetés"; +"New chat" = "Új csevegés"; + +/* No comment provided by engineer. */ +"New chat experience 🎉" = "Új csevegési élmény 🎉"; /* notification */ -"New contact request" = "Új kapcsolattartási kérelem"; +"New contact request" = "Új meghívási kérés"; /* notification */ "New contact:" = "Új kapcsolat:"; /* No comment provided by engineer. */ -"New database archive" = "Új adatbázis-archívum"; +"New desktop app!" = "Új számítógép-alkalmazás!"; /* No comment provided by engineer. */ -"New desktop app!" = "Új asztali alkalmazás!"; +"New display name" = "Új megjelenítendő név"; -/* No comment provided by engineer. */ -"New display name" = "Új megjelenítési név"; +/* notification */ +"New events" = "Új események"; /* No comment provided by engineer. */ "New in %@" = "Újdonságok a(z) %@ verzióban"; +/* No comment provided by engineer. */ +"New media options" = "Új médiabeállítások"; + /* No comment provided by engineer. */ "New member role" = "Új tag szerepköre"; @@ -2448,6 +3444,15 @@ /* No comment provided by engineer. */ "New passphrase…" = "Új jelmondat…"; +/* No comment provided by engineer. */ +"New server" = "Új kiszolgáló"; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used every time you start the app." = "Minden alkalommal, amikor elindítja az alkalmazást, új SOCKS-hitelesítő-adatokat fog használni."; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used for each server." = "Az összes kiszolgálóhoz új, SOCKS-hitelesítő-adatok legyenek használva."; + /* pref value */ "no" = "nem"; @@ -2458,16 +3463,28 @@ "No app password" = "Nincs alkalmazás jelszó"; /* No comment provided by engineer. */ -"No contacts selected" = "Nem kerültek ismerősök kiválasztásra"; +"No chats" = "Nincsenek csevegések"; /* No comment provided by engineer. */ -"No contacts to add" = "Nincs hozzáadandó ismerős"; +"No chats found" = "Nem találhatók csevegések"; + +/* No comment provided by engineer. */ +"No chats in list %@" = "Nincsenek csevegések a(z) %@ nevű listában"; + +/* No comment provided by engineer. */ +"No contacts selected" = "Nincs partner kijelölve"; + +/* No comment provided by engineer. */ +"No contacts to add" = "Nincs hozzáadandó partner"; /* No comment provided by engineer. */ "No delivery information" = "Nincs kézbesítési információ"; /* No comment provided by engineer. */ -"No device token!" = "Nincs eszköztoken!"; +"No device token!" = "Nincs készüléktoken!"; + +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Még nincs közvetlen kapcsolat, az üzenetet az adminisztrátor továbbítja."; /* No comment provided by engineer. */ "no e2e encryption" = "nincs e2e titkosítás"; @@ -2481,45 +3498,108 @@ /* No comment provided by engineer. */ "No history" = "Nincsenek előzmények"; +/* No comment provided by engineer. */ +"No info, try to reload" = "Nincs információ, próbálja meg újratölteni"; + +/* servers error */ +"No media & file servers." = "Nincsenek média- és fájlkiszolgálók."; + +/* No comment provided by engineer. */ +"No message" = "Nincs üzenet"; + +/* servers error */ +"No message servers." = "Nincsenek üzenet-kiszolgálók."; + +/* No comment provided by engineer. */ +"No network connection" = "Nincs hálózati kapcsolat"; + +/* No comment provided by engineer. */ +"No permission to record speech" = "Nincs jogosultság megadva a beszéd rögzítéséhez"; + +/* No comment provided by engineer. */ +"No permission to record video" = "Nincs jogosultság megadva a videó rögzítéséhez"; + /* No comment provided by engineer. */ "No permission to record voice message" = "Nincs engedély a hangüzenet rögzítésére"; +/* No comment provided by engineer. */ +"No push server" = "Helyi"; + /* No comment provided by engineer. */ "No received or sent files" = "Nincsenek fogadott vagy küldött fájlok"; +/* servers error */ +"No servers for private message routing." = "Nincsenek kiszolgálók a privát üzenet-útválasztáshoz."; + +/* servers error */ +"No servers to receive files." = "Nincsenek fájlfogadási kiszolgálók."; + +/* servers error */ +"No servers to receive messages." = "Nincsenek üzenetfogadási kiszolgálók."; + +/* servers error */ +"No servers to send files." = "Nincsenek fájlküldő-kiszolgálók."; + /* copied message info in history */ "no text" = "nincs szöveg"; +/* alert title */ +"No token!" = "Nincs token!"; + +/* No comment provided by engineer. */ +"No unread chats" = "Nincsenek olvasatlan csevegések"; + +/* No comment provided by engineer. */ +"No user identifiers." = "Nincsenek felhasználó-azonosítók."; + /* No comment provided by engineer. */ "Not compatible!" = "Nem kompatibilis!"; +/* No comment provided by engineer. */ +"Notes" = "Jegyzetek"; + +/* No comment provided by engineer. */ +"Nothing selected" = "Nincs semmi kijelölve"; + +/* alert title */ +"Nothing to forward!" = "Nincs mit továbbítani!"; + /* No comment provided by engineer. */ "Notifications" = "Értesítések"; /* No comment provided by engineer. */ "Notifications are disabled!" = "Az értesítések le vannak tiltva!"; +/* alert title */ +"Notifications error" = "Értesítési hiba"; + /* No comment provided by engineer. */ -"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Most már az adminok is:\n- törölhetik a tagok üzeneteit.\n- letilthatnak tagokat (\"megfigyelő\" szerepkör)"; +"Notifications privacy" = "Értesítési adatvédelem"; + +/* alert title */ +"Notifications status" = "Értesítések állapota"; + +/* No comment provided by engineer. */ +"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Most már az adminisztrátorok is:\n- törölhetik a tagok üzeneteit.\n- letilthatnak tagokat („megfigyelő” szerepkör)"; /* member role */ "observer" = "megfigyelő"; /* enabled status - group pref value - time to disappear */ -"off" = "ki"; +group pref value +time to disappear */ +"off" = "kikapcsolva"; -/* No comment provided by engineer. */ -"Off" = "Ki"; +/* blur media */ +"Off" = "Kikapcsolva"; /* feature offered item */ "offered %@" = "%@ ajánlotta"; /* feature offered item */ -"offered %@: %@" = "ajánlotta %1$@: %2$@-kor"; +"offered %@: %@" = "ajánlotta: %1$@, ekkor: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Rendben"; /* No comment provided by engineer. */ @@ -2528,192 +3608,276 @@ /* No comment provided by engineer. */ "Old database" = "Régi adatbázis"; -/* No comment provided by engineer. */ -"Old database archive" = "Régi adatbázis archívum"; - /* group pref value */ -"on" = "be"; +"on" = "bekapcsolva"; /* No comment provided by engineer. */ -"One-time invitation link" = "Egyszer használatos meghívó hivatkozás"; +"One-time invitation link" = "Egyszer használható meghívó"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "A csatlakozáshoz Onion host-okra lesz szükség. VPN engedélyezése szükséges."; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "Onion-kiszolgálók **szükségesek** a kapcsolódáshoz.\nKompatibilis VPN szükséges."; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "Onion host-ok használata, ha azok rendelkezésre állnak. VPN engedélyezése szükséges."; +"Onion hosts will be used when available.\nRequires compatible VPN." = "Onion-kiszolgálók használata, ha azok rendelkezésre állnak.\nVPN engedélyezése szükséges."; /* No comment provided by engineer. */ -"Onion hosts will not be used." = "Onion host-ok nem lesznek használva."; +"Onion hosts will not be used." = "Az onion-kiszolgálók nem lesznek használva."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Csak a klienseszközök tárolják a felhasználói profilokat, névjegyeket, csoportokat és a **2 rétegű végponttól-végpontig titkosítással** küldött üzeneteket."; +"Only chat owners can change preferences." = "Csak a csevegés tulajdonosai módosíthatják a csevegési beállításokat."; /* No comment provided by engineer. */ -"Only group owners can change group preferences." = "Csak a csoporttulajdonosok módosíthatják a csoportbeállításokat."; +"Only client devices store user profiles, contacts, groups, and messages." = "A felhasználói profilok, partnerek, csoportok és üzenetek csak az eszközön vannak tárolva a kliensen belül."; /* No comment provided by engineer. */ -"Only group owners can enable files and media." = "Csak a csoporttulajdonosok engedélyezhetik a fájlok- és a médiatartalmak küldését."; +"Only delete conversation" = "Csak a beszélgetés törlése"; /* No comment provided by engineer. */ -"Only group owners can enable voice messages." = "Csak a csoporttulajdonosok engedélyezhetik a hangüzenetek küldését."; +"Only group owners can change group preferences." = "Csak a csoport tulajdonosai módosíthatják a csoportbeállításokat."; /* No comment provided by engineer. */ -"Only you can add message reactions." = "Csak ön adhat hozzá üzenetreakciókat."; +"Only group owners can enable files and media." = "Csak a csoport tulajdonosai engedélyezhetik a fájlok- és a médiatartalmak küldését."; /* No comment provided by engineer. */ -"Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours)" = "Visszafordíthatatlanul csak ön törölhet üzeneteket (ismerőse csak törlésre jelölheti őket ). (24 óra)"; +"Only group owners can enable voice messages." = "Csak a csoport tulajdonosai engedélyezhetik a hangüzenetek küldését."; /* No comment provided by engineer. */ -"Only you can make calls." = "Csak ön tud hívásokat indítani."; +"Only sender and moderators see it" = "Csak a küldő és a moderátorok látják"; /* No comment provided by engineer. */ -"Only you can send disappearing messages." = "Csak ön tud eltűnő üzeneteket küldeni."; +"Only you and moderators see it" = "Csak Ön és a moderátorok látják"; /* No comment provided by engineer. */ -"Only you can send voice messages." = "Csak ön tud hangüzeneteket küldeni."; +"Only you can add message reactions." = "Csak Ön adhat hozzá reakciókat az üzenetekhez."; /* No comment provided by engineer. */ -"Only your contact can add message reactions." = "Csak az ismerős tud üzeneteakciókat adni."; +"Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours)" = "Véglegesen csak Ön törölhet üzeneteket (partnere csak törlésre jelölheti meg őket ). (24 óra)"; /* No comment provided by engineer. */ -"Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours)" = "Csak az ismerős tud visszafordíthatatlanul törölni üzeneteket (megjelölheti őket törlésre). (24 óra)"; +"Only you can make calls." = "Csak Ön tud hívásokat indítani."; /* No comment provided by engineer. */ -"Only your contact can make calls." = "Csak az ismerős tud hívást indítani."; +"Only you can send disappearing messages." = "Csak Ön tud eltűnő üzeneteket küldeni."; /* No comment provided by engineer. */ -"Only your contact can send disappearing messages." = "Csak az ismerős tud eltűnő üzeneteket küldeni."; +"Only you can send voice messages." = "Csak Ön tud hangüzeneteket küldeni."; /* No comment provided by engineer. */ -"Only your contact can send voice messages." = "Csak az ismerős tud hangüzeneteket küldeni."; +"Only your contact can add message reactions." = "Csak a partnere adhat hozzá reakciókat az üzenetekhez."; /* No comment provided by engineer. */ +"Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours)" = "Csak a partnere tudja az üzeneteket véglegesen törölni (Ön csak törlésre jelölheti meg azokat). (24 óra)"; + +/* No comment provided by engineer. */ +"Only your contact can make calls." = "Csak a partnere tud hívást indítani."; + +/* No comment provided by engineer. */ +"Only your contact can send disappearing messages." = "Csak a partnere tud eltűnő üzeneteket küldeni."; + +/* No comment provided by engineer. */ +"Only your contact can send voice messages." = "Csak a partnere tud hangüzeneteket küldeni."; + +/* alert action */ "Open" = "Megnyitás"; +/* No comment provided by engineer. */ +"Open changes" = "Módosítások megtekintése"; + /* No comment provided by engineer. */ "Open chat" = "Csevegés megnyitása"; /* authentication reason */ -"Open chat console" = "Csevegés konzol megnyitása"; +"Open chat console" = "Csevegési konzol megnyitása"; + +/* No comment provided by engineer. */ +"Open conditions" = "Feltételek megnyitása"; /* No comment provided by engineer. */ "Open group" = "Csoport megnyitása"; +/* authentication reason */ +"Open migration to another device" = "Átköltöztetés indítása egy másik eszközre"; + /* No comment provided by engineer. */ "Open Settings" = "Beállítások megnyitása"; -/* authentication reason */ -"Open user profiles" = "Felhasználói profilok megnyitása"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "Nyílt forráskódú protokoll és forráskód – bárki üzemeltethet kiszolgálókat."; - /* No comment provided by engineer. */ "Opening app…" = "Az alkalmazás megnyitása…"; +/* No comment provided by engineer. */ +"Operator" = "Üzemeltető"; + +/* alert title */ +"Operator server" = "Kiszolgáló-üzemeltető"; + +/* No comment provided by engineer. */ +"Or import archive file" = "Vagy archívumfájl importálása"; + +/* No comment provided by engineer. */ +"Or paste archive link" = "Vagy az archívum hivatkozásának beillesztése"; + /* No comment provided by engineer. */ "Or scan QR code" = "Vagy QR-kód beolvasása"; +/* No comment provided by engineer. */ +"Or securely share this file link" = "Vagy ossza meg biztonságosan ezt a fájlhivatkozást"; + /* No comment provided by engineer. */ "Or show this code" = "Vagy mutassa meg ezt a kódot"; +/* No comment provided by engineer. */ +"Or to share privately" = "Vagy a privát megosztáshoz"; + +/* No comment provided by engineer. */ +"Organize chats into lists" = "Csevegések listákba szervezése"; + +/* No comment provided by engineer. */ +"other" = "egyéb"; + +/* No comment provided by engineer. */ +"Other" = "További"; + +/* No comment provided by engineer. */ +"other errors" = "egyéb hibák"; + +/* alert message */ +"Other file errors:\n%@" = "Egyéb fájlhiba:\n%@"; + /* member role */ "owner" = "tulajdonos"; +/* feature role */ +"owners" = "tulajdonosok"; + /* No comment provided by engineer. */ "Passcode" = "Jelkód"; /* No comment provided by engineer. */ -"Passcode changed!" = "A jelkód megváltozott!"; +"Passcode changed!" = "A jelkód módosult!"; /* No comment provided by engineer. */ "Passcode entry" = "Jelkód bevitele"; /* No comment provided by engineer. */ -"Passcode not changed!" = "A jelkód nem változott!"; +"Passcode not changed!" = "A jelkód nem módosult!"; /* No comment provided by engineer. */ "Passcode set!" = "A jelkód beállítva!"; /* No comment provided by engineer. */ -"Password to show" = "Jelszó mutatása"; - -/* past/unknown group member */ -"Past member %@" = "Korábbi csoport tag %@"; +"Password" = "Jelszó"; /* No comment provided by engineer. */ -"Paste desktop address" = "Számítógép azonosítójának beillesztése"; +"Password to show" = "Jelszó a megjelenítéshez"; + +/* past/unknown group member */ +"Past member %@" = "(Már nem tag) %@"; + +/* No comment provided by engineer. */ +"Paste desktop address" = "Számítógép címének beillesztése"; /* No comment provided by engineer. */ "Paste image" = "Kép beillesztése"; /* No comment provided by engineer. */ -"Paste link to connect!" = "Hivatkozás beillesztése a csatlakozáshoz!"; +"Paste link to connect!" = "Hivatkozás beillesztése a kapcsolódáshoz!"; /* No comment provided by engineer. */ -"Paste the link you received" = "Fogadott hivatkozás beillesztése"; +"Paste the link you received" = "Kapott hivatkozás beillesztése"; /* No comment provided by engineer. */ -"peer-to-peer" = "ponttól-pontig"; +"peer-to-peer" = "egyenrangú"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "Az emberek csak az ön által megosztott hivatkozáson keresztül kapcsolódhatnak."; +"pending" = "függőben"; /* No comment provided by engineer. */ -"Periodically" = "Rendszeresen"; +"Pending" = "Függőben"; + +/* No comment provided by engineer. */ +"pending approval" = "jóváhagyásra vár"; + +/* No comment provided by engineer. */ +"Periodic" = "Időszakos"; /* message decrypt error item */ "Permanent decryption error" = "Végleges visszafejtési hiba"; /* No comment provided by engineer. */ -"PING count" = "PING számláló"; +"Picture-in-picture calls" = "Kép a képben hívások"; /* No comment provided by engineer. */ -"PING interval" = "PING időköze"; +"PING count" = "PING-ek száma"; /* No comment provided by engineer. */ -"Please ask your contact to enable sending voice messages." = "Ismerős felkérése, hogy engedélyezze a hangüzenetek küldését."; +"PING interval" = "Időtartam a PING-ek között"; /* No comment provided by engineer. */ -"Please check that you used the correct link or ask your contact to send you another one." = "Ellenőrizze, hogy a megfelelő hivatkozást használta-e, vagy kérje meg ismerősét, hogy küldjön egy másikat."; +"Play from the chat list." = "Lejátszás a csevegési listából."; /* No comment provided by engineer. */ -"Please check your network connection with %@ and try again." = "Kérjük, ellenőrizze hálózati kapcsolatát a(z) %@ segítségével, és próbálja újra."; +"Please ask your contact to enable calls." = "Kérje meg a partnerét, hogy engedélyezze a hívásokat."; /* No comment provided by engineer. */ -"Please check yours and your contact preferences." = "Ellenőrizze az ön és ismerőse beállításait."; +"Please ask your contact to enable sending voice messages." = "Kérje meg a partnerét, hogy engedélyezze a hangüzenetek küldését."; + +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Ellenőrizze, hogy a hordozható eszköz és a számítógép ugyanahhoz a helyi hálózathoz csatlakozik-e, valamint a számítógép tűzfalában engedélyezve van-e a kapcsolat.\nMinden további problémát osszon meg a fejlesztőkkel."; + +/* No comment provided by engineer. */ +"Please check that you used the correct link or ask your contact to send you another one." = "Ellenőrizze, hogy a megfelelő hivatkozást használta-e, vagy kérje meg a partnerét, hogy küldjön egy másikat."; + +/* No comment provided by engineer. */ +"Please check your network connection with %@ and try again." = "Ellenőrizze a hálózati kapcsolatát a vele: %@, és próbálja újra."; + +/* No comment provided by engineer. */ +"Please check yours and your contact preferences." = "Ellenőrizze a saját- és a partnere beállításait."; + +/* No comment provided by engineer. */ +"Please confirm that network settings are correct for this device." = "Ellenőrizze, hogy a hálózati beállítások megfelelők-e ehhez az eszközhöz."; /* No comment provided by engineer. */ "Please contact developers.\nError: %@" = "Lépjen kapcsolatba a fejlesztőkkel.\nHiba: %@"; /* No comment provided by engineer. */ -"Please contact group admin." = "Lépjen kapcsolatba a csoport adminnal."; +"Please contact group admin." = "Lépjen kapcsolatba a csoport adminisztrátorával."; /* No comment provided by engineer. */ -"Please enter correct current passphrase." = "Adja meg a helyes aktuális jelmondatát."; +"Please enter correct current passphrase." = "Adja meg a helyes, jelenlegi jelmondatot."; /* No comment provided by engineer. */ -"Please enter the previous password after restoring database backup. This action can not be undone." = "Előző jelszó megadása az adatbázis biztonsági mentésének visszaállítása után. Ez a művelet nem visszavonható."; +"Please enter the previous password after restoring database backup. This action can not be undone." = "Adja meg a korábbi jelszót az adatbázis biztonsági mentésének visszaállítása után. Ez a művelet nem vonható vissza."; /* No comment provided by engineer. */ -"Please remember or store it securely - there is no way to recover a lost passcode!" = "Jegyezze fel vagy tárolja el biztonságosan - az elveszett jelkódot nem lehet visszaállítani!"; +"Please remember or store it securely - there is no way to recover a lost passcode!" = "Jegyezze fel vagy tárolja el biztonságosan – az elveszett jelkódot nem lehet visszaállítani!"; /* No comment provided by engineer. */ "Please report it to the developers." = "Jelentse a fejlesztőknek."; /* No comment provided by engineer. */ -"Please restart the app and migrate the database to enable push notifications." = "Indítsa újra az alkalmazást az adatbázis-migrációhoz szükséges push értesítések engedélyezéséhez."; +"Please restart the app and migrate the database to enable push notifications." = "Indítsa újra az alkalmazást az adatbázis-átköltöztetéséhez szükséges push-értesítések engedélyezéséhez."; /* No comment provided by engineer. */ "Please store passphrase securely, you will NOT be able to access chat if you lose it." = "Tárolja el biztonságosan jelmondát, mert ha elveszti azt, akkor NEM férhet hozzá a csevegéshez."; /* No comment provided by engineer. */ -"Please store passphrase securely, you will NOT be able to change it if you lose it." = "Tárolja el biztonságosan jelmondatát, mert ha elveszíti azt, NEM tudja megváltoztatni."; +"Please store passphrase securely, you will NOT be able to change it if you lose it." = "Tárolja el biztonságosan jelmondatát, mert ha elveszíti azt, NEM tudja módosítani."; + +/* token info */ +"Please try to disable and re-enable notfications." = "Próbálja meg letiltani és újra engedélyezni az értesítéseket."; + +/* token info */ +"Please wait for token activation to complete." = "Várjon, amíg a token aktiválása befejeződik."; + +/* token info */ +"Please wait for token to be registered." = "Várjon a token regisztrálására."; /* No comment provided by engineer. */ "Polish interface" = "Lengyel kezelőfelület"; +/* No comment provided by engineer. */ +"Port" = "Port"; + /* server test error */ "Possibly, certificate fingerprint in server address is incorrect" = "Lehetséges, hogy a kiszolgáló címében szereplő tanúsítvány-ujjlenyomat helytelen"; @@ -2721,26 +3885,53 @@ "Preserve the last message draft, with attachments." = "Az utolsó üzenet tervezetének megőrzése a mellékletekkel együtt."; /* No comment provided by engineer. */ -"Preset server" = "Előre beállított kiszolgáló"; +"Preset server address" = "Az előre beállított kiszolgáló címe"; /* No comment provided by engineer. */ -"Preset server address" = "Előre beállított kiszolgáló címe"; +"Preset servers" = "Előre beállított kiszolgálók"; /* No comment provided by engineer. */ "Preview" = "Előnézet"; +/* No comment provided by engineer. */ +"Previously connected servers" = "Korábban kapcsolódott kiszolgálók"; + /* No comment provided by engineer. */ "Privacy & security" = "Adatvédelem és biztonság"; /* No comment provided by engineer. */ -"Privacy redefined" = "Adatvédelem újraértelmezve"; +"Privacy for your customers." = "Saját ügyfeleinek adatvédelme."; /* No comment provided by engineer. */ -"Private filenames" = "Privát fájl nevek"; +"Privacy policy and conditions of use." = "Adatvédelmi szabályzat és felhasználási feltételek."; + +/* No comment provided by engineer. */ +"Privacy redefined" = "Újraértelmezett adatvédelem"; + +/* No comment provided by engineer. */ +"Private chats, groups and your contacts are not accessible to server operators." = "A privát csevegések, a csoportok és a partnerek nem érhetők el a szerver üzemeltetői számára."; + +/* No comment provided by engineer. */ +"Private filenames" = "Privát fájlnevek"; + +/* No comment provided by engineer. */ +"Private media file names." = "Privát nevek a médiafájlokhoz."; + +/* No comment provided by engineer. */ +"Private message routing" = "Privát üzenet-útválasztás"; + +/* No comment provided by engineer. */ +"Private message routing 🚀" = "Privát üzenet-útválasztás 🚀"; /* name of notes to self */ "Private notes" = "Privát jegyzetek"; +/* No comment provided by engineer. */ +"Private routing" = "Privát útválasztás"; + +/* No comment provided by engineer. */ +"Private routing error" = "Privát útválasztási hiba"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profil és kiszolgálókapcsolatok"; @@ -2748,208 +3939,344 @@ "Profile image" = "Profilkép"; /* No comment provided by engineer. */ -"Profile name" = "Profilnév"; - -/* No comment provided by engineer. */ -"Profile name:" = "Profil neve:"; +"Profile images" = "Profilképek"; /* No comment provided by engineer. */ "Profile password" = "Profiljelszó"; /* No comment provided by engineer. */ -"Profile update will be sent to your contacts." = "A profilfrissítés elküldésre került az ismerősök számára."; +"Profile theme" = "Profiltéma"; + +/* alert message */ +"Profile update will be sent to your contacts." = "A profilfrissítés el lesz küldve a partnerei számára."; /* No comment provided by engineer. */ -"Prohibit audio/video calls." = "Hang- és videóhívások tiltása."; +"Prohibit audio/video calls." = "A hívások kezdeményezése le van tiltva."; /* No comment provided by engineer. */ -"Prohibit irreversible message deletion." = "Az üzenetek véglegesen való törlése le van tiltva."; +"Prohibit irreversible message deletion." = "Az elküldött üzenetek végleges törlése le van tiltva."; /* No comment provided by engineer. */ -"Prohibit message reactions." = "Üzenetreakciók tiltása."; +"Prohibit message reactions." = "A reakciók hozzáadása az üzenethez le van tiltva."; /* No comment provided by engineer. */ -"Prohibit messages reactions." = "Az üzenetreakciók tiltása."; +"Prohibit messages reactions." = "A reakciók hozzáadása az üzenetekhez le van tiltva."; /* No comment provided by engineer. */ -"Prohibit sending direct messages to members." = "Közvetlen üzenetek küldésének letiltása tagok részére."; +"Prohibit reporting messages to moderators." = "Az üzenetek a moderátorok felé történő jelentésének megtiltása."; /* No comment provided by engineer. */ -"Prohibit sending disappearing messages." = "Eltűnő üzenetek küldésének letiltása."; +"Prohibit sending direct messages to members." = "A közvetlen üzenetek küldése a tagok között le van tiltva."; /* No comment provided by engineer. */ -"Prohibit sending files and media." = "Fájlok- és a médiatartalom küldés letiltása."; +"Prohibit sending disappearing messages." = "Az eltűnő üzenetek küldése le van tiltva."; /* No comment provided by engineer. */ -"Prohibit sending voice messages." = "Hangüzenetek küldésének letiltása."; +"Prohibit sending files and media." = "A fájlok- és a médiatartalmak küldése le van tiltva."; /* No comment provided by engineer. */ -"Protect app screen" = "App képernyőjének védelme"; +"Prohibit sending SimpleX links." = "A SimpleX-hivatkozások küldése le van tiltva."; /* No comment provided by engineer. */ -"Protect your chat profiles with a password!" = "Csevegési profiljok védelme jelszóval!"; +"Prohibit sending voice messages." = "A hangüzenetek küldése le van tiltva."; /* No comment provided by engineer. */ -"Protocol timeout" = "Protokoll időtúllépés"; +"Protect app screen" = "Alkalmazás képernyőjének védelme"; /* No comment provided by engineer. */ -"Protocol timeout per KB" = "Protokoll időkorlát KB-onként"; +"Protect IP address" = "IP-cím védelme"; /* No comment provided by engineer. */ -"Push notifications" = "Push értesítések"; +"Protect your chat profiles with a password!" = "Védje meg a csevegési profiljait egy jelszóval!"; + +/* No comment provided by engineer. */ +"Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." = "Védje az IP-címét a partnerei által kiválasztott üzenetváltási továbbítókiszolgálókkal szemben.\nEngedélyezze a *Hálózat és kiszolgálók* menüben."; + +/* No comment provided by engineer. */ +"Protocol timeout" = "Protokoll időtúllépése"; + +/* No comment provided by engineer. */ +"Protocol timeout per KB" = "Protokoll időtúllépése kB-onként"; + +/* No comment provided by engineer. */ +"Proxied" = "Proxyzott"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Proxyzott kiszolgálók"; + +/* No comment provided by engineer. */ +"Proxy requires password" = "A proxy jelszót igényel"; + +/* No comment provided by engineer. */ +"Push notifications" = "Push-értesítések"; + +/* No comment provided by engineer. */ +"Push server" = "Push-kiszolgáló"; + +/* chat item text */ +"quantum resistant e2e encryption" = "végpontok közötti kvantumbiztos titkosítás"; + +/* No comment provided by engineer. */ +"Quantum resistant encryption" = "Kvantumbiztos titkosítás"; /* No comment provided by engineer. */ "Rate the app" = "Értékelje az alkalmazást"; +/* No comment provided by engineer. */ +"Reachable chat toolbar" = "Könnyen elérhető eszköztár"; + /* chat item menu */ "React…" = "Reagálj…"; -/* No comment provided by engineer. */ -"Read" = "Olvasd el"; +/* swipe action */ +"Read" = "Olvasott"; /* No comment provided by engineer. */ "Read more" = "Tudjon meg többet"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "További információ a [Felhasználói útmutatóban](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "További információ a [Használati útmutatóban](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "További információ a [Felhasználói útmutatóban](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "További információ a [Használati útmutatóban](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)."; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "További információ a [Felhasználói útmutatóban](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "További információ a [Használati útmutatóban](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; /* No comment provided by engineer. */ "Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "További információ a [GitHub tárolóban](https://github.com/simplex-chat/simplex-chat#readme)."; /* No comment provided by engineer. */ -"Read more in our GitHub repository." = "További információ a GitHub tárolónkban."; +"Receipts are disabled" = "A kézbesítési jelentések le vannak tiltva"; /* No comment provided by engineer. */ -"Receipts are disabled" = "Üzenet kézbesítési jelentés letiltva"; +"Receive errors" = "Üzenetfogadási hibák"; /* No comment provided by engineer. */ -"received answer…" = "fogadott válasz…"; +"received answer…" = "válasz fogadása…"; /* No comment provided by engineer. */ -"Received at" = "Fogadva ekkor"; +"Received at" = "Fogadva"; /* copied message info */ -"Received at: %@" = "Fogadva ekkor: %@"; +"Received at: %@" = "Fogadva: %@"; /* No comment provided by engineer. */ "received confirmation…" = "visszaigazolás fogadása…"; /* notification */ -"Received file event" = "Fogadott fájl esemény"; +"Received file event" = "Fogadott fájlesemény"; /* message info title */ -"Received message" = "Fogadott üzenet"; +"Received message" = "Fogadott üzenetbuborék színe"; /* No comment provided by engineer. */ -"Receiving address will be changed to a different server. Address change will complete after sender comes online." = "A fogadó cím egy másik kiszolgálóra változik. A címváltoztatás a feladó online állapotba kerülése után fejeződik be."; +"Received messages" = "Fogadott üzenetek"; /* No comment provided by engineer. */ -"Receiving file will be stopped." = "A fájl fogadása leállt."; +"Received reply" = "Fogadott válaszüzenet-buborék színe"; /* No comment provided by engineer. */ -"Receiving via" = "Fogadás a"; +"Received total" = "Összes fogadott üzenet"; /* No comment provided by engineer. */ -"Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)." = "Legutóbbi előzmények és továbbfejlesztett [könyvtárbot] (simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2TxW3dfMfxy 3%23%2F%3Fv%3D1-2% 26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gloncbqjek4gloncbqjek."; +"Receiving address will be changed to a different server. Address change will complete after sender comes online." = "A fogadási cím egy másik kiszolgálóra fog módosulni. A cím módosítása a feladó online állapotba kerülése után fejeződik be."; /* No comment provided by engineer. */ -"Recipients see updates as you type them." = "A címzettek a beírás közben látják a frissítéseket."; +"Receiving file will be stopped." = "A fájl fogadása le fog állni."; /* No comment provided by engineer. */ -"Reconnect all connected servers to force message delivery. It uses additional traffic." = "Az összes csatlakoztatott kiszolgáló újrakapcsolása az üzenetek kézbesítésének kikényszerítéséhez. Ez további forgalmat használ."; +"Receiving via" = "Fogadás a következőn keresztül:"; /* No comment provided by engineer. */ -"Reconnect servers?" = "Kiszolgálók újracsatlakoztatása?"; +"Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)." = "Legutóbbi előzmények és továbbfejlesztett [könyvtárbot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)."; /* No comment provided by engineer. */ -"Record updated at" = "A bejegyzés frissítve"; +"Recipient(s) can't see who this message is from." = "A címzett(ek) nem látja(k), hogy kitől származik ez az üzenet."; + +/* No comment provided by engineer. */ +"Recipients see updates as you type them." = "A címzettek a beírás közben látják a szövegváltozásokat."; + +/* No comment provided by engineer. */ +"Reconnect" = "Újrakapcsolódás"; + +/* No comment provided by engineer. */ +"Reconnect all connected servers to force message delivery. It uses additional traffic." = "Az összes kiszolgálóhoz való újrakapcsolódás az üzenetkézbesítési jelentések kikényszerítéséhez. Ez további adatforgalmat használ."; + +/* No comment provided by engineer. */ +"Reconnect all servers" = "Újrakapcsolódás az összes kiszolgálóhoz"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "Újrakapcsolódik az összes kiszolgálóhoz?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "A kiszolgálóhoz való újrakapcsolódás az üzenetkézbesítési jelentések kikényszerítéséhez. Ez további adatforgalmat használ."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "Újrakapcsolódik a kiszolgálóhoz?"; + +/* No comment provided by engineer. */ +"Reconnect servers?" = "Újrakapcsolódik a kiszolgálókhoz?"; + +/* No comment provided by engineer. */ +"Record updated at" = "Bejegyzés frissítve"; /* copied message info */ -"Record updated at: %@" = "A bejegyzés frissítve: %@"; +"Record updated at: %@" = "Bejegyzés frissítve: %@"; /* No comment provided by engineer. */ -"Reduced battery usage" = "Csökkentett akkumulátorhasználat"; +"Reduced battery usage" = "Csökkentett akkumulátor-használat"; -/* reject incoming call via notification */ +/* No comment provided by engineer. */ +"Register" = "Regisztrálás"; + +/* token info */ +"Register notification token?" = "Regisztrálja az értesítési tokent?"; + +/* token status text */ +"Registered" = "Regisztrálva"; + +/* reject incoming call via notification +swipe action */ "Reject" = "Elutasítás"; /* No comment provided by engineer. */ "Reject (sender NOT notified)" = "Elutasítás (a feladó NEM kap értesítést)"; /* No comment provided by engineer. */ -"Reject contact request" = "Kapcsolatfelvételi kérelem elutasítása"; +"Reject contact request" = "Meghívási kérés elutasítása"; + +/* No comment provided by engineer. */ +"rejected" = "elutasítva"; /* call status */ "rejected call" = "elutasított hívás"; /* No comment provided by engineer. */ -"Relay server is only used if necessary. Another party can observe your IP address." = "Az átjátszó kiszolgáló csak szükség esetén kerül használatra. Egy másik fél megfigyelheti az IP-címét."; +"Relay server is only used if necessary. Another party can observe your IP address." = "A továbbítókiszolgáló csak szükség esetén lesz használva. Egy másik fél megfigyelheti az IP-címet."; /* No comment provided by engineer. */ -"Relay server protects your IP address, but it can observe the duration of the call." = "Az átjátszó kiszolgáló megvédi IP-címét, de megfigyelheti a hívás időtartamát."; +"Relay server protects your IP address, but it can observe the duration of the call." = "A továbbítókiszolgáló megvédi az Ön IP-címét, de megfigyelheti a hívás időtartamát."; /* No comment provided by engineer. */ "Remove" = "Eltávolítás"; /* No comment provided by engineer. */ -"Remove member" = "Tag eltávolítása"; +"Remove archive?" = "Eltávolítja az archívumot?"; /* No comment provided by engineer. */ -"Remove member?" = "Tag eltávolítása?"; +"Remove image" = "Kép eltávolítása"; /* No comment provided by engineer. */ -"Remove passphrase from keychain?" = "Jelmondat eltávolítása a kulcstárolóból?"; +"Remove member" = "Eltávolítás"; + +/* No comment provided by engineer. */ +"Remove member?" = "Eltávolítja a tagot?"; + +/* No comment provided by engineer. */ +"Remove passphrase from keychain?" = "Eltávolítja a jelmondatot a kulcstartóból?"; /* No comment provided by engineer. */ "removed" = "eltávolítva"; /* rcv group event chat item */ -"removed %@" = "%@ eltávolítva"; +"removed %@" = "eltávolította őt: %@"; /* profile update event chat item */ -"removed contact address" = "törölt csatlakozási cím"; +"removed contact address" = "eltávolította a kapcsolattartási címet"; /* profile update event chat item */ -"removed profile picture" = "törölt profilkép"; +"removed profile picture" = "eltávolította a profilképét"; /* rcv group event chat item */ -"removed you" = "eltávolítottak"; +"removed you" = "eltávolította Önt"; /* No comment provided by engineer. */ -"Renegotiate" = "Újraegyzetetés"; +"Renegotiate" = "Újraegyeztetés"; /* No comment provided by engineer. */ "Renegotiate encryption" = "Titkosítás újraegyeztetése"; /* No comment provided by engineer. */ -"Renegotiate encryption?" = "Titkosítás újraegyeztetése?"; +"Renegotiate encryption?" = "Újraegyezteti a titkosítást?"; /* No comment provided by engineer. */ -"Repeat connection request?" = "Kapcsolódási kérés megismétlése?"; +"Repeat connection request?" = "Megismétli a meghívási kérést?"; /* No comment provided by engineer. */ -"Repeat join request?" = "Csatlakozási kérés megismétlése?"; +"Repeat download" = "Letöltés ismét"; + +/* No comment provided by engineer. */ +"Repeat import" = "Importálás ismét"; + +/* No comment provided by engineer. */ +"Repeat join request?" = "Megismétli a meghívási kérést?"; + +/* No comment provided by engineer. */ +"Repeat upload" = "Feltöltés ismét"; /* chat item action */ "Reply" = "Válasz"; -/* No comment provided by engineer. */ -"Required" = "Megkövetelt"; +/* chat item action */ +"Report" = "Jelentés"; + +/* report reason */ +"Report content: only group moderators will see it." = "Tartalom jelentése: csak a csoport moderátorai látják."; + +/* report reason */ +"Report member profile: only group moderators will see it." = "Tag profiljának jelentése: csak a csoport moderátorai látják."; + +/* report reason */ +"Report other: only group moderators will see it." = "Egyéb jelentés: csak a csoport moderátorai látják."; /* No comment provided by engineer. */ -"Reset" = "Alaphelyzetbe állítás"; +"Report reason?" = "Jelentés indoklása?"; + +/* report reason */ +"Report spam: only group moderators will see it." = "Kéretlen tartalom jelentése: csak a csoport moderátorai látják."; + +/* report reason */ +"Report violation: only group moderators will see it." = "Szabálysértés jelentése: csak a csoport moderátorai látják."; + +/* report in notification */ +"Report: %@" = "Jelentés: %@"; /* No comment provided by engineer. */ -"Reset colors" = "Színek alaphelyzetbe állítása"; +"Reporting messages to moderators is prohibited." = "Az üzenetek jelentése a moderátorok felé le van tiltva."; /* No comment provided by engineer. */ -"Reset to defaults" = "Alaphelyzetbe állítás"; +"Reports" = "Jelentések"; + +/* chat list item title */ +"requested to connect" = "Függőben lévő meghívási kérelem"; + +/* No comment provided by engineer. */ +"Required" = "Szükséges"; + +/* No comment provided by engineer. */ +"Reset" = "Visszaállítás"; + +/* No comment provided by engineer. */ +"Reset all hints" = "Tippek visszaállítása"; + +/* No comment provided by engineer. */ +"Reset all statistics" = "Az összes statisztika visszaállítása"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "Visszaállítja az összes statisztikát?"; + +/* No comment provided by engineer. */ +"Reset colors" = "Színek visszaállítása"; + +/* No comment provided by engineer. */ +"Reset to app theme" = "Alkalmazás témájának visszaállítása"; + +/* No comment provided by engineer. */ +"Reset to defaults" = "Visszaállítás alapértelmezettre"; + +/* No comment provided by engineer. */ +"Reset to user theme" = "Felhasználó által létrehozott téma visszaállítása"; /* No comment provided by engineer. */ "Restart the app to create a new chat profile" = "Új csevegési profil létrehozásához indítsa újra az alkalmazást"; @@ -2964,10 +4291,10 @@ "Restore database backup" = "Adatbázismentés visszaállítása"; /* No comment provided by engineer. */ -"Restore database backup?" = "Adatbázismentés visszaállítása?"; +"Restore database backup?" = "Visszaállítja az adatbázismentést?"; /* No comment provided by engineer. */ -"Restore database error" = "Hiba az adatbázis visszaállításakor"; +"Restore database error" = "Hiba történt az adatbázis visszaállításakor"; /* No comment provided by engineer. */ "Retry" = "Újrapróbálkozás"; @@ -2976,7 +4303,7 @@ "Reveal" = "Felfedés"; /* No comment provided by engineer. */ -"Revert" = "Visszaállít"; +"Review conditions" = "Feltételek felülvizsgálata"; /* No comment provided by engineer. */ "Revoke" = "Visszavonás"; @@ -2985,7 +4312,7 @@ "Revoke file" = "Fájl visszavonása"; /* No comment provided by engineer. */ -"Revoke file?" = "Fájl visszavonása?"; +"Revoke file?" = "Visszavonja a fájlt?"; /* No comment provided by engineer. */ "Role" = "Szerepkör"; @@ -2993,87 +4320,121 @@ /* No comment provided by engineer. */ "Run chat" = "Csevegési szolgáltatás indítása"; -/* chat item action */ +/* No comment provided by engineer. */ +"Safely receive files" = "Fájlok biztonságos fogadása"; + +/* No comment provided by engineer. */ +"Safer groups" = "Biztonságosabb csoportok"; + +/* alert button +chat item action */ "Save" = "Mentés"; -/* No comment provided by engineer. */ -"Save (and notify contacts)" = "Mentés (és az ismerősök értesítése)"; +/* alert button */ +"Save (and notify contacts)" = "Mentés (és a partnerek értesítése)"; -/* No comment provided by engineer. */ -"Save and notify contact" = "Mentés és ismerős értesítése"; +/* alert button */ +"Save and notify contact" = "Mentés és a partner értesítése"; /* No comment provided by engineer. */ "Save and notify group members" = "Mentés és a csoporttagok értesítése"; /* No comment provided by engineer. */ -"Save and update group profile" = "Mentés és a csoport profil frissítése"; +"Save and reconnect" = "Mentés és újrakapcsolódás"; /* No comment provided by engineer. */ -"Save archive" = "Archívum mentése"; +"Save and update group profile" = "Mentés és a csoportprofil frissítése"; /* No comment provided by engineer. */ -"Save auto-accept settings" = "Automatikus elfogadási beállítások mentése"; +"Save group profile" = "Csoportprofil mentése"; /* No comment provided by engineer. */ -"Save group profile" = "Csoport profil elmentése"; +"Save list" = "Lista mentése"; /* No comment provided by engineer. */ -"Save passphrase and open chat" = "Jelmondat elmentése és csevegés megnyitása"; +"Save passphrase and open chat" = "Jelmondat mentése és a csevegés megnyitása"; /* No comment provided by engineer. */ -"Save passphrase in Keychain" = "Jelmondat mentése a kulcstárban"; +"Save passphrase in Keychain" = "Jelmondat mentése a kulcstartóba"; + +/* alert title */ +"Save preferences?" = "Menti a beállításokat?"; /* No comment provided by engineer. */ -"Save preferences?" = "Beállítások mentése?"; - -/* No comment provided by engineer. */ -"Save profile password" = "Felhasználói fiók jelszavának mentése"; +"Save profile password" = "Profiljelszó mentése"; /* No comment provided by engineer. */ "Save servers" = "Kiszolgálók mentése"; -/* No comment provided by engineer. */ -"Save servers?" = "Kiszolgálók mentése?"; +/* alert title */ +"Save servers?" = "Menti a kiszolgálókat?"; /* No comment provided by engineer. */ -"Save settings?" = "Beállítások mentése?"; +"Save welcome message?" = "Menti az üdvözlőüzenetet?"; + +/* alert title */ +"Save your profile?" = "Menti a profilt?"; /* No comment provided by engineer. */ -"Save welcome message?" = "Üdvözlőszöveg mentése?"; +"saved" = "mentett"; + +/* No comment provided by engineer. */ +"Saved" = "Mentett"; + +/* No comment provided by engineer. */ +"Saved from" = "Elmentve innen"; + +/* No comment provided by engineer. */ +"saved from %@" = "elmentve innen: %@"; /* message info title */ "Saved message" = "Mentett üzenet"; /* No comment provided by engineer. */ -"Saved WebRTC ICE servers will be removed" = "A mentett WebRTC ICE kiszolgálók eltávolításra kerülnek"; +"Saved WebRTC ICE servers will be removed" = "A mentett WebRTC ICE-kiszolgálók el lesznek távolítva"; /* No comment provided by engineer. */ -"Scan code" = "Kód beolvasása"; +"Saving %lld messages" = "%lld üzenet mentése"; + +/* No comment provided by engineer. */ +"Scale" = "Méretezés"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Hivatkozás beolvasása / beillesztése"; + +/* No comment provided by engineer. */ +"Scan code" = "Beolvasás"; /* No comment provided by engineer. */ "Scan QR code" = "QR-kód beolvasása"; /* No comment provided by engineer. */ -"Scan QR code from desktop" = "QR-kód beolvasása számítógépről"; +"Scan QR code from desktop" = "QR-kód beolvasása a számítógépről"; /* No comment provided by engineer. */ -"Scan security code from your contact's app." = "Biztonsági kód beolvasása ismerős általi alkalmazásból."; +"Scan security code from your contact's app." = "Biztonsági kód beolvasása a partnere alkalmazásából."; /* No comment provided by engineer. */ "Scan server QR code" = "A kiszolgáló QR-kódjának beolvasása"; +/* No comment provided by engineer. */ +"search" = "keresés"; + /* No comment provided by engineer. */ "Search" = "Keresés"; /* No comment provided by engineer. */ -"Search bar accepts invitation links." = "A keresősáv fogadja a meghívó hivatkozásokat."; +"Search bar accepts invitation links." = "A keresősáv elfogadja a meghívási hivatkozásokat."; /* No comment provided by engineer. */ -"Search or paste SimpleX link" = "Keresés, vagy SimpleX hivatkozás beillesztése"; +"Search or paste SimpleX link" = "Keresés vagy SimpleX-hivatkozás beillesztése"; /* network option */ "sec" = "mp"; +/* No comment provided by engineer. */ +"Secondary" = "Másodlagos szín"; + /* time unit */ "seconds" = "másodperc"; @@ -3081,7 +4442,10 @@ "secret" = "titok"; /* server test step */ -"Secure queue" = "Biztonságos várólista"; +"Secure queue" = "Biztonságos sorba állítás"; + +/* No comment provided by engineer. */ +"Secured" = "Biztosítva"; /* No comment provided by engineer. */ "Security assessment" = "Biztonsági kiértékelés"; @@ -3090,28 +4454,37 @@ "Security code" = "Biztonsági kód"; /* chat item text */ -"security code changed" = "biztonsági kód megváltozott"; +"security code changed" = "a biztonsági kód módosult"; + +/* chat item action */ +"Select" = "Kijelölés"; /* No comment provided by engineer. */ -"Select" = "Választás"; +"Select chat profile" = "Csevegési profil kijelölése"; + +/* No comment provided by engineer. */ +"Selected %lld" = "%lld kijelölve"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "A kijelölt csevegési beállítások tiltják ezt az üzenetet."; /* No comment provided by engineer. */ "Self-destruct" = "Önmegsemmisítés"; /* No comment provided by engineer. */ -"Self-destruct passcode" = "Önmegsemmisítési jelkód"; +"Self-destruct passcode" = "Önmegsemmisítő-jelkód"; /* No comment provided by engineer. */ -"Self-destruct passcode changed!" = "Az önmegsemmisítési jelkód megváltozott!"; +"Self-destruct passcode changed!" = "Az önmegsemmisítő-jelkód módosult!"; /* No comment provided by engineer. */ -"Self-destruct passcode enabled!" = "Az önmegsemmisítési jelkód engedélyezve!"; +"Self-destruct passcode enabled!" = "Az önmegsemmisítő-jelkód engedélyezve!"; /* No comment provided by engineer. */ "Send" = "Küldés"; /* No comment provided by engineer. */ -"Send a live message - it will update for the recipient(s) as you type it" = "Élő üzenet küldése - a címzett(ek) számára frissül, ahogy beírja"; +"Send a live message - it will update for the recipient(s) as you type it" = "Élő üzenet küldése – az üzenet a címzett(ek) számára valós időben frissül, ahogy Ön beírja az üzenetet"; /* No comment provided by engineer. */ "Send delivery receipts to" = "A kézbesítési jelentéseket a következő címre kell küldeni"; @@ -3120,95 +4493,161 @@ "send direct message" = "közvetlen üzenet küldése"; /* No comment provided by engineer. */ -"Send direct message" = "Közvetlen üzenet küldése"; - -/* No comment provided by engineer. */ -"Send direct message to connect" = "A kapcsolódáshoz közvetlen üzenet küldése"; +"Send direct message to connect" = "Közvetlen üzenet küldése a kapcsolódáshoz"; /* No comment provided by engineer. */ "Send disappearing message" = "Eltűnő üzenet küldése"; /* No comment provided by engineer. */ -"Send link previews" = "Hivatkozás előnézetek küldése"; +"Send errors" = "Üzenetküldési hibák"; + +/* No comment provided by engineer. */ +"Send link previews" = "Hivatkozás előnézete"; /* No comment provided by engineer. */ "Send live message" = "Élő üzenet küldése"; +/* No comment provided by engineer. */ +"Send message to enable calls." = "Üzenet küldése a hívások engedélyezéséhez."; + +/* No comment provided by engineer. */ +"Send messages directly when IP address is protected and your or destination server does not support private routing." = "Közvetlen üzenetküldés, ha az IP-cím védett és a saját kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást."; + +/* No comment provided by engineer. */ +"Send messages directly when your or destination server does not support private routing." = "Közvetlen üzenetküldés, ha a saját kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást."; + /* No comment provided by engineer. */ "Send notifications" = "Értesítések küldése"; /* No comment provided by engineer. */ -"Send notifications:" = "Értesítések küldése:"; +"Send private reports" = "Privát jelentések küldése"; /* No comment provided by engineer. */ -"Send questions and ideas" = "Ötletek és kérdések beküldése"; +"Send questions and ideas" = "Ötletek és javaslatok"; /* No comment provided by engineer. */ -"Send receipts" = "Üzenet kézbesítési jelentések"; +"Send receipts" = "Kézbesítési jelentések küldése"; /* No comment provided by engineer. */ -"Send them from gallery or custom keyboards." = "Küldje el őket galériából vagy egyedi billentyűzetekről."; +"Send them from gallery or custom keyboards." = "Küldje el őket a galériából vagy az egyéni billentyűzetekről."; /* No comment provided by engineer. */ -"Send up to 100 last messages to new members." = "Utolsó 100 üzenet küldése új tagoknak."; +"Send up to 100 last messages to new members." = "Legfeljebb az utolsó 100 üzenet elküldése az új tagok számára."; + +/* alert message */ +"Sender cancelled file transfer." = "A fájl küldője visszavonta az átvitelt."; /* No comment provided by engineer. */ -"Sender cancelled file transfer." = "A küldő megszakította a fájl átvitelt."; +"Sender may have deleted the connection request." = "A küldője törölhette a meghívási kérést."; /* No comment provided by engineer. */ -"Sender may have deleted the connection request." = "A küldő törölhette a kapcsolódási kérelmet."; +"Sending delivery receipts will be enabled for all contacts in all visible chat profiles." = "A kézbesítési jelentések küldése engedélyezve lesz az összes látható csevegési profilban lévő összes partnere számára."; /* No comment provided by engineer. */ -"Sending delivery receipts will be enabled for all contacts in all visible chat profiles." = "A kézbesítési jelentések küldése engedélyezésre kerül az összes látható csevegési profilban lévő minden ismerős számára."; +"Sending delivery receipts will be enabled for all contacts." = "A kézbesítési jelentések küldése az összes partnere számára engedélyezve lesz."; /* No comment provided by engineer. */ -"Sending delivery receipts will be enabled for all contacts." = "A kézbesítési jelentés küldése minden ismerős számára engedélyezésre kerül."; +"Sending file will be stopped." = "A fájl küldése le fog állni."; /* No comment provided by engineer. */ -"Sending file will be stopped." = "A fájl küldése leállt."; +"Sending receipts is disabled for %lld contacts" = "A kézbesítési jelentések le vannak tiltva %lld partnernél"; /* No comment provided by engineer. */ -"Sending receipts is disabled for %lld contacts" = "A kézbesítési jelentések küldése le van tiltva %lld ismerősnél"; +"Sending receipts is disabled for %lld groups" = "A kézbesítési jelentések le vannak tiltva %lld csoportban"; /* No comment provided by engineer. */ -"Sending receipts is disabled for %lld groups" = "A kézbesítési jelentések küldése le van tiltva %lld csoportban"; +"Sending receipts is enabled for %lld contacts" = "A kézbesítési jelentések engedélyezve vannak %lld partnernél"; /* No comment provided by engineer. */ -"Sending receipts is enabled for %lld contacts" = "A kézbesítési jelentések küldése engedélyezve van %lld ismerős számára"; +"Sending receipts is enabled for %lld groups" = "A kézbesítési jelentések engedélyezve vannak %lld csoportban"; /* No comment provided by engineer. */ -"Sending receipts is enabled for %lld groups" = "A kézbesítési jelentések küldése engedélyezve van %lld csoportban"; +"Sending via" = "Küldés a következőn keresztül:"; /* No comment provided by engineer. */ -"Sending via" = "Küldés ezen keresztül"; - -/* No comment provided by engineer. */ -"Sent at" = "Elküldve ekkor"; +"Sent at" = "Elküldve"; /* copied message info */ -"Sent at: %@" = "Elküldve ekkor: %@"; +"Sent at: %@" = "Elküldve: %@"; + +/* No comment provided by engineer. */ +"Sent directly" = "Közvetlenül küldött"; /* notification */ -"Sent file event" = "Elküldött fájl esemény"; +"Sent file event" = "Elküldött fájlesemény"; /* message info title */ -"Sent message" = "Elküldött üzenet"; +"Sent message" = "Üzenetbuborék színe"; /* No comment provided by engineer. */ -"Sent messages will be deleted after set time." = "Az elküldött üzenetek törlésre kerülnek a beállított idő után."; - -/* server test error */ -"Server requires authorization to create queues, check password" = "A kiszolgálónak engedélyre van szüksége a várólisták létrehozásához, ellenőrizze jelszavát"; - -/* server test error */ -"Server requires authorization to upload, check password" = "A kiszolgálónak engedélyre van szüksége a várólisták feltöltéséhez, ellenőrizze jelszavát"; +"Sent messages" = "Elküldött üzenetek"; /* No comment provided by engineer. */ -"Server test failed!" = "A kiszolgáló tesztje sikertelen!"; +"Sent messages will be deleted after set time." = "Az elküldött üzenetek törölve lesznek a beállított idő után."; + +/* No comment provided by engineer. */ +"Sent reply" = "Válaszüzenet-buborék színe"; + +/* No comment provided by engineer. */ +"Sent total" = "Összes elküldött üzenet"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Proxyn keresztül küldött"; + +/* No comment provided by engineer. */ +"Server" = "Kiszolgáló"; + +/* alert message */ +"Server added to operator %@." = "Kiszolgáló hozzáadva a következő üzemeltetőhöz: %@."; + +/* No comment provided by engineer. */ +"Server address" = "Kiszolgáló címe"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "A kiszolgáló címe nem kompatibilis a hálózati beállításokkal: %@."; + +/* srv error text. */ +"Server address is incompatible with network settings." = "A kiszolgáló címe nem kompatibilis a hálózati beállításokkal."; + +/* alert title */ +"Server operator changed." = "A kiszolgáló üzemeltetője módosult."; + +/* No comment provided by engineer. */ +"Server operators" = "Kiszolgálóüzemeltetők"; + +/* alert title */ +"Server protocol changed." = "A kiszolgáló-protokoll módosult."; + +/* queue info */ +"server queue info: %@\n\nlast received msg: %@" = "a kiszolgáló sorbaállítási információi: %1$@\n\nutoljára fogadott üzenet: %2$@"; + +/* server test error */ +"Server requires authorization to create queues, check password" = "A kiszolgálónak engedélyre van szüksége a sorba állítás létrehozásához, ellenőrizze a jelszavát"; + +/* server test error */ +"Server requires authorization to upload, check password" = "A kiszolgálónak hitelesítésre van szüksége a feltöltéshez, ellenőrizze jelszavát"; + +/* No comment provided by engineer. */ +"Server test failed!" = "Sikertelen kiszolgáló teszt!"; + +/* No comment provided by engineer. */ +"Server type" = "Kiszolgáló típusa"; + +/* srv error text */ +"Server version is incompatible with network settings." = "A kiszolgáló verziója nem kompatibilis a hálózati beállításokkal."; + +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "A kiszolgáló verziója nem kompatibilis az alkalmazással: %@."; /* No comment provided by engineer. */ "Servers" = "Kiszolgálók"; +/* No comment provided by engineer. */ +"Servers info" = "Információk a kiszolgálókról"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "A kiszolgálók statisztikái visszaállnak – ez a művelet nem vonható vissza!"; + /* No comment provided by engineer. */ "Session code" = "Munkamenet kód"; @@ -3216,28 +4655,40 @@ "Set 1 day" = "Beállítva 1 nap"; /* No comment provided by engineer. */ -"Set contact name…" = "Ismerős nevének beállítása…"; +"Set chat name…" = "Csevegés nevének beállítása…"; + +/* No comment provided by engineer. */ +"Set contact name…" = "Partner nevének beállítása…"; + +/* No comment provided by engineer. */ +"Set default theme" = "Alapértelmezett téma beállítása"; /* No comment provided by engineer. */ "Set group preferences" = "Csoportbeállítások megadása"; /* No comment provided by engineer. */ -"Set it instead of system authentication." = "Rendszerhitelesítés helyetti beállítás."; +"Set it instead of system authentication." = "Beállítás a rendszer-hitelesítés helyett."; + +/* No comment provided by engineer. */ +"Set message expiration in chats." = "Üzenetek eltűnési idejének módosítása a csevegésekben."; /* profile update event chat item */ -"set new contact address" = "új kapcsolattartási cím beállítása"; +"set new contact address" = "új kapcsolattartási címet állított be"; /* profile update event chat item */ -"set new profile picture" = "új profilkép beállítása"; +"set new profile picture" = "új profilképet állított be"; /* No comment provided by engineer. */ "Set passcode" = "Jelkód beállítása"; +/* No comment provided by engineer. */ +"Set passphrase" = "Jelmondat beállítása"; + /* No comment provided by engineer. */ "Set passphrase to export" = "Jelmondat beállítása az exportáláshoz"; /* No comment provided by engineer. */ -"Set the message shown to new members!" = "Megjelenő üzenetet beállítása új tagok részére!"; +"Set the message shown to new members!" = "Megjelenítendő üzenet beállítása az új tagok számára!"; /* No comment provided by engineer. */ "Set timeouts for proxy/VPN" = "Időtúllépések beállítása a proxy/VPN számára"; @@ -3245,80 +4696,147 @@ /* No comment provided by engineer. */ "Settings" = "Beállítások"; -/* chat item action */ +/* alert message */ +"Settings were changed." = "A beállítások módosultak."; + +/* No comment provided by engineer. */ +"Shape profile images" = "Profilkép alakzata"; + +/* alert action +chat item action */ "Share" = "Megosztás"; /* No comment provided by engineer. */ -"Share 1-time link" = "Egyszer használatos hivatkozás megosztása"; +"Share 1-time link" = "Egyszer használható meghívó megosztása"; /* No comment provided by engineer. */ -"Share address" = "Azonosító megosztása"; +"Share 1-time link with a friend" = "Egyszer használható meghívó megosztása egy baráttal"; /* No comment provided by engineer. */ -"Share address with contacts?" = "Megosztja az azonosítót az ismerősökkel?"; +"Share address" = "Cím megosztása"; /* No comment provided by engineer. */ -"Share link" = "Hivatkozás megosztása"; +"Share address publicly" = "Cím nyilvános megosztása"; + +/* alert title */ +"Share address with contacts?" = "Megosztja a címet a partnereivel?"; /* No comment provided by engineer. */ -"Share this 1-time invite link" = "Egyszer használatos meghívó hivatkozás megosztása"; +"Share from other apps." = "Megosztás más alkalmazásokból."; /* No comment provided by engineer. */ -"Share with contacts" = "Megosztás ismerősökkel"; +"Share link" = "Megosztás"; + +/* No comment provided by engineer. */ +"Share profile" = "Profil megosztása"; + +/* No comment provided by engineer. */ +"Share SimpleX address on social media." = "SimpleX-cím megosztása a közösségi médiában."; + +/* No comment provided by engineer. */ +"Share this 1-time invite link" = "Ennek az egyszer használható meghívónak a megosztása"; + +/* No comment provided by engineer. */ +"Share to SimpleX" = "Megosztás a SimpleXben"; + +/* No comment provided by engineer. */ +"Share with contacts" = "Megosztás a partnerekkel"; + +/* No comment provided by engineer. */ +"Short link" = "Rövid hivatkozás"; + +/* No comment provided by engineer. */ +"Show → on messages sent via private routing." = "Egy „→” jel megjelenítése a privát útválasztáson keresztül küldött üzeneteknél."; /* No comment provided by engineer. */ "Show calls in phone history" = "Hívások megjelenítése a híváslistában"; /* No comment provided by engineer. */ -"Show developer options" = "Fejlesztői beállítások mutatása"; +"Show developer options" = "Fejlesztői beállítások megjelenítése"; /* No comment provided by engineer. */ -"Show last messages" = "Utolsó üzenetek megjelenítése"; +"Show last messages" = "Legutóbbi üzenet előnézetének megjelenítése"; /* No comment provided by engineer. */ -"Show preview" = "Előnézet megjelenítése"; +"Show message status" = "Üzenet állapotának megjelenítése"; /* No comment provided by engineer. */ -"Show:" = "Mutat:"; +"Show percentage" = "Százalék megjelenítése"; /* No comment provided by engineer. */ -"SimpleX address" = "SimpleX azonosító"; +"Show preview" = "Értesítés előnézete"; /* No comment provided by engineer. */ -"SimpleX Address" = "SimpleX azonosító"; +"Show QR code" = "QR-kód megjelenítése"; + +/* No comment provided by engineer. */ +"Show:" = "Megjelenítve:"; + +/* No comment provided by engineer. */ +"SimpleX" = "SimpleX"; + +/* No comment provided by engineer. */ +"SimpleX address" = "SimpleX-cím"; + +/* No comment provided by engineer. */ +"SimpleX Address" = "SimpleX-cím"; + +/* No comment provided by engineer. */ +"SimpleX address and 1-time links are safe to share via any messenger." = "A SimpleX-cím és az egyszer használható meghívó biztonságosan megosztható bármilyen üzenetváltó-alkalmazáson keresztül."; + +/* No comment provided by engineer. */ +"SimpleX address or 1-time link?" = "SimpleX-cím vagy egyszer használható meghívó?"; + +/* simplex link type */ +"SimpleX channel link" = "SimpleX-csatornahivatkozás"; + +/* No comment provided by engineer. */ +"SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app." = "A SimpleX Chat és a Flux megállapodást kötött arról, hogy a Flux által üzemeltetett kiszolgálókat beépítik az alkalmazásba."; /* No comment provided by engineer. */ "SimpleX Chat security was audited by Trail of Bits." = "A SimpleX Chat biztonsága a Trail of Bits által lett auditálva."; /* simplex link type */ -"SimpleX contact address" = "SimpleX ismerős azonosítója"; +"SimpleX contact address" = "SimpleX kapcsolattartási cím"; /* notification */ "SimpleX encrypted message or connection event" = "SimpleX titkosított üzenet vagy kapcsolati esemény"; /* simplex link type */ -"SimpleX group link" = "SimpleX csoport hivatkozás"; +"SimpleX group link" = "SimpleX-csoporthivatkozás"; + +/* chat feature */ +"SimpleX links" = "SimpleX-hivatkozások"; /* No comment provided by engineer. */ -"SimpleX links" = "SimpleX hivatkozások"; +"SimpleX links are prohibited." = "A SimpleX-hivatkozások küldése le van tiltva."; /* No comment provided by engineer. */ -"SimpleX Lock" = "SimpleX zárolás"; +"SimpleX links not allowed" = "A SimpleX-hivatkozások küldése le van tiltva"; /* No comment provided by engineer. */ -"SimpleX Lock mode" = "SimpleX zárolási mód"; +"SimpleX Lock" = "SimpleX-zár"; /* No comment provided by engineer. */ -"SimpleX Lock not enabled!" = "SimpleX zárolás nincs engedélyezve!"; +"SimpleX Lock mode" = "Zárolási mód"; /* No comment provided by engineer. */ -"SimpleX Lock turned on" = "SimpleX zárolás bekapcsolva"; +"SimpleX Lock not enabled!" = "A SimpleX-zár nincs bekapcsolva!"; + +/* No comment provided by engineer. */ +"SimpleX Lock turned on" = "SimpleX-zár bekapcsolva"; /* simplex link type */ -"SimpleX one-time invitation" = "SimpleX egyszer használatos meghívó"; +"SimpleX one-time invitation" = "Egyszer használható SimpleX-meghívó"; /* No comment provided by engineer. */ -"Simplified incognito mode" = "Egyszerűsített inkognító mód"; +"SimpleX protocols reviewed by Trail of Bits." = "A SimpleX Chat biztonsága a Trail of Bits által lett felülvizsgálva."; + +/* No comment provided by engineer. */ +"Simplified incognito mode" = "Egyszerűsített inkognitómód"; + +/* No comment provided by engineer. */ +"Size" = "Méret"; /* No comment provided by engineer. */ "Skip" = "Kihagyás"; @@ -3330,100 +4848,167 @@ "Small groups (max 20)" = "Kis csoportok (max. 20 tag)"; /* No comment provided by engineer. */ -"SMP servers" = "Üzenetküldő (SMP) kiszolgálók"; +"SMP server" = "SMP-kiszolgáló"; /* No comment provided by engineer. */ -"Some non-fatal errors occurred during import - you may see Chat console for more details." = "Néhány nem végzetes hiba történt az importálás során – további részletekért a csevegési konzolban olvashat."; +"SOCKS proxy" = "SOCKS-proxy"; + +/* blur media */ +"Soft" = "Enyhe"; + +/* No comment provided by engineer. */ +"Some app settings were not migrated." = "Egyes alkalmazásbeállítások nem lettek átköltöztetve."; + +/* No comment provided by engineer. */ +"Some file(s) were not exported:" = "Néhány fájl nem lett exportálva:"; + +/* No comment provided by engineer. */ +"Some non-fatal errors occurred during import - you may see Chat console for more details." = "Néhány nem végzetes hiba történt az importáláskor – további részleteket a csevegési konzolban olvashat."; + +/* No comment provided by engineer. */ +"Some non-fatal errors occurred during import:" = "Néhány nem végzetes hiba történt az importáláskor:"; + +/* alert message */ +"Some servers failed the test:\n%@" = "Néhány kiszolgáló megbukott a teszten:\n%@"; /* notification title */ "Somebody" = "Valaki"; +/* blocking reason +report reason */ +"Spam" = "Kéretlen tartalom"; + +/* No comment provided by engineer. */ +"Square, circle, or anything in between." = "Négyzet, kör vagy bármi a kettő között."; + +/* chat item text */ +"standard end-to-end encryption" = "szabványos végpontok közötti titkosítás"; + /* No comment provided by engineer. */ "Start chat" = "Csevegés indítása"; /* No comment provided by engineer. */ -"Start chat?" = "Csevegés indítása?"; +"Start chat?" = "Elindítja a csevegést?"; /* No comment provided by engineer. */ -"Start migration" = "Migráció indítása"; +"Start migration" = "Átköltöztetés indítása"; + +/* No comment provided by engineer. */ +"Starting from %@." = "Statisztikagyűjtés kezdete: %@."; /* No comment provided by engineer. */ "starting…" = "indítás…"; +/* No comment provided by engineer. */ +"Statistics" = "Statisztikák"; + /* No comment provided by engineer. */ "Stop" = "Megállítás"; /* No comment provided by engineer. */ -"Stop chat to enable database actions" = "Csevegés leállítása az adatbázis-műveletek engedélyezéséhez"; +"Stop chat" = "Csevegési szolgáltatás megállítása"; /* No comment provided by engineer. */ -"Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "A csevegés leállítása a csevegőadatbázis exportálásához, importálásához vagy törléséhez. A csevegés leállítása alatt nem tud üzeneteket fogadni és küldeni."; +"Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "A csevegés megállítása a csevegési adatbázis exportálásához, importálásához vagy törléséhez. A csevegés megállításakor nem tud üzeneteket fogadni és küldeni."; /* No comment provided by engineer. */ -"Stop chat?" = "Csevegési szolgáltatás megállítása?"; +"Stop chat?" = "Megállítja a csevegést?"; /* cancel file action */ "Stop file" = "Fájl megállítása"; /* No comment provided by engineer. */ -"Stop receiving file?" = "Fájl fogadás megszakítása?"; +"Stop receiving file?" = "Megállítja a fájlfogadást?"; /* No comment provided by engineer. */ -"Stop sending file?" = "Fájl küldés megszakítása?"; +"Stop sending file?" = "Megállítja a fájlküldést?"; -/* No comment provided by engineer. */ -"Stop sharing" = "Megosztás leállítása"; +/* alert action */ +"Stop sharing" = "Megosztás megállítása"; -/* No comment provided by engineer. */ -"Stop sharing address?" = "Címmegosztás megállítása?"; +/* alert title */ +"Stop sharing address?" = "Megállítja a címmegosztást?"; /* authentication reason */ -"Stop SimpleX" = "A SimpleX megállítása"; +"Stop SimpleX" = "SimpleX megállítása"; + +/* No comment provided by engineer. */ +"Stopping chat" = "Csevegés megállítása folyamatban"; + +/* No comment provided by engineer. */ +"Storage" = "Tárhely"; /* No comment provided by engineer. */ "strike" = "áthúzott"; +/* blur media */ +"Strong" = "Erős"; + /* No comment provided by engineer. */ "Submit" = "Elküldés"; /* No comment provided by engineer. */ -"Support SimpleX Chat" = "Támogassa a SimpleX Chatet"; +"Subscribed" = "Feliratkozva"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Feliratkozási hibák"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Mellőzött feliratkozások"; + +/* No comment provided by engineer. */ +"Support SimpleX Chat" = "SimpleX Chat támogatása"; + +/* No comment provided by engineer. */ +"Switch audio and video during the call." = "Hang/Videó váltása hívás közben."; + +/* No comment provided by engineer. */ +"Switch chat profile for 1-time invitations." = "Csevegési profilváltás az egyszer használható meghívókhoz."; /* No comment provided by engineer. */ "System" = "Rendszer"; /* No comment provided by engineer. */ -"System authentication" = "Rendszerhitelesítés"; +"System authentication" = "Rendszer-hitelesítés"; /* No comment provided by engineer. */ -"Take picture" = "Fotó készítése"; +"Tail" = "Farok"; /* No comment provided by engineer. */ -"Tap button " = "Koppintson a gombra "; +"Take picture" = "Kép készítése"; + +/* No comment provided by engineer. */ +"Tap button " = "Koppintson a "; + +/* No comment provided by engineer. */ +"Tap Create SimpleX address in the menu to create it later." = "Koppintson a SimpleX-cím létrehozása menüpontra a későbbi létrehozáshoz."; /* No comment provided by engineer. */ "Tap to activate profile." = "A profil aktiválásához koppintson az ikonra."; /* No comment provided by engineer. */ -"Tap to Connect" = "Koppintson a csatlakozáshoz"; +"Tap to Connect" = "Koppintson ide a kapcsolódáshoz"; /* No comment provided by engineer. */ -"Tap to join" = "Koppintson a csatlakozáshoz"; +"Tap to join" = "Koppintson ide a csatlakozáshoz"; /* No comment provided by engineer. */ -"Tap to join incognito" = "Koppintson az inkognitómódhoz való csatlakozáshoz"; +"Tap to join incognito" = "Koppintson ide az inkognitóban való kapcsolódáshoz"; /* No comment provided by engineer. */ -"Tap to paste link" = "Koppintson a hivatkozás beillesztéséhez"; +"Tap to paste link" = "Koppintson ide a hivatkozás beillesztéséhez"; /* No comment provided by engineer. */ -"Tap to scan" = "Koppintson a beolvasáshoz"; +"Tap to scan" = "Koppintson ide a QR-kód beolvasásához"; /* No comment provided by engineer. */ -"Tap to start a new chat" = "Koppintson az új csevegés indításához"; +"TCP connection" = "TCP-kapcsolat"; /* No comment provided by engineer. */ -"TCP connection timeout" = "TCP kapcsolat időtúllépés"; +"TCP connection timeout" = "TCP-kapcsolat időtúllépése"; + +/* No comment provided by engineer. */ +"TCP port for messaging" = "TCP-port az üzenetváltáshoz"; /* No comment provided by engineer. */ "TCP_KEEPCNT" = "TCP_KEEPCNT"; @@ -3434,8 +5019,14 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* file error alert title */ +"Temporary file error" = "Ideiglenes fájlhiba"; + /* server test failure */ -"Test failed at step %@." = "A teszt sikertelen volt a(z) %@ lépésnél."; +"Test failed at step %@." = "A teszt a(z) %@ lépésnél sikertelen volt."; + +/* No comment provided by engineer. */ +"Test notifications" = "Értesítések tesztelése"; /* No comment provided by engineer. */ "Test server" = "Kiszolgáló tesztelése"; @@ -3443,62 +5034,80 @@ /* No comment provided by engineer. */ "Test servers" = "Kiszolgálók tesztelése"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "Sikertelen tesztek!"; /* No comment provided by engineer. */ "Thank you for installing SimpleX Chat!" = "Köszönjük, hogy telepítette a SimpleX Chatet!"; /* No comment provided by engineer. */ -"Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Köszönet a felhasználóknak – [hozzájárulás a Weblate-en keresztül](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"; +"Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Köszönet a felhasználóknak [a Weblate-en való közreműködésért](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"; /* No comment provided by engineer. */ -"Thanks to the users – contribute via Weblate!" = "Köszönet a felhasználóknak - hozzájárulás a Weblaten!"; +"Thanks to the users – contribute via Weblate!" = "Köszönet a felhasználóknak a Weblate-en való közreműködésért!"; /* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "Az első csevegési rendszer bármiféle felhasználó azonosító nélkül - privátra lett tervezre."; +"The app can notify you when you receive messages or contact requests - please open settings to enable." = "Az alkalmazás értesíteni fogja, amikor üzeneteket vagy meghívási kéréseket kap – ezt a beállítások menüben engedélyezheti."; /* No comment provided by engineer. */ -"The app can notify you when you receive messages or contact requests - please open settings to enable." = "Az alkalmazás értesíteni fogja, amikor üzeneteket vagy kapcsolatfelvételi kéréseket kap – beállítások megnyitása az engedélyezéshez."; +"The app protects your privacy by using different operators in each conversation." = "Az alkalmazás úgy védi az adatait, hogy minden egyes beszélgetéshez más-más üzemeltetőt használ."; /* No comment provided by engineer. */ -"The attempt to change database passphrase was not completed." = "Az adatbázis jelmondatának megváltoztatására tett kísérlet nem fejeződött be."; +"The app will ask to confirm downloads from unknown file servers (except .onion)." = "Az alkalmazás kérni fogja az ismeretlen fájlkiszolgálókról (kivéve .onion) történő letöltések megerősítését."; /* No comment provided by engineer. */ -"The code you scanned is not a SimpleX link QR code." = "A beolvasott kód nem egy SimpleX hivatkozás QR-kód."; +"The attempt to change database passphrase was not completed." = "Az adatbázis jelmondatának módosítására tett kísérlet nem fejeződött be."; /* No comment provided by engineer. */ -"The connection you accepted will be cancelled!" = "Az ön által elfogadott kapcsolat megszakad!"; +"The code you scanned is not a SimpleX link QR code." = "A beolvasott QR-kód nem egy SimpleX-QR-kód-hivatkozás."; /* No comment provided by engineer. */ -"The contact you shared this link with will NOT be able to connect!" = "Ismerőse NEM fog tudni csatlakozni, akivel megosztotta ezt a hivatkozást!"; +"The connection reached the limit of undelivered messages, your contact may be offline." = "A kapcsolat elérte a kézbesítetlen üzenetek számának határát, a partnere lehet, hogy offline állapotban van."; /* No comment provided by engineer. */ -"The created archive is available via app Settings / Database / Old database archive." = "A létrehozott archívum a Beállítások / Adatbázis / Régi adatbázis-archívum menüpontban érhető el."; +"The connection you accepted will be cancelled!" = "Az Ön által elfogadott kérelem vissza lesz vonva!"; + +/* No comment provided by engineer. */ +"The contact you shared this link with will NOT be able to connect!" = "A partnere, akivel megosztotta ezt a hivatkozást, NEM fog tudni kapcsolódni!"; + +/* No comment provided by engineer. */ +"The created archive is available via app Settings / Database / Old database archive." = "A létrehozott archívum a „Beállítások / Adatbázis / Régi adatbázis-archívum” menüben érhető el."; /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "A titkosítás működik, és új titkosítási egyezményre nincs szükség. Ez kapcsolati hibákat eredményezhet!"; /* No comment provided by engineer. */ -"The hash of the previous message is different." = "Az előző üzenet hash-e más."; +"The future of messaging" = "Az üzenetváltás jövője"; /* No comment provided by engineer. */ -"The ID of the next message is incorrect (less or equal to the previous).\nIt can happen because of some bug or when the connection is compromised." = "A következő üzenet azonosítója hibás (kisebb vagy egyenlő az előzővel).\nEz valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő."; +"The hash of the previous message is different." = "Az előző üzenet hasítóértéke különbözik."; /* No comment provided by engineer. */ -"The message will be deleted for all members." = "Az üzenet minden tag számára törlésre kerül."; +"The ID of the next message is incorrect (less or equal to the previous).\nIt can happen because of some bug or when the connection is compromised." = "A következő üzenet azonosítója érvénytelen (kisebb vagy egyenlő az előzővel).\nEz valamilyen hiba vagy sérült kapcsolat esetén fordulhat elő."; /* No comment provided by engineer. */ -"The message will be marked as moderated for all members." = "Az üzenet minden tag számára moderáltként lesz megjelölve."; +"The message will be deleted for all members." = "Az üzenet az összes tag számára törölve lesz."; /* No comment provided by engineer. */ -"The next generation of private messaging" = "A privát üzenetküldés következő generációja"; +"The message will be marked as moderated for all members." = "Az üzenet az összes tag számára moderáltként lesz megjelölve."; /* No comment provided by engineer. */ -"The old database was not removed during the migration, it can be deleted." = "A régi adatbázis nem került eltávolításra a migráció során, így törölhető."; +"The messages will be deleted for all members." = "Az üzenetek az összes tag számára törölve lesznek."; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "Profilja csak az ismerősök számára kerül megosztásra."; +"The messages will be marked as moderated for all members." = "Az üzenetek az összes tag számára moderáltként lesznek megjelölve."; + +/* No comment provided by engineer. */ +"The old database was not removed during the migration, it can be deleted." = "A régi adatbázis nem lett eltávolítva az átköltöztetéskor, ezért törölhető."; + +/* No comment provided by engineer. */ +"Your profile is stored on your device and only shared with your contacts." = "A profilja csak a partnereivel van megosztva."; + +/* No comment provided by engineer. */ +"The same conditions will apply to operator **%@**." = "Ugyanezek a feltételek lesznek elfogadva a következő üzemeltető számára is: **%@**."; + +/* No comment provided by engineer. */ +"The second preset operator in the app!" = "A második előre beállított üzemeltető az alkalmazásban!"; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "A második jelölés, amit kihagytunk! ✅"; @@ -3507,58 +5116,88 @@ "The sender will NOT be notified" = "A feladó NEM fog értesítést kapni"; /* No comment provided by engineer. */ -"The servers for new connections of your current chat profile **%@**." = "Jelenlegi profil új ismerőseinek kiszolgálói **%@**."; +"The servers for new connections of your current chat profile **%@**." = "A jelenlegi **%@** nevű csevegési profiljához tartozó új kapcsolatok kiszolgálói."; /* No comment provided by engineer. */ -"The text you pasted is not a SimpleX link." = "A beillesztett szöveg nem egy SimpleX hivatkozás."; +"The servers for new files of your current chat profile **%@**." = "A jelenlegi **%@** nevű csevegési profiljához tartozó új fájlok kiszolgálói."; /* No comment provided by engineer. */ -"Theme" = "Téma"; +"The text you pasted is not a SimpleX link." = "A beillesztett szöveg nem egy SimpleX-hivatkozás."; /* No comment provided by engineer. */ -"These settings are for your current profile **%@**." = "Ezek a beállítások a jelenlegi **%@** profiljára vonatkoznak."; +"The uploaded database archive will be permanently removed from the servers." = "A feltöltött adatbázis-archívum véglegesen el lesz távolítva a kiszolgálókról."; /* No comment provided by engineer. */ -"They can be overridden in contact and group settings." = "Ezek felülbírálhatóak az ismerős- és csoportbeállításokban."; +"Themes" = "Témák"; /* No comment provided by engineer. */ -"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Ez a művelet nem vonható vissza - az összes fogadott és küldött fájl a médiatartalommal együtt törlésre kerülnek. Az alacsony felbontású fotók viszont megmaradnak."; +"These conditions will also apply for: **%@**." = "Ezek a feltételek lesznek elfogadva a következő számára is: **%@**."; /* No comment provided by engineer. */ -"This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Ez a művelet nem vonható vissza - a kiválasztottnál korábban küldött és fogadott üzenetek törlésre kerülnek. Ez több percet is igénybe vehet."; +"These settings are for your current profile **%@**." = "Ezek a beállítások csak a jelenlegi **%@** nevű csevegési profiljára vonatkoznak."; /* No comment provided by engineer. */ -"This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Ez a művelet nem vonható vissza - profilok, ismerősök, üzenetek és fájlok visszafordíthatatlanul törlésre kerülnek."; +"They can be overridden in contact and group settings." = "Ezek felülbírálhatók a partner- és csoportbeállításokban."; + +/* No comment provided by engineer. */ +"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Ez a művelet nem vonható vissza – az összes fogadott és küldött fájl a médiatartalmakkal együtt törölve lesznek. Az alacsony felbontású képek viszont megmaradnak."; + +/* No comment provided by engineer. */ +"This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Ez a művelet nem vonható vissza – a kijelöltnél korábban küldött és fogadott üzenetek törölve lesznek. Ez több percet is igénybe vehet."; + +/* alert message */ +"This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted." = "Ez a művelet nem vonható vissza – a kijelölt üzenettől korábban küldött és fogadott üzenetek törölve lesznek a csevegésből."; + +/* No comment provided by engineer. */ +"This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Ez a művelet nem vonható vissza – profiljai, partnerei, üzenetei és fájljai véglegesen törölve lesznek."; + +/* E2EE info chat item */ +"This chat is protected by end-to-end encryption." = "Ez a csevegés végpontok közötti titkosítással védett."; + +/* E2EE info chat item */ +"This chat is protected by quantum resistant end-to-end encryption." = "Ez a csevegés végpontok közötti kvantumbiztos titkosítással védett."; /* notification title */ -"this contact" = "ez az ismerős"; +"this contact" = "ez a partner"; /* No comment provided by engineer. */ "This device name" = "Ennek az eszköznek a neve"; /* No comment provided by engineer. */ -"This display name is invalid. Please choose another name." = "Ez a megjelenített felhasználónév érvénytelen. Válasszon egy másik nevet."; +"This display name is invalid. Please choose another name." = "Ez a megjelenítendő név érvénytelen. Válasszon egy másik nevet."; /* No comment provided by engineer. */ -"This group has over %lld members, delivery receipts are not sent." = "Ennek a csoportnak több mint %lld tagja van, a kézbesítési jelentések nem kerülnek elküldésre."; +"This group has over %lld members, delivery receipts are not sent." = "Ennek a csoportnak több mint %lld tagja van, a kézbesítési jelentések nem lesznek elküldve."; /* No comment provided by engineer. */ "This group no longer exists." = "Ez a csoport már nem létezik."; /* No comment provided by engineer. */ -"This is your own one-time link!" = "Ez az egyszer használatos hivatkozása!"; +"This is your own one-time link!" = "Ez a saját egyszer használható meghívója!"; /* No comment provided by engineer. */ -"This is your own SimpleX address!" = "Ez a SimpleX azonosítója!"; +"This is your own SimpleX address!" = "Ez a saját SimpleX-címe!"; /* No comment provided by engineer. */ -"This setting applies to messages in your current chat profile **%@**." = "Ez a beállítás a jelenlegi **%@** profiljában lévő üzenetekre érvényes."; +"This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link." = "Ez a hivatkozás újabb alkalmazásverziót igényel. Frissítse az alkalmazást vagy kérjen egy kompatibilis hivatkozást a partnerétől."; + +/* No comment provided by engineer. */ +"This link was used with another mobile device, please create a new link on the desktop." = "Ezt a hivatkozást egy másik hordozható eszközön már használták, hozzon létre egy új hivatkozást a számítógépén."; + +/* No comment provided by engineer. */ +"This message was deleted or not received yet." = "Ez az üzenet törölve lett vagy még nem érkezett meg."; + +/* No comment provided by engineer. */ +"This setting applies to messages in your current chat profile **%@**." = "Ez a beállítás csak az Ön jelenlegi **%@** nevű csevegési profiljában lévő üzenetekre vonatkozik."; + +/* No comment provided by engineer. */ +"Title" = "Cím"; /* No comment provided by engineer. */ "To ask any questions and to receive updates:" = "Bármilyen kérdés feltevéséhez és a frissítésekért:"; /* No comment provided by engineer. */ -"To connect, your contact can scan QR code or use the link in the app." = "A csatlakozáshoz az ismerős beolvashatja a QR-kódot, vagy használhatja az alkalmazásban található hivatkozást."; +"To connect, your contact can scan QR code or use the link in the app." = "A kapcsolódáshoz a partnere beolvashatja a QR-kódot, vagy használhatja az alkalmazásban található hivatkozást."; /* No comment provided by engineer. */ "To hide unwanted messages." = "Kéretlen üzenetek elrejtése."; @@ -3567,37 +5206,73 @@ "To make a new connection" = "Új kapcsolat létrehozásához"; /* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Az adatvédelem érdekében, a más csevegési platformokon megszokott felhasználói azonosítók helyett, a SimpleX üzenetsorokhoz rendel azonosítókat, minden egyes ismerőshöz egy különbözőt."; +"To protect against your link being replaced, you can compare contact security codes." = "A hivatkozás cseréje elleni védelem érdekében összehasonlíthatja a biztonsági kódokat a partnerével."; /* No comment provided by engineer. */ -"To protect timezone, image/voice files use UTC." = "Az időzóna védelme érdekében a kép-/hangfájlok UTC-t használnak."; +"To protect timezone, image/voice files use UTC." = "Az időzóna védelmének érdekében a kép-/hangfájlok UTC-t használnak."; /* No comment provided by engineer. */ -"To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Az adatavédelem érdekében kapcsolja be a SimpleX zárolás funkciót.\nA funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befejezésére."; +"To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "A biztonsága érdekében kapcsolja be a SimpleX-zár funkciót.\nA funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beállítására az eszközén."; + +/* No comment provided by engineer. */ +"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Az IP-cím védelmének érdekében a privát útválasztás az SMP-kiszolgálókat használja az üzenetek kézbesítéséhez."; + +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "Adatainak védelme érdekében a SimpleX külön üzenet-azonosítókat használ minden egyes kapcsolatához."; + +/* No comment provided by engineer. */ +"To receive" = "A fogadáshoz"; + +/* No comment provided by engineer. */ +"To record speech please grant permission to use Microphone." = "A beszéd rögzítéséhez adjon engedélyt a Mikrofon használatára."; + +/* No comment provided by engineer. */ +"To record video please grant permission to use Camera." = "A videó rögzítéséhez adjon engedélyt a Kamera használatára."; /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Hangüzenet rögzítéséhez adjon engedélyt a mikrofon használathoz."; /* No comment provided by engineer. */ -"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Rejtett profilja feltárásához írja be a teljes jelszót a keresőmezőbe a **Csevegési profiljai** oldalon."; +"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Rejtett profilja felfedéséhez adja meg a teljes jelszót a keresőmezőben, a **Csevegési profilok** menüben."; /* No comment provided by engineer. */ -"To support instant push notifications the chat database has to be migrated." = "Az azonnali push értesítések támogatásához a csevegési adatbázis migrálása szükséges."; +"To send" = "A küldéshez"; /* No comment provided by engineer. */ -"To verify end-to-end encryption with your contact compare (or scan) the code on your devices." = "A végpontok közötti titkosítás ellenőrzéséhez ismerősével hasonlítsa össze (vagy szkennelje be) az eszközén lévő kódot."; +"To support instant push notifications the chat database has to be migrated." = "Az azonnali push-értesítések támogatásához a csevegési adatbázis átköltöztetése szükséges."; /* No comment provided by engineer. */ -"Toggle incognito when connecting." = "Inkognító mód csatlakozáskor."; +"To use the servers of **%@**, accept conditions of use." = "A(z) **%@** kiszolgálóinak használatához fogadja el a használati feltételeket."; /* No comment provided by engineer. */ -"Transport isolation" = "Kapcsolat izolációs mód"; +"To verify end-to-end encryption with your contact compare (or scan) the code on your devices." = "A végpontok közötti titkosítás hitelesítéséhez hasonlítsa össze (vagy olvassa be a QR-kódot) a partnere eszközén lévő kóddal."; /* No comment provided by engineer. */ -"Trying to connect to the server used to receive messages from this contact (error: %@)." = "Csatlakozási kísérlet a kapcsolat üzeneteinek fogadására használt kiszolgálóhoz (hiba: %@)."; +"Toggle chat list:" = "Csevegési lista átváltása:"; /* No comment provided by engineer. */ -"Trying to connect to the server used to receive messages from this contact." = "Csatlakozási kísérlet a kapcsolat üzeneteinek fogadására használt kiszolgálóhoz ettől az ismerőstől."; +"Toggle incognito when connecting." = "Inkognitóra váltás kapcsolódáskor."; + +/* token status */ +"Token status: %@." = "Token állapota: %@."; + +/* No comment provided by engineer. */ +"Toolbar opacity" = "Eszköztár átlátszatlansága"; + +/* No comment provided by engineer. */ +"Total" = "Összes kapcsolat"; + +/* No comment provided by engineer. */ +"Transport isolation" = "Átvitel-izoláció"; + +/* No comment provided by engineer. */ +"Transport sessions" = "Munkamenetek átvitele"; + +/* No comment provided by engineer. */ +"Trying to connect to the server used to receive messages from this contact (error: %@)." = "Kapcsolódási kísérlet ahhoz a kiszolgálóhoz, amely az adott partnerétől érkező üzenetek fogadására szolgál (hiba: %@)."; + +/* No comment provided by engineer. */ +"Trying to connect to the server used to receive messages from this contact." = "Kapcsolódási kísérlet ahhoz a kiszolgálóhoz, amely az adott partnerétől érkező üzenetek fogadására szolgál."; /* No comment provided by engineer. */ "Turkish interface" = "Török kezelőfelület"; @@ -3615,28 +5290,28 @@ "Unblock" = "Feloldás"; /* No comment provided by engineer. */ -"Unblock for all" = "Letiltás feloldása mindenki számára"; +"Unblock for all" = "Feloldás"; /* No comment provided by engineer. */ "Unblock member" = "Tag feloldása"; /* No comment provided by engineer. */ -"Unblock member for all?" = "Mindenki számára feloldja a tag letiltását?"; +"Unblock member for all?" = "Az összes tag számára feloldja a tag letiltását?"; /* No comment provided by engineer. */ -"Unblock member?" = "Tag feloldása?"; +"Unblock member?" = "Feloldja a tag letiltását?"; /* rcv group event chat item */ -"unblocked %@" = "%@ feloldva"; - -/* item status description */ -"Unexpected error: %@" = "Váratlan hiba: %@"; +"unblocked %@" = "feloldotta %@ letiltását"; /* No comment provided by engineer. */ -"Unexpected migration state" = "Váratlan migrációs állapot"; +"Undelivered messages" = "Kézbesítetlen üzenetek"; /* No comment provided by engineer. */ -"Unfav." = "Nem kedvelt."; +"Unexpected migration state" = "Váratlan átköltöztetési állapot"; + +/* swipe action */ +"Unfav." = "Kedvenc megszüntetése"; /* No comment provided by engineer. */ "Unhide" = "Felfedés"; @@ -3663,19 +5338,25 @@ "Unknown error" = "Ismeretlen hiba"; /* No comment provided by engineer. */ -"unknown status" = "ismeretlen státusz"; +"unknown servers" = "ismeretlen átjátszók"; + +/* alert title */ +"Unknown servers!" = "Ismeretlen kiszolgálók!"; + +/* No comment provided by engineer. */ +"unknown status" = "ismeretlen állapot"; /* No comment provided by engineer. */ "Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions." = "Hacsak nem az iOS hívási felületét használja, engedélyezze a Ne zavarjanak módot a megszakítások elkerülése érdekében."; /* No comment provided by engineer. */ -"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Hacsak az ismerős nem törölte a kapcsolatot, vagy ez a hivatkozás már használatban volt, hiba lehet – kérjük, jelentse.\nA csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsolati hivatkozást, és ellenőrizze, hogy a hálózati kapcsolat stabil-e."; +"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Hacsak a partnere nem törölte a kapcsolatot, vagy ez a hivatkozás már használatban volt egyszer, lehet hogy ez egy hiba – jelentse a problémát.\nA kapcsolódáshoz kérje meg a partnerét, hogy hozzon létre egy másik kapcsolattartási hivatkozást, és ellenőrizze, hogy a hálózati kapcsolat stabil-e."; /* No comment provided by engineer. */ "Unlink" = "Szétkapcsolás"; /* No comment provided by engineer. */ -"Unlink desktop?" = "Számítógép szétkapcsolása?"; +"Unlink desktop?" = "Leválasztja a számítógépet?"; /* No comment provided by engineer. */ "Unlock" = "Feloldás"; @@ -3683,86 +5364,143 @@ /* authentication reason */ "Unlock app" = "Alkalmazás feloldása"; -/* No comment provided by engineer. */ -"Unmute" = "Némítás feloldása"; +/* notification label action */ +"Unmute" = "Némítás megszüntetése"; /* No comment provided by engineer. */ +"unprotected" = "nem védett"; + +/* swipe action */ "Unread" = "Olvasatlan"; /* No comment provided by engineer. */ -"Up to 100 last messages are sent to new members." = "Legfeljebb az utolsó 100 üzenet kerül elküldésre az új tagoknak."; +"Unsupported connection link" = "Nem támogatott kapcsolattartási hivatkozás"; + +/* No comment provided by engineer. */ +"Up to 100 last messages are sent to new members." = "Legfeljebb az utolsó 100 üzenet lesz elküldve az új tagok számára."; /* No comment provided by engineer. */ "Update" = "Frissítés"; /* No comment provided by engineer. */ -"Update .onion hosts setting?" = "Tor .onion host beállítások frissítése?"; +"Update database passphrase" = "Az adatbázis jelmondatának módosítása"; /* No comment provided by engineer. */ -"Update database passphrase" = "Adatbázis jelmondat megváltoztatása"; +"Update network settings?" = "Módosítja a hálózati beállításokat?"; /* No comment provided by engineer. */ -"Update network settings?" = "Hálózati beállítások megváltoztatása?"; +"Update settings?" = "Frissíti a beállításokat?"; /* No comment provided by engineer. */ -"Update transport isolation mode?" = "Kapcsolat izolációs mód frissítése?"; +"Updated conditions" = "Frissített feltételek"; /* rcv group event chat item */ -"updated group profile" = "módosított csoport profil"; +"updated group profile" = "frissítette a csoport profilját"; /* profile update event chat item */ "updated profile" = "frissített profil"; /* No comment provided by engineer. */ -"Updating settings will re-connect the client to all servers." = "A beállítások frissítése a szerverekhez újra kapcsolódással jár."; +"Updating settings will re-connect the client to all servers." = "A beállítások frissítése a kiszolgálókhoz való újra kapcsolódással jár."; /* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "A beállítás frissítésével a kliens újracsatlakozik az összes kiszolgálóhoz."; +"Upgrade and open chat" = "Fejlesztés és a csevegés megnyitása"; /* No comment provided by engineer. */ -"Upgrade and open chat" = "A csevegés frissítése és megnyitása"; +"Upload errors" = "Feltöltési hibák"; + +/* No comment provided by engineer. */ +"Upload failed" = "Sikertelen feltöltés"; /* server test step */ "Upload file" = "Fájl feltöltése"; /* No comment provided by engineer. */ -"Use .onion hosts" = "Tor .onion hostok használata"; +"Uploaded" = "Feltöltve"; /* No comment provided by engineer. */ -"Use chat" = "Csevegés használata"; +"Uploaded files" = "Feltöltött fájlok"; + +/* No comment provided by engineer. */ +"Uploading archive" = "Archívum feltöltése"; + +/* No comment provided by engineer. */ +"Use .onion hosts" = "Onion-kiszolgálók használata"; + +/* No comment provided by engineer. */ +"Use %@" = "%@ használata"; + +/* No comment provided by engineer. */ +"Use chat" = "SimpleX Chat használata"; /* No comment provided by engineer. */ "Use current profile" = "Jelenlegi profil használata"; +/* No comment provided by engineer. */ +"Use for files" = "Használat a fájlokhoz"; + +/* No comment provided by engineer. */ +"Use for messages" = "Használat az üzenetekhez"; + /* No comment provided by engineer. */ "Use for new connections" = "Alkalmazás új kapcsolatokhoz"; /* No comment provided by engineer. */ -"Use from desktop" = "Használat számítógépről"; +"Use from desktop" = "Társítás számítógéppel"; /* No comment provided by engineer. */ -"Use iOS call interface" = "Az iOS hívófelület használata"; +"Use iOS call interface" = "Az iOS hívási felületét használata"; /* No comment provided by engineer. */ -"Use new incognito profile" = "Az új inkognító profil használata"; +"Use new incognito profile" = "Új inkognitóprofil használata"; /* No comment provided by engineer. */ "Use only local notifications?" = "Csak helyi értesítések használata?"; +/* No comment provided by engineer. */ +"Use private routing with unknown servers when IP address is not protected." = "Használjon privát útválasztást ismeretlen kiszolgálókkal, ha az IP-cím nem védett."; + +/* No comment provided by engineer. */ +"Use private routing with unknown servers." = "Használjon privát útválasztást ismeretlen kiszolgálókkal."; + /* No comment provided by engineer. */ "Use server" = "Kiszolgáló használata"; /* No comment provided by engineer. */ -"Use SimpleX Chat servers?" = "SimpleX Chat kiszolgálók használata?"; +"Use servers" = "Kiszolgálók használata"; /* No comment provided by engineer. */ -"User profile" = "Felhasználói profil"; +"Use short links (BETA)" = "Rövid hivatkozások használata (béta)"; /* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = "A .onion hosztok használatához kompatibilis VPN szolgáltatóra van szükség."; +"Use SimpleX Chat servers?" = "SimpleX Chat-kiszolgálók használata?"; /* No comment provided by engineer. */ -"Using SimpleX Chat servers." = "SimpleX Chat kiszolgálók használatban."; +"Use SOCKS proxy" = "SOCKS-proxy használata"; + +/* No comment provided by engineer. */ +"Use TCP port %@ when no port is specified." = "A következő TCP-port használata, amikor nincs port megadva: %@."; + +/* No comment provided by engineer. */ +"Use TCP port 443 for preset servers only." = "A 443-as TCP-port használata kizárólag az előre beállított kiszolgálokhoz."; + +/* No comment provided by engineer. */ +"Use the app while in the call." = "Használja az alkalmazást hívás közben."; + +/* No comment provided by engineer. */ +"Use the app with one hand." = "Használja az alkalmazást egy kézzel."; + +/* No comment provided by engineer. */ +"Use web port" = "Webport használata"; + +/* No comment provided by engineer. */ +"User selection" = "Felhasználó kijelölése"; + +/* No comment provided by engineer. */ +"Username" = "Felhasználónév"; + +/* No comment provided by engineer. */ +"Using SimpleX Chat servers." = "SimpleX Chat-kiszolgálók használatban."; /* No comment provided by engineer. */ "v%@" = "v%@"; @@ -3771,37 +5509,46 @@ "v%@ (%@)" = "v%@ (%@)"; /* No comment provided by engineer. */ -"Verify code with desktop" = "Kód ellenőrzése a számítógépen"; +"Verify code with desktop" = "Kód hitelesítése a számítógépen"; /* No comment provided by engineer. */ -"Verify connection" = "Kapcsolat ellenőrzése"; +"Verify connection" = "Kapcsolat hitelesítése"; /* No comment provided by engineer. */ -"Verify connection security" = "Kapcsolat biztonságának ellenőrzése"; +"Verify connection security" = "Biztonságos kapcsolat hitelesítése"; /* No comment provided by engineer. */ -"Verify connections" = "Kapcsolatok ellenőrzése"; +"Verify connections" = "Kapcsolatok hitelesítése"; /* No comment provided by engineer. */ -"Verify security code" = "Biztonsági kód ellenőrzése"; +"Verify database passphrase" = "Az adatbázis jelmondatának hitelesítése"; + +/* No comment provided by engineer. */ +"Verify passphrase" = "Jelmondat hitelesítése"; + +/* No comment provided by engineer. */ +"Verify security code" = "Biztonsági kód hitelesítése"; /* No comment provided by engineer. */ "Via browser" = "Böngészőn keresztül"; /* chat list item description */ -"via contact address link" = "ismerős azonosítójának hivatkozásán keresztül"; +"via contact address link" = "a kapcsolattartási címhivatkozáson keresztül"; /* chat list item description */ -"via group link" = "csoport hivatkozáson keresztül"; +"via group link" = "a csoporthivatkozáson keresztül"; /* chat list item description */ -"via one-time link" = "egyszer használatos hivatkozáson keresztül"; +"via one-time link" = "egy egyszer használható meghívón keresztül"; /* No comment provided by engineer. */ -"via relay" = "átjátszón keresztül"; +"via relay" = "egy továbbítókiszolgálón keresztül"; /* No comment provided by engineer. */ -"Via secure quantum resistant protocol." = "Biztonságos kvantum ellenálló protokoll által."; +"Via secure quantum resistant protocol." = "Biztonságos kvantumbiztos protokollon keresztül."; + +/* No comment provided by engineer. */ +"video" = "videó"; /* No comment provided by engineer. */ "Video call" = "Videóhívás"; @@ -3810,17 +5557,23 @@ "video call (not e2e encrypted)" = "videóhívás (nem e2e titkosított)"; /* No comment provided by engineer. */ -"Video will be received when your contact completes uploading it." = "A videó akkor érkezik meg, amikor az ismerőse befejezte annak feltöltését."; +"Video will be received when your contact completes uploading it." = "A videó akkor érkezik meg, amikor a küldője befejezte annak feltöltését."; /* No comment provided by engineer. */ -"Video will be received when your contact is online, please wait or check later!" = "A videó akkor érkezik meg, amikor az ismerős elérhető, várjon, vagy ellenőrizze később!"; +"Video will be received when your contact is online, please wait or check later!" = "A videó akkor érkezik meg, amikor a küldője elérhető lesz, várjon, vagy ellenőrizze később!"; /* No comment provided by engineer. */ -"Videos and files up to 1gb" = "Videók és fájlok 1Gb méretig"; +"Videos and files up to 1gb" = "Videók és fájlok legfeljebb 1GB méretig"; + +/* No comment provided by engineer. */ +"View conditions" = "Feltételek megtekintése"; /* No comment provided by engineer. */ "View security code" = "Biztonsági kód megtekintése"; +/* No comment provided by engineer. */ +"View updated conditions" = "Frissített feltételek megtekintése"; + /* chat feature */ "Visible history" = "Látható előzmények"; @@ -3831,319 +5584,400 @@ "Voice messages" = "Hangüzenetek"; /* No comment provided by engineer. */ -"Voice messages are prohibited in this chat." = "A hangüzenetek le vannak tiltva ebben a csevegésben."; +"Voice messages are prohibited in this chat." = "A hangüzenetek küldése le van tiltva ebben a csevegésben."; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "A hangüzenetek küldése le van tiltva ebben a csoportban."; +"Voice messages are prohibited." = "A hangüzenetek küldése le van tiltva."; /* No comment provided by engineer. */ -"Voice messages prohibited!" = "A hangüzenetek le vannak tilva!"; +"Voice messages not allowed" = "A hangüzenetek küldése le van tiltva"; /* No comment provided by engineer. */ -"waiting for answer…" = "várakozás válaszra…"; +"Voice messages prohibited!" = "A hangüzenetek le vannak tiltva!"; + +/* No comment provided by engineer. */ +"waiting for answer…" = "várakozás a válaszra…"; /* No comment provided by engineer. */ "waiting for confirmation…" = "várakozás a visszaigazolásra…"; /* No comment provided by engineer. */ -"Waiting for desktop..." = "Várakozás az asztali kliensre..."; +"Waiting for desktop..." = "Várakozás a számítógép-alkalmazásra…"; /* No comment provided by engineer. */ -"Waiting for file" = "Fájlra várakozás"; +"Waiting for file" = "Várakozás a fájlra"; /* No comment provided by engineer. */ -"Waiting for image" = "Képre várakozás"; +"Waiting for image" = "Várakozás a képre"; /* No comment provided by engineer. */ -"Waiting for video" = "Videóra várakozás"; +"Waiting for video" = "Várakozás a videóra"; /* No comment provided by engineer. */ -"wants to connect to you!" = "kapcsolatba akar lépni önnel!"; +"Wallpaper accent" = "Háttérkép kiemelőszíne"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Háttérkép háttérszíne"; + +/* No comment provided by engineer. */ +"wants to connect to you!" = "kapcsolatba akar lépni Önnel!"; + +/* No comment provided by engineer. */ +"Warning: starting chat on multiple devices is not supported and will cause message delivery failures" = "Figyelmeztetés: a csevegés elindítása egyszerre több eszközön nem támogatott, mert üzenetkézbesítési hibákat okoz"; /* No comment provided by engineer. */ "Warning: you may lose some data!" = "Figyelmeztetés: néhány adat elveszhet!"; /* No comment provided by engineer. */ -"WebRTC ICE servers" = "WebRTC ICE kiszolgálók"; +"WebRTC ICE servers" = "WebRTC ICE-kiszolgálók"; /* time unit */ "weeks" = "hét"; /* No comment provided by engineer. */ -"Welcome %@!" = "Üdvözöllek %@!"; +"Welcome %@!" = "Üdvözöljük %@!"; /* No comment provided by engineer. */ -"Welcome message" = "Üdvözlő üzenet"; +"Welcome message" = "Üdvözlőüzenet"; /* No comment provided by engineer. */ -"What's new" = "Milyen újdonságok vannak"; +"Welcome message is too long" = "Az üdvözlőüzenet túl hosszú"; + +/* No comment provided by engineer. */ +"What's new" = "Újdonságok"; /* No comment provided by engineer. */ "When available" = "Amikor elérhető"; /* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "Csatlakozási kérelmek esetében, elfogadhatja vagy elutasíthatja azokat."; +"When connecting audio and video calls." = "Amikor egy bejövő hang- vagy videóhívás érkezik."; + +/* No comment provided by engineer. */ +"when IP hidden" = "ha az IP-cím rejtett"; + +/* No comment provided by engineer. */ +"When more than one operator is enabled, none of them has metadata to learn who communicates with whom." = "Amikor egynél több üzemeltető van engedélyezve, akkor egyik sem rendelkezik olyan metaadatokkal, amelyekből megtudható, hogy ki kivel kommunikál."; /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Inkognitóprofil megosztása esetén a rendszer azt a profilt fogja használni azokhoz a csoportokhoz, amelyekbe meghívást kapott."; /* No comment provided by engineer. */ -"With encrypted files and media." = "Titkosított fájlokkal és médiatartalommal."; +"WiFi" = "Wi-Fi"; /* No comment provided by engineer. */ -"With optional welcome message." = "Opcionális üdvözlő üzenettel."; +"Will be enabled in direct chats!" = "A közvetlen csevegésekben engedélyezve lesz!"; /* No comment provided by engineer. */ -"With reduced battery usage." = "Csökkentett akkumulátorhasználattal."; +"Wired ethernet" = "Vezetékes Ethernet"; /* No comment provided by engineer. */ -"Wrong database passphrase" = "Téves adatbázis jelmondat"; +"With encrypted files and media." = "Titkosított fájlokkal és médiatartalmakkal."; /* No comment provided by engineer. */ -"Wrong passphrase!" = "Téves jelmondat!"; +"With optional welcome message." = "Nem kötelező üdvözlőüzenettel."; /* No comment provided by engineer. */ -"XFTP servers" = "XFTP kiszolgálók"; +"With reduced battery usage." = "Csökkentett akkumulátor-használattal."; + +/* No comment provided by engineer. */ +"Without Tor or VPN, your IP address will be visible to file servers." = "Tor vagy VPN nélkül az Ön IP-címe látható lesz a fájlkiszolgálók számára."; + +/* alert message */ +"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Tor vagy VPN nélkül az Ön IP-címe látható lesz a következő XFTP-továbbítókiszolgálók számára: %@."; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Érvénytelen adatbázis-jelmondat"; + +/* snd error text */ +"Wrong key or unknown connection - most likely this connection is deleted." = "Érvénytelen kulcs vagy ismeretlen kapcsolat – valószínűleg ez a kapcsolat törlődött."; + +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Érvénytelen kulcs vagy ismeretlen fájltöredékcím – valószínűleg a fájl törlődött."; + +/* No comment provided by engineer. */ +"Wrong passphrase!" = "Érvénytelen jelmondat!"; + +/* No comment provided by engineer. */ +"XFTP server" = "XFTP-kiszolgáló"; /* pref value */ "yes" = "igen"; /* No comment provided by engineer. */ -"You" = "Ön"; +"you" = "Ön"; /* No comment provided by engineer. */ -"You accepted connection" = "Kapcsolódás elfogadva"; +"You **must not** use the same database on two devices." = "**Nem szabad** ugyanazt az adatbázist használni egyszerre két eszközön."; /* No comment provided by engineer. */ -"You allow" = "Engedélyezte"; +"You accepted connection" = "Kapcsolat létrehozása"; /* No comment provided by engineer. */ -"You already have a chat profile with the same display name. Please choose another name." = "Már van egy csevegési profil ugyanezzel a megjelenített névvel. Válasszon egy másik nevet."; +"You allow" = "Ön engedélyezi"; /* No comment provided by engineer. */ -"You are already connected to %@." = "Már csatlakozva van ehhez: %@."; +"You already have a chat profile with the same display name. Please choose another name." = "Már van egy csevegési profil ugyanezzel a megjelenítendő névvel. Válasszon egy másik nevet."; /* No comment provided by engineer. */ -"You are already connecting to %@." = "Már folyamatban van a csatlakozás ehhez: %@."; +"You are already connected to %@." = "Ön már kapcsolódott a következőhöz: %@."; /* No comment provided by engineer. */ -"You are already connecting via this one-time link!" = "Már csatlakozik ezen az egyszer használatos hivatkozáson keresztül!"; +"You are already connected with %@." = "Ön már kapcsolódva van vele: %@."; /* No comment provided by engineer. */ -"You are already in group %@." = "Már a %@ csoportban van."; +"You are already connecting to %@." = "A kapcsolódás már folyamatban van a következőhöz: %@."; /* No comment provided by engineer. */ -"You are already joining the group %@." = "Már folyamatban van a csatlakozás a csoporthoz %@."; +"You are already connecting via this one-time link!" = "A kapcsolódás már folyamatban van ezen az egyszer használható meghívón keresztül!"; /* No comment provided by engineer. */ -"You are already joining the group via this link!" = "Már csatlakozott a csoporthoz ezen a hivatkozáson keresztül!"; +"You are already in group %@." = "Ön már a(z) %@ nevű csoport tagja."; /* No comment provided by engineer. */ -"You are already joining the group via this link." = "Ezen a hivatkozáson keresztül már csatlakozik a csoporthoz."; +"You are already joining the group %@." = "A csatlakozás már folyamatban van a(z) %@ nevű csoporthoz."; /* No comment provided by engineer. */ -"You are already joining the group!\nRepeat join request?" = "Csatlakozás folyamatban!\nCsatlakozási kérés megismétlése?"; +"You are already joining the group via this link!" = "A csatlakozás már folyamatban van a csoporthoz ezen a hivatkozáson keresztül!"; /* No comment provided by engineer. */ -"You are connected to the server used to receive messages from this contact." = "Kiszolgálóhoz történő csatlakozás, mely az adott ismerőstől érkező üzenetek fogadására szolgál."; +"You are already joining the group via this link." = "A csatlakozás már folyamatban van a csoporthoz ezen a hivatkozáson keresztül."; /* No comment provided by engineer. */ -"you are invited to group" = "meghívást kapott a csoportba"; +"You are already joining the group!\nRepeat join request?" = "A csatlakozás már folyamatban van a csoporthoz!\nMegismétli a meghívási kérést?"; /* No comment provided by engineer. */ -"You are invited to group" = "Meghívást kapott a csoportba"; +"You are connected to the server used to receive messages from this contact." = "Ön már kapcsolódott ahhoz a kiszolgálóhoz, amely az adott partnerétől érkező üzenetek fogadására szolgál."; /* No comment provided by engineer. */ -"you are observer" = "megfigyelő szerep"; +"you are invited to group" = "Ön meghívást kapott a csoportba"; + +/* No comment provided by engineer. */ +"You are invited to group" = "Ön meghívást kapott a csoportba"; + +/* No comment provided by engineer. */ +"You are not connected to these servers. Private routing is used to deliver messages to them." = "Ön nem kapcsolódik ezekhez a kiszolgálókhoz. A privát útválasztás az üzenetek kézbesítésére szolgál."; + +/* No comment provided by engineer. */ +"you are observer" = "Ön megfigyelő"; /* snd group event chat item */ -"you blocked %@" = "blokkolta őt: %@"; +"you blocked %@" = "Ön letiltotta őt: %@"; /* No comment provided by engineer. */ -"You can accept calls from lock screen, without device and app authentication." = "Hívásokat fogadhat a lezárási képernyőről, eszköz- és alkalmazáshitelesítés nélkül."; +"You can accept calls from lock screen, without device and app authentication." = "Hívásokat fogadhat a lezárási képernyőről, eszköz- és alkalmazás-hitelesítés nélkül."; + +/* No comment provided by engineer. */ +"You can change it in Appearance settings." = "Ezt a „Megjelenés” menüben módosíthatja."; + +/* No comment provided by engineer. */ +"You can configure servers via settings." = "A kiszolgálókat a „Hálózat és kiszolgálók” menüben konfigurálhatja."; /* No comment provided by engineer. */ "You can create it later" = "Létrehozás később"; /* No comment provided by engineer. */ -"You can enable later via Settings" = "Később engedélyezheti a Beállításokban"; +"You can enable later via Settings" = "Később engedélyezheti a „Beállításokban”"; /* No comment provided by engineer. */ -"You can enable them later via app Privacy & Security settings." = "Később engedélyezheti őket az alkalmazás Adatvédelem és biztonság menüpontban."; +"You can enable them later via app Privacy & Security settings." = "Később engedélyezheti őket az „Adatvédelem és biztonság” menüben."; /* No comment provided by engineer. */ -"You can hide or mute a user profile - swipe it to the right." = "Elrejthet vagy némíthat egy felhasználói profilt – csúsztasson jobbra."; +"You can give another try." = "Megpróbálhatja még egyszer."; /* No comment provided by engineer. */ -"You can make it visible to your SimpleX contacts via Settings." = "Láthatóvá teheti SimpleX ismerősök számára a Beállításokban."; +"You can hide or mute a user profile - swipe it to the right." = "Elrejtheti vagy lenémíthatja a felhasználó -profiljait – csúsztassa jobbra a profilt."; + +/* No comment provided by engineer. */ +"You can make it visible to your SimpleX contacts via Settings." = "Láthatóvá teheti a SimpleXbeli partnerei számára a „Beállításokban”."; /* notification body */ -"You can now send messages to %@" = "Mostantól küldhet üzeneteket %@ számára"; +"You can now chat with %@" = "Mostantól küldhet üzeneteket %@ számára"; /* No comment provided by engineer. */ -"You can set lock screen notification preview via settings." = "A beállításokon keresztül beállíthatja a lezárási képernyő értesítési előnézetét."; +"You can send messages to %@ from Archived contacts." = "Az „Archivált partnerekből” továbbra is küldhet üzeneteket neki: %@."; /* No comment provided by engineer. */ -"You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it." = "Megoszthat egy hivatkozást vagy QR-kódot - így bárki csatlakozhat a csoporthoz. Ha a csoport később törlésre kerül, akkor nem fogja elveszíteni annak tagjait."; +"You can set connection name, to remember who the link was shared with." = "Beállíthatja a partner nevét, hogy emlékezzen arra, hogy kivel osztotta meg a hivatkozást."; /* No comment provided by engineer. */ -"You can share this address with your contacts to let them connect with **%@**." = "Megoszthatja ezt a hivatkozást ismerőseivel, hogy kapcsolatba léphessenek önnel a **%@** nevű profilján keresztül."; +"You can set lock screen notification preview via settings." = "A lezárási képernyő értesítési előnézetét az „Értesítések” menüben állíthatja be."; /* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "Megoszthatja azonosítóját hivatkozásként vagy QR-kódként – így bárki csatlakozhat önhöz."; +"You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it." = "Megoszthat egy hivatkozást vagy QR-kódot – így bárki csatlakozhat a csoporthoz. Ha a csoportot Ön később törli, akkor nem fogja elveszíteni annak tagjait."; /* No comment provided by engineer. */ -"You can start chat via app Settings / Database or by restarting the app" = "A csevegést az alkalmazás Beállítások / Adatbázis menü segítségével vagy az alkalmazás újraindításával indíthatja el"; +"You can share this address with your contacts to let them connect with **%@**." = "Megoszthatja ezt a SimpleX-címet a partnereivel, hogy kapcsolatba léphessenek vele: **%@**."; /* No comment provided by engineer. */ -"You can turn on SimpleX Lock via Settings." = "A SimpleX zárolás a Beállításokon keresztül kapcsolható be."; +"You can start chat via app Settings / Database or by restarting the app" = "A csevegést az alkalmazás „Beállítások / Adatbázis” menüben vagy az alkalmazás újraindításával indíthatja el"; + +/* No comment provided by engineer. */ +"You can still view conversation with %@ in the list of chats." = "A(z) %@ nevű partnerével folytatott beszélgetéseit továbbra is megtekintheti a csevegések listájában."; + +/* No comment provided by engineer. */ +"You can turn on SimpleX Lock via Settings." = "A SimpleX-zár az „Adatvédelem és biztonság” menüben kapcsolható be."; /* No comment provided by engineer. */ "You can use markdown to format messages:" = "Üzenetek formázása a szövegbe szúrt speciális karakterekkel:"; -/* No comment provided by engineer. */ -"You can view invitation link again in connection details." = "A meghívó hivatkozást újra megtekintheti a kapcsolat részleteinél."; +/* alert message */ +"You can view invitation link again in connection details." = "A meghívási hivatkozást újra megtekintheti a kapcsolat részleteinél."; /* No comment provided by engineer. */ "You can't send messages!" = "Nem lehet üzeneteket küldeni!"; /* chat item text */ -"you changed address" = "azonosítója megváltoztatva"; +"you changed address" = "Ön módosította a címet"; /* chat item text */ -"you changed address for %@" = "%@ azonosítója megváltoztatva"; +"you changed address for %@" = "Ön módosította a címet %@ számára"; /* snd group event chat item */ -"you changed role for yourself to %@" = "saját szerepkör megváltoztatva erre: %@"; +"you changed role for yourself to %@" = "Ön a következőre módosította a saját szerepkörét: „%@”"; /* snd group event chat item */ -"you changed role of %@ to %@" = "megváltoztatta %1$@ szerepkörét erre: %@"; +"you changed role of %@ to %@" = "Ön a következőre módosította %1$@ szerepkörét: „%2$@”"; /* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "Ön szabályozhatja, hogy mely kiszogál(ók)ón keresztül **kapja** az üzeneteket, az ismerősöket - az üzenetküldéshez használt szervereken."; +"You could not be verified; please try again." = "Nem sikerült hitelesíteni; próbálja meg újra."; /* No comment provided by engineer. */ -"You could not be verified; please try again." = "Nem lehetett ellenőrizni; próbálja meg újra."; +"You decide who can connect." = "Ön dönti el, hogy kivel beszélget."; /* No comment provided by engineer. */ -"You have already requested connection via this address!" = "Már kért egy csatlakozást ezen az azonosítón keresztül!"; +"You have already requested connection via this address!" = "Már küldött egy meghívási kérést ezen a címen keresztül!"; /* No comment provided by engineer. */ -"You have already requested connection!\nRepeat connection request?" = "Már kérelmezte a csatlakozást!\nKapcsolódási kérés megismétlése?"; +"You have already requested connection!\nRepeat connection request?" = "Ön már küldött egy meghívási kérést!\nMegismétli a meghívási kérést?"; /* No comment provided by engineer. */ -"You have no chats" = "Nincsenek csevegési üzenetek"; +"You have to enter passphrase every time the app starts - it is not stored on the device." = "A jelmondatot minden alkalommal meg kell adnia, amikor az alkalmazás elindul – nem az eszközön van tárolva."; /* No comment provided by engineer. */ -"You have to enter passphrase every time the app starts - it is not stored on the device." = "A jelmondatot minden alkalommal meg kell adnia, amikor az alkalmazás elindul - nem az eszközön kerül tárolásra."; +"You invited a contact" = "Ön meghívta egy partnerét"; /* No comment provided by engineer. */ -"You invited a contact" = "Meghívott egy ismerőst"; +"You joined this group" = "Ön csatlakozott ehhez a csoporthoz"; /* No comment provided by engineer. */ -"You joined this group" = "Csatlakozott ehhez a csoporthoz"; - -/* No comment provided by engineer. */ -"You joined this group. Connecting to inviting group member." = "Csatlakozott ehhez a csoporthoz. Kapcsolódás a meghívó csoporttaghoz."; +"You joined this group. Connecting to inviting group member." = "Ön csatlakozott ehhez a csoporthoz. Kapcsolódás a meghívó csoporttaghoz."; /* snd group event chat item */ -"you left" = "elhagyta"; +"you left" = "Ön elhagyta a csoportot"; /* No comment provided by engineer. */ -"You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." = "A csevegési adatbázis legfrissebb verzióját CSAK egy eszközön kell használnia, ellenkező esetben előfordulhat, hogy az üzeneteket nem fogja megkapni valamennyi ismerőstől."; +"You may migrate the exported database." = "Az exportált adatbázist átköltöztetheti."; /* No comment provided by engineer. */ -"You need to allow your contact to send voice messages to be able to send them." = "Hangüzeneteket küldéséhez engedélyeznie kell azok küldését az ismerősök számára."; +"You may save the exported archive." = "Az exportált archívumot elmentheti."; /* No comment provided by engineer. */ -"You rejected group invitation" = "Csoport meghívó elutasítva"; +"You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." = "A csevegési adatbázis legfrissebb verzióját CSAK egy eszközön kell használnia, ellenkező esetben előfordulhat, hogy az üzeneteket nem fogja megkapni valamennyi partnerétől."; + +/* No comment provided by engineer. */ +"You need to allow your contact to call to be able to call them." = "Engedélyeznie kell a hívásokat a partnere számára, hogy fel tudják hívni egymást."; + +/* No comment provided by engineer. */ +"You need to allow your contact to send voice messages to be able to send them." = "Engedélyeznie kell a hangüzenetek küldését a partnere számára, hogy hangüzeneteket küldhessenek egymásnak."; + +/* No comment provided by engineer. */ +"You rejected group invitation" = "Csoportmeghívó elutasítva"; /* snd group event chat item */ -"you removed %@" = "eltávolította őt: %@"; +"you removed %@" = "Ön eltávolította őt: %@"; /* No comment provided by engineer. */ -"You sent group invitation" = "Csoport meghívó elküldve"; +"You sent group invitation" = "Csoportmeghívó elküldve"; /* chat list item description */ -"you shared one-time link" = "egyszer használatos hivatkozást osztott meg"; +"you shared one-time link" = "Ön egy egyszer használható meghívót osztott meg"; /* chat list item description */ -"you shared one-time link incognito" = "egyszer használatos hivatkozást osztott meg inkognitóban"; +"you shared one-time link incognito" = "Ön egy egyszer használható meghívót osztott meg inkognitóban"; + +/* token info */ +"You should receive notifications." = "Ön megkapja az értesítéseket."; /* snd group event chat item */ -"you unblocked %@" = "feloldotta %@ blokkolását"; +"you unblocked %@" = "Ön feloldotta %@ letiltását"; /* No comment provided by engineer. */ -"You will be connected to group when the group host's device is online, please wait or check later!" = "Akkor tud csatlakozni a csoporthoz, amikor a csoport tulajdonosának eszköze online lesz, várjon, vagy ellenőrizze később!"; +"You will be connected to group when the group host's device is online, please wait or check later!" = "Akkor lesz kapcsolódva a csoporthoz, amikor a csoport tulajdonosának eszköze online lesz, várjon, vagy ellenőrizze később!"; /* No comment provided by engineer. */ -"You will be connected when group link host's device is online, please wait or check later!" = "Akkor lesz csatlakoztatva, amikor a csoportos hivatkozás tulajdonosának eszköze online lesz, várjon, vagy ellenőrizze később!"; +"You will be connected when group link host's device is online, please wait or check later!" = "Akkor lesz kapcsolódva, amikor a csoporthivatkozás tulajdonosának eszköze online lesz, várjon, vagy ellenőrizze később!"; /* No comment provided by engineer. */ -"You will be connected when your connection request is accepted, please wait or check later!" = "Akkor lesz csatlakoztatva, ha a csatlakozási kérelme elfogadásra került, várjon, vagy ellenőrizze később!"; +"You will be connected when your connection request is accepted, please wait or check later!" = "Akkor lesz kapcsolódva, ha a meghívási kérése el lesz fogadva, várjon, vagy ellenőrizze később!"; /* No comment provided by engineer. */ -"You will be connected when your contact's device is online, please wait or check later!" = "Akkor csatlakozik, amikor az ismerős eszköze online lesz, várjon, vagy ellenőrizze később!"; +"You will be connected when your contact's device is online, please wait or check later!" = "Akkor lesz kapcsolódva, amikor a partnerének az eszköze online lesz, várjon, vagy ellenőrizze később!"; /* No comment provided by engineer. */ -"You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Az alkalmazás indításakor, vagy 30 másodpercnyi háttérben töltött idő után az alkalmazáshoz visszatérve hitelesítés szükséges."; +"You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Az alkalmazás elindításához vagy 30 másodpercnyi háttérben töltött idő után, az alkalmazáshoz való visszatéréshez hitelesítésre lesz szükség."; /* No comment provided by engineer. */ -"You will connect to all group members." = "Csatlakozni fog a csoport összes tagjához."; +"You will connect to all group members." = "Kapcsolódni fog a csoport összes tagjához."; /* No comment provided by engineer. */ "You will still receive calls and notifications from muted profiles when they are active." = "Továbbra is kap hívásokat és értesítéseket a némított profiloktól, ha azok aktívak."; +/* No comment provided by engineer. */ +"You will stop receiving messages from this chat. Chat history will be preserved." = "Ön nem fog több üzenetet kapni ebből a csevegésből, de a csevegés előzményei megmaradnak."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Ettől a csoporttól nem fog értesítéseket kapni. A csevegési előzmények megmaradnak."; /* No comment provided by engineer. */ -"You won't lose your contacts if you later delete your address." = "Nem veszíti el ismerőseit, ha később törli az azonosítóját."; +"You won't lose your contacts if you later delete your address." = "Nem veszíti el a partnereit, ha később törli a címét."; /* No comment provided by engineer. */ -"you: " = "ön: "; +"you: " = "Ön: "; /* No comment provided by engineer. */ -"You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile" = "Egy olyan ismerőst próbál meghívni, akivel inkognító profilt osztott meg abban a csoportban, amelyben saját fő profilja van használatban"; +"You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile" = "Egy olyan partnerét próbálja meghívni, akivel inkognitóprofilt osztott meg abban a csoportban, amelyben a fő profilja van használatban"; /* No comment provided by engineer. */ -"You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Inkognító profilt használ ehhez a csoporthoz - fő profilja megosztásának elkerülése érdekében meghívók küldése tiltott"; - -/* No comment provided by engineer. */ -"Your %@ servers" = "%@ nevű profiljához tartozó kiszolgálók"; +"You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Inkognitóprofilt használ ehhez a csoporthoz – fő profilja megosztásának elkerülése érdekében a meghívók küldése le van tiltva"; /* No comment provided by engineer. */ "Your calls" = "Hívások"; /* No comment provided by engineer. */ -"Your chat database" = "Csevegési adatbázisa"; +"Your chat database" = "Csevegési adatbázis"; /* No comment provided by engineer. */ -"Your chat database is not encrypted - set passphrase to encrypt it." = "Csevegési adatbázisa nincs titkosítva – adjon meg egy jelmondatot a titkosításhoz."; +"Your chat database is not encrypted - set passphrase to encrypt it." = "A csevegési adatbázis nincs titkosítva – adjon meg egy jelmondatot a titkosításhoz."; + +/* alert title */ +"Your chat preferences" = "Az Ön csevegési beállításai"; /* No comment provided by engineer. */ -"Your chat profiles" = "Csevegési profiljai"; +"Your chat profiles" = "Csevegési profilok"; /* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Az ismerősnek online kell lennie ahhoz, hogy a kapcsolat létrejöjjön.\nMegszakíthatja ezt a kapcsolatfelvételt és törölheti az ismerőst (ezt később ismét megpróbálhatja egy új hivatkozással)."; +"Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." = "A kapcsolata át lett helyezve ide: %@, de egy váratlan hiba történt a profilra való átirányításkor."; /* No comment provided by engineer. */ -"Your contact sent a file that is larger than currently supported maximum size (%@)." = "Ismerőse olyan fájlt küldött, amely meghaladja a jelenleg támogatott maximális méretet (%@)."; +"Your contact sent a file that is larger than currently supported maximum size (%@)." = "A partnere a jelenleg megengedett maximális méretű (%@) fájlnál nagyobbat küldött."; /* No comment provided by engineer. */ -"Your contacts can allow full message deletion." = "Ismerősök engedélyezhetik a teljes üzenet törlést."; +"Your contacts can allow full message deletion." = "A partnerei engedélyezhetik a teljes üzenet törlését."; /* No comment provided by engineer. */ -"Your contacts will remain connected." = "Az ismerősök továbbra is csatlakoztatva maradnak."; +"Your contacts will remain connected." = "A partnerei továbbra is kapcsolódva maradnak."; /* No comment provided by engineer. */ -"Your current chat database will be DELETED and REPLACED with the imported one." = "A jelenlegi csevegési adatbázis TÖRLŐDNI FOG, és a HELYÉRE az importált adatbázis kerül."; +"Your credentials may be sent unencrypted." = "A hitelesítőadatai titkosítatlanul is elküldhetők."; + +/* No comment provided by engineer. */ +"Your current chat database will be DELETED and REPLACED with the imported one." = "A jelenlegi csevegési adatbázis TÖRÖLVE és CSERÉLVE lesz az importáltra."; /* No comment provided by engineer. */ "Your current profile" = "Jelenlegi profil"; /* No comment provided by engineer. */ -"Your ICE servers" = "ICE kiszolgálók"; +"Your ICE servers" = "Saját ICE-kiszolgálók"; /* No comment provided by engineer. */ "Your preferences" = "Beállítások"; @@ -4155,32 +5989,29 @@ "Your profile" = "Profil"; /* No comment provided by engineer. */ -"Your profile **%@** will be shared." = "**%@** nevű profilja megosztásra kerül."; +"Your profile **%@** will be shared." = "A(z) **%@** nevű profilja meg lesz osztva."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Profilja az eszközön van tárolva, és csak az ismerősökkel kerül megosztásra.\nA SimpleX kiszolgálók nem látjhatják profilját."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "A profilja az eszközén van tárolva és csak a partnereivel van megosztva. A SimpleX-kiszolgálók nem láthatják a profilját."; + +/* alert message */ +"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "A profilja módosult. Ha elmenti, a profilfrissítés el lesz küldve a partnerei számára."; /* No comment provided by engineer. */ -"Your profile, contacts and delivered messages are stored on your device." = "Profilja, ismerősök és az elküldött üzenetek az eszközön kerülnek tárolásra."; +"Your profile, contacts and delivered messages are stored on your device." = "A profilja, a partnerei és az elküldött üzenetei a saját eszközén vannak tárolva."; /* No comment provided by engineer. */ "Your random profile" = "Véletlenszerű profil"; /* No comment provided by engineer. */ -"Your server" = "Saját kiszolgáló"; +"Your server address" = "Saját SMP-kiszolgálójának címe"; /* No comment provided by engineer. */ -"Your server address" = "Saját kiszolgáló cím"; +"Your servers" = "Saját kiszolgálók"; /* No comment provided by engineer. */ "Your settings" = "Beállítások"; /* No comment provided by engineer. */ -"Your SimpleX address" = "SimpleX azonosítója"; - -/* No comment provided by engineer. */ -"Your SMP servers" = "SMP kiszolgálók"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "XFTP kiszolgálók"; +"Your SimpleX address" = "Profil SimpleX-címe"; diff --git a/apps/ios/hu.lproj/SimpleX--iOS--InfoPlist.strings b/apps/ios/hu.lproj/SimpleX--iOS--InfoPlist.strings index 8ba8ca5b41..f389e41458 100644 --- a/apps/ios/hu.lproj/SimpleX--iOS--InfoPlist.strings +++ b/apps/ios/hu.lproj/SimpleX--iOS--InfoPlist.strings @@ -2,17 +2,17 @@ "CFBundleName" = "SimpleX"; /* Privacy - Camera Usage Description */ -"NSCameraUsageDescription" = "A SimpleX-nek kamera-hozzáférésre van szüksége a QR-kódok beolvasásához, hogy csatlakozhasson más felhasználókhoz és videohívásokhoz."; +"NSCameraUsageDescription" = "A SimpleXnek kamera-hozzáférésre van szüksége a QR-kódok beolvasásához, hogy kapcsolódhasson más felhasználókhoz és videohívásokhoz."; /* Privacy - Face ID Usage Description */ "NSFaceIDUsageDescription" = "A SimpleX Face ID-t használ a helyi hitelesítéshez"; /* Privacy - Local Network Usage Description */ -"NSLocalNetworkUsageDescription" = "A SimpleX helyi hálózati hozzáférést használ, hogy lehetővé tegye a felhasználói csevegőprofil használatát számítógépen keresztül ugyanazon a hálózaton."; +"NSLocalNetworkUsageDescription" = "A SimpleX helyi hálózati hozzáférést használ, hogy lehetővé tegye a felhasználói csevegési profil használatát számítógépen keresztül ugyanazon a hálózaton."; /* Privacy - Microphone Usage Description */ -"NSMicrophoneUsageDescription" = "A SimpleX-nek mikrofon-hozzáférésre van szüksége hang- és videohívásokhoz, valamint hangüzenetek rögzítéséhez."; +"NSMicrophoneUsageDescription" = "A SimpleXnek mikrofon-hozzáférésre van szüksége hang- és videohívásokhoz, valamint hangüzenetek rögzítéséhez."; /* Privacy - Photo Library Additions Usage Description */ -"NSPhotoLibraryAddUsageDescription" = "A SimpleX-nek hozzáférésre van szüksége a Galériához a rögzített és fogadott média mentéséhez"; +"NSPhotoLibraryAddUsageDescription" = "A SimpleXnek galéria-hozzáférésre van szüksége a rögzített és fogadott média mentéséhez"; diff --git a/apps/ios/it.lproj/Localizable.strings b/apps/ios/it.lproj/Localizable.strings index c105bb1fa1..b914a06079 100644 --- a/apps/ios/it.lproj/Localizable.strings +++ b/apps/ios/it.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (può essere copiato)"; @@ -31,30 +16,15 @@ /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- messaggi vocali fino a 5 minuti.\n- tempo di scomparsa personalizzato.\n- cronologia delle modifiche."; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 colorato!"; -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - /* No comment provided by engineer. */ "(new)" = "(nuovo)"; /* No comment provided by engineer. */ "(this device v%@)" = "(questo dispositivo v%@)"; -/* No comment provided by engineer. */ -")" = ")"; - /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Contribuisci](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -65,10 +35,7 @@ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Dai una stella su GitHub](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add contact**: to create a new invitation link, or connect via a link you received." = "**Aggiungi contatto**: per creare un nuovo link di invito o connetterti tramite un link che hai ricevuto."; - -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Aggiungi un contatto**: per creare il tuo codice QR o link una tantum per il tuo contatto."; +"**Create 1-time link**: to create and share a new invitation link." = "**Aggiungi contatto**: per creare un nuovo link di invito."; /* No comment provided by engineer. */ "**Create group**: to create a new group." = "**Crea gruppo**: per creare un nuovo gruppo."; @@ -80,20 +47,29 @@ "**e2e encrypted** video call" = "Videochiamata **crittografata e2e**"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Più privato**: controlla messaggi nuovi ogni 20 minuti. Viene condiviso il token del dispositivo con il server di SimpleX Chat, ma non quanti contatti o messaggi hai."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Più privato**: controlla messaggi nuovi ogni 20 minuti. Viene condiviso il token del dispositivo con il server di SimpleX Chat, ma non quanti contatti o messaggi hai."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Il più privato**: non usare il server di notifica di SimpleX Chat, controlla i messaggi periodicamente in secondo piano (dipende da quanto spesso usi l'app)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**Il più privato**: non usare il server di notifica di SimpleX Chat, controlla i messaggi periodicamente in secondo piano (dipende da quanto spesso usi l'app)."; + +/* No comment provided by engineer. */ +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Nota bene**: usare lo stesso database su due dispositivi bloccherà la decifrazione dei messaggi dalle tue connessioni, come misura di sicurezza."; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Nota bene**: NON potrai recuperare o cambiare la password se la perdi."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Consigliato**: vengono inviati il token del dispositivo e le notifiche al server di notifica di SimpleX Chat, ma non il contenuto del messaggio,la sua dimensione o il suo mittente."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Consigliato**: vengono inviati il token del dispositivo e le notifiche al server di notifica di SimpleX Chat, ma non il contenuto del messaggio,la sua dimensione o il suo mittente."; + +/* No comment provided by engineer. */ +"**Scan / Paste link**: to connect via a link you received." = "**Scansiona / Incolla link**: per connetterti tramite un link che hai ricevuto."; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Attenzione**: le notifiche push istantanee richiedono una password salvata nel portachiavi."; +/* No comment provided by engineer. */ +"**Warning**: the archive will be removed." = "**Attenzione**: l'archivio verrà rimosso."; + /* No comment provided by engineer. */ "*bold*" = "\\*grassetto*"; @@ -134,7 +110,10 @@ "%@ at %@:" = "%1$@ alle %2$@:"; /* No comment provided by engineer. */ -"%@ connected" = "%@ si è connesso/a"; +"%@ connected" = "%@ connesso/a"; + +/* No comment provided by engineer. */ +"%@ downloaded" = "%@ scaricati"; /* notification title */ "%@ is connected!" = "%@ è connesso/a!"; @@ -146,11 +125,20 @@ "%@ is verified" = "%@ è verificato/a"; /* No comment provided by engineer. */ -"%@ servers" = "Server %@"; +"%@ server" = "%@ server"; + +/* No comment provided by engineer. */ +"%@ servers" = "%@ server"; + +/* No comment provided by engineer. */ +"%@ uploaded" = "%@ caricati"; /* notification title */ "%@ wants to connect!" = "%@ si vuole connettere!"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + /* No comment provided by engineer. */ "%@, %@ and %lld members" = "%@, %@ e %lld membri"; @@ -163,9 +151,24 @@ /* time interval */ "%d days" = "%d giorni"; +/* forward confirmation reason */ +"%d file(s) are still being downloaded." = "%d file è/sono ancora in scaricamento."; + +/* forward confirmation reason */ +"%d file(s) failed to download." = "%d file ha/hanno fallito lo scaricamento."; + +/* forward confirmation reason */ +"%d file(s) were deleted." = "%d file è/sono stato/i eliminato/i."; + +/* forward confirmation reason */ +"%d file(s) were not downloaded." = "%d file non è/sono stato/i scaricato/i."; + /* time interval */ "%d hours" = "%d ore"; +/* alert title */ +"%d messages not forwarded" = "%d messaggi non inoltrati"; + /* time interval */ "%d min" = "%d min"; @@ -175,6 +178,9 @@ /* time interval */ "%d sec" = "%d sec"; +/* delete after time */ +"%d seconds(s)" = "%d secondo/i"; + /* integrity error chat item */ "%d skipped message(s)" = "%d messaggio/i saltato/i"; @@ -217,9 +223,6 @@ /* No comment provided by engineer. */ "%lld new interface languages" = "%lld nuove lingue dell'interfaccia"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld secondo/i"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld secondi"; @@ -265,7 +268,8 @@ /* No comment provided by engineer. */ "0s" = "0s"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "1 giorno"; /* time interval */ @@ -274,12 +278,23 @@ /* No comment provided by engineer. */ "1 minute" = "1 minuto"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "1 mese"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "1 settimana"; +/* delete after time */ +"1 year" = "1 anno"; + +/* No comment provided by engineer. */ +"1-time link" = "Link una tantum"; + +/* No comment provided by engineer. */ +"1-time link can be used *with one contact only* - share in person or via any messenger." = "Il link una tantum può essere usato *con un solo contatto* - condividilo di persona o tramite qualsiasi messenger."; + /* No comment provided by engineer. */ "5 minutes" = "5 minuti"; @@ -314,10 +329,7 @@ "Abort changing address?" = "Interrompere il cambio di indirizzo?"; /* No comment provided by engineer. */ -"About SimpleX" = "Riguardo SimpleX"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "Info sull'indirizzo SimpleX"; +"About operators" = "Info sugli operatori"; /* No comment provided by engineer. */ "About SimpleX Chat" = "Riguardo SimpleX Chat"; @@ -326,81 +338,158 @@ "above, then choose:" = "sopra, quindi scegli:"; /* No comment provided by engineer. */ -"Accent color" = "Colore principale"; +"Accent" = "Principale"; /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "Accetta"; +/* No comment provided by engineer. */ +"Accept conditions" = "Accetta le condizioni"; + /* No comment provided by engineer. */ "Accept connection request?" = "Accettare la richiesta di connessione?"; /* notification body */ "Accept contact request from %@?" = "Accettare la richiesta di contatto da %@?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "Accetta in incognito"; /* call status */ "accepted call" = "chiamata accettata"; +/* No comment provided by engineer. */ +"Accepted conditions" = "Condizioni accettate"; + +/* chat list item title */ +"accepted invitation" = "invito accettato"; + +/* No comment provided by engineer. */ +"Acknowledged" = "Riconosciuto"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Errori di riconoscimento"; + +/* token status text */ +"Active" = "Attivo"; + +/* No comment provided by engineer. */ +"Active connections" = "Connessioni attive"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Aggiungi l'indirizzo al tuo profilo, in modo che i tuoi contatti possano condividerlo con altre persone. L'aggiornamento del profilo verrà inviato ai tuoi contatti."; /* No comment provided by engineer. */ -"Add contact" = "Aggiungi contatto"; +"Add friends" = "Aggiungi amici"; /* No comment provided by engineer. */ -"Add preset servers" = "Aggiungi server preimpostati"; +"Add list" = "Aggiungi elenco"; /* No comment provided by engineer. */ "Add profile" = "Aggiungi profilo"; /* No comment provided by engineer. */ -"Add server…" = "Aggiungi server…"; +"Add server" = "Aggiungi server"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "Aggiungi server scansionando codici QR."; +/* No comment provided by engineer. */ +"Add team members" = "Aggiungi membri del team"; + /* No comment provided by engineer. */ "Add to another device" = "Aggiungi ad un altro dispositivo"; +/* No comment provided by engineer. */ +"Add to list" = "Aggiungi ad un elenco"; + /* No comment provided by engineer. */ "Add welcome message" = "Aggiungi messaggio di benvenuto"; +/* No comment provided by engineer. */ +"Add your team members to the conversations." = "Aggiungi i membri del tuo team alle conversazioni."; + +/* No comment provided by engineer. */ +"Added media & file servers" = "Server di multimediali e file aggiunti"; + +/* No comment provided by engineer. */ +"Added message servers" = "Server dei messaggi aggiunti"; + +/* No comment provided by engineer. */ +"Additional accent" = "Principale aggiuntivo"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Principale aggiuntivo 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Secondario aggiuntivo"; + /* No comment provided by engineer. */ "Address" = "Indirizzo"; /* No comment provided by engineer. */ "Address change will be aborted. Old receiving address will be used." = "Il cambio di indirizzo verrà interrotto. Verrà usato il vecchio indirizzo di ricezione."; +/* No comment provided by engineer. */ +"Address or 1-time link?" = "Indirizzo o link una tantum?"; + +/* No comment provided by engineer. */ +"Address settings" = "Impostazioni dell'indirizzo"; + /* member role */ "admin" = "amministratore"; +/* feature role */ +"admins" = "amministratori"; + +/* No comment provided by engineer. */ +"Admins can block a member for all." = "Gli amministratori possono bloccare un membro per tutti."; + /* No comment provided by engineer. */ "Admins can create the links to join groups." = "Gli amministratori possono creare i link per entrare nei gruppi."; /* No comment provided by engineer. */ "Advanced network settings" = "Impostazioni di rete avanzate"; +/* No comment provided by engineer. */ +"Advanced settings" = "Impostazioni avanzate"; + /* chat item text */ "agreeing encryption for %@…" = "concordando la crittografia per %@…"; /* chat item text */ "agreeing encryption…" = "concordando la crittografia…"; +/* No comment provided by engineer. */ +"All" = "Tutte"; + /* No comment provided by engineer. */ "All app data is deleted." = "Tutti i dati dell'app vengono eliminati."; /* No comment provided by engineer. */ "All chats and messages will be deleted - this cannot be undone!" = "Tutte le chat e i messaggi verranno eliminati. Non è reversibile!"; +/* alert message */ +"All chats will be removed from the list %@, and the list deleted." = "Tutte le chat verranno rimosse dall'elenco %@ e l'elenco eliminato."; + /* No comment provided by engineer. */ "All data is erased when it is entered." = "Tutti i dati vengono cancellati quando inserito."; +/* No comment provided by engineer. */ +"All data is kept private on your device." = "Tutti i dati sono privati, nel tuo dispositivo."; + /* No comment provided by engineer. */ "All group members will remain connected." = "Tutti i membri del gruppo resteranno connessi."; +/* feature role */ +"all members" = "tutti i membri"; + +/* No comment provided by engineer. */ +"All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages." = "Tutti i messaggi e i file vengono inviati **crittografati end-to-end**, con sicurezza resistenti alla quantistica nei messaggi diretti."; + /* No comment provided by engineer. */ "All messages will be deleted - this cannot be undone!" = "Tutti i messaggi verranno eliminati, non è reversibile!"; @@ -410,21 +499,39 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Tutti i nuovi messaggi da %@ verrranno nascosti!"; +/* profile dropdown */ +"All profiles" = "Tutti gli profili"; + +/* No comment provided by engineer. */ +"All reports will be archived for you." = "Tutte le segnalazioni verranno archiviate per te."; + +/* No comment provided by engineer. */ +"All servers" = "Tutti i server"; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Tutti i tuoi contatti resteranno connessi."; /* No comment provided by engineer. */ "All your contacts will remain connected. Profile update will be sent to your contacts." = "Tutti i tuoi contatti resteranno connessi. L'aggiornamento del profilo verrà inviato ai tuoi contatti."; +/* No comment provided by engineer. */ +"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Tutti i tuoi contatti, le conversazioni e i file verranno criptati in modo sicuro e caricati in blocchi sui relay XFTP configurati."; + /* No comment provided by engineer. */ "Allow" = "Consenti"; /* No comment provided by engineer. */ "Allow calls only if your contact allows them." = "Consenti le chiamate solo se il tuo contatto le consente."; +/* No comment provided by engineer. */ +"Allow calls?" = "Consentire le chiamate?"; + /* No comment provided by engineer. */ "Allow disappearing messages only if your contact allows it to you." = "Consenti i messaggi a tempo solo se il contatto li consente a te."; +/* No comment provided by engineer. */ +"Allow downgrade" = "Consenti downgrade"; + /* No comment provided by engineer. */ "Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Consenti l'eliminazione irreversibile dei messaggi solo se il contatto la consente a te. (24 ore)"; @@ -440,12 +547,21 @@ /* No comment provided by engineer. */ "Allow sending disappearing messages." = "Permetti l'invio di messaggi a tempo."; +/* No comment provided by engineer. */ +"Allow sharing" = "Consenti la condivisione"; + /* No comment provided by engineer. */ "Allow to irreversibly delete sent messages. (24 hours)" = "Permetti di eliminare irreversibilmente i messaggi inviati. (24 ore)"; +/* No comment provided by engineer. */ +"Allow to report messsages to moderators." = "Consenti di segnalare messaggi ai moderatori."; + /* No comment provided by engineer. */ "Allow to send files and media." = "Consenti l'invio di file e contenuti multimediali."; +/* No comment provided by engineer. */ +"Allow to send SimpleX links." = "Consenti di inviare link di SimpleX."; + /* No comment provided by engineer. */ "Allow to send voice messages." = "Permetti l'invio di messaggi vocali."; @@ -482,6 +598,9 @@ /* pref value */ "always" = "sempre"; +/* No comment provided by engineer. */ +"Always use private routing." = "Usa sempre l'instradamento privato."; + /* No comment provided by engineer. */ "Always use relay" = "Connetti via relay"; @@ -491,15 +610,27 @@ /* No comment provided by engineer. */ "and %lld other events" = "e altri %lld eventi"; +/* report reason */ +"Another reason" = "Altro motivo"; + /* No comment provided by engineer. */ "Answer call" = "Rispondi alla chiamata"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "Chiunque può installare i server."; + /* No comment provided by engineer. */ "App build: %@" = "Build dell'app: %@"; +/* No comment provided by engineer. */ +"App data migration" = "Migrazione dati dell'app"; + /* No comment provided by engineer. */ "App encrypts new local files (except videos)." = "L'app cripta i nuovi file locali (eccetto i video)."; +/* No comment provided by engineer. */ +"App group:" = "Gruppo app:"; + /* No comment provided by engineer. */ "App icon" = "Icona app"; @@ -509,6 +640,9 @@ /* No comment provided by engineer. */ "App passcode is replaced with self-destruct passcode." = "Il codice di accesso dell'app viene sostituito da un codice di autodistruzione."; +/* No comment provided by engineer. */ +"App session" = "Sessione dell'app"; + /* No comment provided by engineer. */ "App version" = "Versione dell'app"; @@ -518,9 +652,51 @@ /* No comment provided by engineer. */ "Appearance" = "Aspetto"; +/* No comment provided by engineer. */ +"Apply" = "Applica"; + +/* No comment provided by engineer. */ +"Apply to" = "Applica a"; + +/* No comment provided by engineer. */ +"Archive" = "Archivia"; + +/* No comment provided by engineer. */ +"Archive %lld reports?" = "Archiviare %lld segnalazioni?"; + +/* No comment provided by engineer. */ +"Archive all reports?" = "Archiviare tutte le segnalazioni?"; + +/* No comment provided by engineer. */ +"Archive and upload" = "Archivia e carica"; + +/* No comment provided by engineer. */ +"Archive contacts to chat later." = "Archivia contatti per chattare più tardi."; + +/* No comment provided by engineer. */ +"Archive report" = "Archivia la segnalazione"; + +/* No comment provided by engineer. */ +"Archive report?" = "Archiviare la segnalazione?"; + +/* swipe action */ +"Archive reports" = "Archivia segnalazioni"; + +/* No comment provided by engineer. */ +"Archived contacts" = "Contatti archiviati"; + +/* No comment provided by engineer. */ +"archived report" = "segnalazione archiviata"; + +/* No comment provided by engineer. */ +"Archiving database" = "Archiviazione del database"; + /* No comment provided by engineer. */ "Attach" = "Allega"; +/* No comment provided by engineer. */ +"attempts" = "tentativi"; + /* No comment provided by engineer. */ "Audio & video calls" = "Chiamate audio e video"; @@ -555,14 +731,20 @@ "Auto-accept" = "Accetta automaticamente"; /* No comment provided by engineer. */ -"Auto-accept contact requests" = "Auto-accetta richieste di contatto"; +"Auto-accept contact requests" = "Auto-accetta le richieste di contatto"; /* No comment provided by engineer. */ -"Auto-accept images" = "Auto-accetta immagini"; +"Auto-accept images" = "Auto-accetta le immagini"; + +/* alert title */ +"Auto-accept settings" = "Accetta automaticamente le impostazioni"; /* No comment provided by engineer. */ "Back" = "Indietro"; +/* No comment provided by engineer. */ +"Background" = "Sfondo"; + /* No comment provided by engineer. */ "Bad desktop address" = "Indirizzo desktop errato"; @@ -578,12 +760,39 @@ /* No comment provided by engineer. */ "Bad message ID" = "ID del messaggio errato"; +/* No comment provided by engineer. */ +"Better calls" = "Chiamate migliorate"; + /* No comment provided by engineer. */ "Better groups" = "Gruppi migliorati"; +/* No comment provided by engineer. */ +"Better groups performance" = "Prestazioni dei gruppi migliorate"; + +/* No comment provided by engineer. */ +"Better message dates." = "Date dei messaggi migliorate."; + /* No comment provided by engineer. */ "Better messages" = "Messaggi migliorati"; +/* No comment provided by engineer. */ +"Better networking" = "Rete migliorata"; + +/* No comment provided by engineer. */ +"Better notifications" = "Notifiche migliorate"; + +/* No comment provided by engineer. */ +"Better privacy and security" = "Privacy e sicurezza migliori"; + +/* No comment provided by engineer. */ +"Better security ✅" = "Sicurezza migliorata ✅"; + +/* No comment provided by engineer. */ +"Better user experience" = "Esperienza utente migliorata"; + +/* No comment provided by engineer. */ +"Black" = "Nero"; + /* No comment provided by engineer. */ "Block" = "Blocca"; @@ -608,12 +817,19 @@ /* rcv group event chat item */ "blocked %@" = "ha bloccato %@"; -/* marked deleted chat item preview text */ +/* blocked chat item +marked deleted chat item preview text */ "blocked by admin" = "bloccato dall'amministratore"; /* No comment provided by engineer. */ "Blocked by admin" = "Bloccato dall'amministratore"; +/* No comment provided by engineer. */ +"Blur for better privacy." = "Sfoca per una privacy maggiore."; + +/* No comment provided by engineer. */ +"Blur media" = "Sfocatura dei file multimediali"; + /* No comment provided by engineer. */ "bold" = "grassetto"; @@ -635,9 +851,24 @@ /* No comment provided by engineer. */ "Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Bulgaro, finlandese, tailandese e ucraino - grazie agli utenti e a [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"; +/* No comment provided by engineer. */ +"Business address" = "Indirizzo di lavoro"; + +/* No comment provided by engineer. */ +"Business chats" = "Chat di lavoro"; + +/* No comment provided by engineer. */ +"Businesses" = "Lavorative"; + /* No comment provided by engineer. */ "By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Per profilo di chat (predefinito) o [per connessione](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)."; +/* No comment provided by engineer. */ +"By using SimpleX Chat you agree to:\n- send only legal content in public groups.\n- respect other users – no spam." = "Usando SimpleX Chat accetti di:\n- inviare solo contenuto legale nei gruppi pubblici.\n- rispettare gli altri utenti - niente spam."; + +/* No comment provided by engineer. */ +"call" = "chiama"; + /* No comment provided by engineer. */ "Call already ended!" = "Chiamata già terminata!"; @@ -653,9 +884,18 @@ /* No comment provided by engineer. */ "Calls" = "Chiamate"; +/* No comment provided by engineer. */ +"Calls prohibited!" = "Chiamate proibite!"; + /* No comment provided by engineer. */ "Camera not available" = "Fotocamera non disponibile"; +/* No comment provided by engineer. */ +"Can't call contact" = "Impossibile chiamare il contatto"; + +/* No comment provided by engineer. */ +"Can't call member" = "Impossibile chiamare il membro"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Impossibile invitare il contatto!"; @@ -663,8 +903,15 @@ "Can't invite contacts!" = "Impossibile invitare i contatti!"; /* No comment provided by engineer. */ +"Can't message member" = "Impossibile inviare un messaggio al membro"; + +/* alert action +alert button */ "Cancel" = "Annulla"; +/* No comment provided by engineer. */ +"Cancel migration" = "Annulla migrazione"; + /* feature offered item */ "cancelled %@" = "annullato %@"; @@ -672,11 +919,26 @@ "Cannot access keychain to save database password" = "Impossibile accedere al portachiavi per salvare la password del database"; /* No comment provided by engineer. */ +"Cannot forward message" = "Impossibile inoltrare il messaggio"; + +/* alert title */ "Cannot receive file" = "Impossibile ricevere il file"; +/* snd error text */ +"Capacity exceeded - recipient did not receive previously sent messages." = "Quota superata - il destinatario non ha ricevuto i messaggi precedentemente inviati."; + +/* No comment provided by engineer. */ +"Cellular" = "Mobile"; + /* No comment provided by engineer. */ "Change" = "Cambia"; +/* alert title */ +"Change automatic message deletion?" = "Cambiare l'eliminazione automatica dei messaggi?"; + +/* authentication reason */ +"Change chat profiles" = "Modifica profili utente"; + /* No comment provided by engineer. */ "Change database passphrase?" = "Cambiare password del database?"; @@ -702,7 +964,7 @@ "Change self-destruct mode" = "Cambia modalità di autodistruzione"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "Cambia codice di autodistruzione"; /* chat item text */ @@ -712,7 +974,7 @@ "changed role of %@ to %@" = "ha cambiato il ruolo di %1$@ in %2$@"; /* rcv group event chat item */ -"changed your role to %@" = "cambiato il tuo ruolo in %@"; +"changed your role to %@" = "ha cambiato il tuo ruolo in %@"; /* chat item text */ "changing address for %@…" = "cambio indirizzo per %@…"; @@ -721,7 +983,16 @@ "changing address…" = "cambio indirizzo…"; /* No comment provided by engineer. */ -"Chat archive" = "Archivio chat"; +"Chat" = "Chat"; + +/* No comment provided by engineer. */ +"Chat already exists" = "La chat esiste già"; + +/* No comment provided by engineer. */ +"Chat already exists!" = "La chat esiste già!"; + +/* No comment provided by engineer. */ +"Chat colors" = "Colori della chat"; /* No comment provided by engineer. */ "Chat console" = "Console della chat"; @@ -732,6 +1003,9 @@ /* No comment provided by engineer. */ "Chat database deleted" = "Database della chat eliminato"; +/* No comment provided by engineer. */ +"Chat database exported" = "Database della chat esportato"; + /* No comment provided by engineer. */ "Chat database imported" = "Database della chat importato"; @@ -744,18 +1018,48 @@ /* No comment provided by engineer. */ "Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "La chat è ferma. Se hai già usato questo database su un altro dispositivo, dovresti trasferirlo prima di avviare la chat."; +/* No comment provided by engineer. */ +"Chat list" = "Elenco delle chat"; + +/* No comment provided by engineer. */ +"Chat migrated!" = "Chat migrata!"; + /* No comment provided by engineer. */ "Chat preferences" = "Preferenze della chat"; +/* alert message */ +"Chat preferences were changed." = "Le preferenze della chat sono state cambiate."; + +/* No comment provided by engineer. */ +"Chat profile" = "Profilo utente"; + +/* No comment provided by engineer. */ +"Chat theme" = "Tema della chat"; + +/* No comment provided by engineer. */ +"Chat will be deleted for all members - this cannot be undone!" = "La chat verrà eliminata per tutti i membri, non è reversibile!"; + +/* No comment provided by engineer. */ +"Chat will be deleted for you - this cannot be undone!" = "La chat verrà eliminata solo per te, non è reversibile!"; + /* No comment provided by engineer. */ "Chats" = "Chat"; /* No comment provided by engineer. */ +"Check messages every 20 min." = "Controlla i messaggi ogni 20 min."; + +/* No comment provided by engineer. */ +"Check messages when allowed." = "Controlla i messaggi quando consentito."; + +/* alert title */ "Check server address and try again." = "Controlla l'indirizzo del server e riprova."; /* No comment provided by engineer. */ "Chinese and Spanish interface" = "Interfaccia cinese e spagnola"; +/* No comment provided by engineer. */ +"Choose _Migrate from another device_ on the new device and scan QR code." = "Scegli _Migra da un altro dispositivo_ sul nuovo dispositivo e scansione il codice QR."; + /* No comment provided by engineer. */ "Choose file" = "Scegli file"; @@ -763,6 +1067,15 @@ "Choose from library" = "Scegli dalla libreria"; /* No comment provided by engineer. */ +"Chunks deleted" = "Blocchi eliminati"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "Blocchi scaricati"; + +/* No comment provided by engineer. */ +"Chunks uploaded" = "Blocchi inviati"; + +/* swipe action */ "Clear" = "Svuota"; /* No comment provided by engineer. */ @@ -771,6 +1084,12 @@ /* No comment provided by engineer. */ "Clear conversation?" = "Svuotare la conversazione?"; +/* No comment provided by engineer. */ +"Clear group?" = "Svuotare il gruppo?"; + +/* No comment provided by engineer. */ +"Clear or delete group?" = "Svuotare o eliminare il gruppo?"; + /* No comment provided by engineer. */ "Clear private notes?" = "Svuotare le note private?"; @@ -778,10 +1097,16 @@ "Clear verification" = "Annulla la verifica"; /* No comment provided by engineer. */ -"colored" = "colorato"; +"Color chats with the new themes." = "Colora le chat con i nuovi temi."; /* No comment provided by engineer. */ -"Colors" = "Colori"; +"Color mode" = "Modalità di colore"; + +/* No comment provided by engineer. */ +"colored" = "colorato"; + +/* report reason */ +"Community guidelines violation" = "Violazione delle linee guida della comunità"; /* server test step */ "Compare file" = "Confronta file"; @@ -792,15 +1117,51 @@ /* No comment provided by engineer. */ "complete" = "completo"; +/* No comment provided by engineer. */ +"Completed" = "Completato"; + +/* No comment provided by engineer. */ +"Conditions accepted on: %@." = "Condizioni accettate il: %@."; + +/* No comment provided by engineer. */ +"Conditions are accepted for the operator(s): **%@**." = "Le condizioni sono state accettate per gli operatori: **%@**."; + +/* No comment provided by engineer. */ +"Conditions are already accepted for these operator(s): **%@**." = "Le condizioni sono già state accettate per i seguenti operatori: **%@**."; + +/* No comment provided by engineer. */ +"Conditions of use" = "Condizioni d'uso"; + +/* No comment provided by engineer. */ +"Conditions will be accepted for the operator(s): **%@**." = "Le condizioni verranno accettate per gli operatori: **%@**."; + +/* No comment provided by engineer. */ +"Conditions will be accepted on: %@." = "Le condizioni verranno accettate il: %@."; + +/* No comment provided by engineer. */ +"Conditions will be automatically accepted for enabled operators on: %@." = "Le condizioni verranno accettate automaticamente per gli operatori attivi il: %@."; + /* No comment provided by engineer. */ "Configure ICE servers" = "Configura server ICE"; +/* No comment provided by engineer. */ +"Configure server operators" = "Configura gli operatori dei server"; + /* No comment provided by engineer. */ "Confirm" = "Conferma"; +/* No comment provided by engineer. */ +"Confirm contact deletion?" = "Confermare l'eliminazione del contatto?"; + /* No comment provided by engineer. */ "Confirm database upgrades" = "Conferma aggiornamenti database"; +/* No comment provided by engineer. */ +"Confirm files from unknown servers." = "Conferma i file da server sconosciuti."; + +/* No comment provided by engineer. */ +"Confirm network settings" = "Conferma le impostazioni di rete"; + /* No comment provided by engineer. */ "Confirm new passphrase…" = "Conferma nuova password…"; @@ -810,6 +1171,15 @@ /* No comment provided by engineer. */ "Confirm password" = "Conferma password"; +/* No comment provided by engineer. */ +"Confirm that you remember database passphrase to migrate it." = "Conferma che ricordi la password del database da migrare."; + +/* No comment provided by engineer. */ +"Confirm upload" = "Conferma caricamento"; + +/* token status text */ +"Confirmed" = "Confermato"; + /* server test step */ "Connect" = "Connetti"; @@ -825,6 +1195,9 @@ /* No comment provided by engineer. */ "connect to SimpleX Chat developers." = "connettiti agli sviluppatori di SimpleX Chat."; +/* No comment provided by engineer. */ +"Connect to your friends faster." = "Connettiti più velocemente ai tuoi amici."; + /* No comment provided by engineer. */ "Connect to yourself?" = "Connettersi a te stesso?"; @@ -849,18 +1222,27 @@ /* No comment provided by engineer. */ "connected" = "connesso/a"; +/* No comment provided by engineer. */ +"Connected" = "Connesso"; + /* No comment provided by engineer. */ "Connected desktop" = "Desktop connesso"; /* rcv group event chat item */ "connected directly" = "si è connesso/a direttamente"; +/* No comment provided by engineer. */ +"Connected servers" = "Server connessi"; + /* No comment provided by engineer. */ "Connected to desktop" = "Connesso al desktop"; /* No comment provided by engineer. */ "connecting" = "in connessione"; +/* No comment provided by engineer. */ +"Connecting" = "In connessione"; + /* No comment provided by engineer. */ "connecting (accepted)" = "in connessione (accettato)"; @@ -882,15 +1264,24 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "Connessione al server… (errore: %@)"; +/* No comment provided by engineer. */ +"Connecting to contact, please wait or check later!" = "In collegamento con il contatto, attendi o controlla più tardi!"; + /* No comment provided by engineer. */ "Connecting to desktop" = "Connessione al desktop"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "in connessione…"; /* No comment provided by engineer. */ "Connection" = "Connessione"; +/* No comment provided by engineer. */ +"Connection and servers status." = "Stato della connessione e dei server."; + +/* No comment provided by engineer. */ +"Connection blocked" = "Connessione bloccata"; + /* No comment provided by engineer. */ "Connection error" = "Errore di connessione"; @@ -900,18 +1291,39 @@ /* chat list item title (it should not be shown */ "connection established" = "connessione stabilita"; +/* No comment provided by engineer. */ +"Connection is blocked by server operator:\n%@" = "La connessione è bloccata dall'operatore del server:\n%@"; + +/* No comment provided by engineer. */ +"Connection not ready." = "Connessione non pronta."; + +/* No comment provided by engineer. */ +"Connection notifications" = "Notifiche di connessione"; + /* No comment provided by engineer. */ "Connection request sent!" = "Richiesta di connessione inviata!"; +/* No comment provided by engineer. */ +"Connection requires encryption renegotiation." = "La connessione richiede la rinegoziazione della crittografia."; + +/* No comment provided by engineer. */ +"Connection security" = "Sicurezza della connessione"; + /* No comment provided by engineer. */ "Connection terminated" = "Connessione terminata"; /* No comment provided by engineer. */ "Connection timeout" = "Connessione scaduta"; +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "Connessione con il desktop fermata"; + /* connection information */ "connection:%@" = "connessione:% @"; +/* No comment provided by engineer. */ +"Connections" = "Connessioni"; + /* profile update event chat item */ "contact %@ changed to %@" = "contatto %1$@ cambiato in %2$@"; @@ -921,6 +1333,9 @@ /* No comment provided by engineer. */ "Contact already exists" = "Il contatto esiste già"; +/* No comment provided by engineer. */ +"Contact deleted!" = "Contatto eliminato!"; + /* No comment provided by engineer. */ "contact has e2e encryption" = "il contatto ha la crittografia e2e"; @@ -934,7 +1349,7 @@ "Contact is connected" = "Il contatto è connesso"; /* No comment provided by engineer. */ -"Contact is not connected yet!" = "Il contatto non è ancora connesso!"; +"Contact is deleted." = "Il contatto è stato eliminato."; /* No comment provided by engineer. */ "Contact name" = "Nome del contatto"; @@ -942,21 +1357,36 @@ /* No comment provided by engineer. */ "Contact preferences" = "Preferenze del contatto"; +/* No comment provided by engineer. */ +"Contact will be deleted - this cannot be undone!" = "Il contatto verrà eliminato - non è reversibile!"; + /* No comment provided by engineer. */ "Contacts" = "Contatti"; /* No comment provided by engineer. */ "Contacts can mark messages for deletion; you will be able to view them." = "I contatti possono contrassegnare i messaggi per l'eliminazione; potrai vederli."; +/* blocking reason */ +"Content violates conditions of use" = "Il contenuto viola le condizioni di utilizzo"; + /* No comment provided by engineer. */ "Continue" = "Continua"; -/* chat item action */ +/* No comment provided by engineer. */ +"Conversation deleted!" = "Conversazione eliminata!"; + +/* No comment provided by engineer. */ "Copy" = "Copia"; +/* No comment provided by engineer. */ +"Copy error" = "Copia errore"; + /* No comment provided by engineer. */ "Core version: v%@" = "Versione core: v%@"; +/* No comment provided by engineer. */ +"Corner" = "Angolo"; + /* No comment provided by engineer. */ "Correct name to %@?" = "Correggere il nome a %@?"; @@ -964,10 +1394,10 @@ "Create" = "Crea"; /* No comment provided by engineer. */ -"Create a group using a random profile." = "Crea un gruppo usando un profilo casuale."; +"Create 1-time link" = "Crea link una tantum"; /* No comment provided by engineer. */ -"Create an address to let people connect with you." = "Crea un indirizzo per consentire alle persone di connettersi con te."; +"Create a group using a random profile." = "Crea un gruppo usando un profilo casuale."; /* server test step */ "Create file" = "Crea file"; @@ -981,6 +1411,9 @@ /* No comment provided by engineer. */ "Create link" = "Crea link"; +/* No comment provided by engineer. */ +"Create list" = "Crea elenco"; + /* No comment provided by engineer. */ "Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" = "Crea un nuovo profilo nell'[app desktop](https://simplex.chat/downloads/). 💻"; @@ -999,6 +1432,9 @@ /* No comment provided by engineer. */ "Create your profile" = "Crea il tuo profilo"; +/* No comment provided by engineer. */ +"Created" = "Creato"; + /* No comment provided by engineer. */ "Created at" = "Creato il"; @@ -1006,7 +1442,7 @@ "Created at: %@" = "Creato il: %@"; /* No comment provided by engineer. */ -"Created on %@" = "Creato il %@"; +"Creating archive link" = "Creazione link dell'archivio"; /* No comment provided by engineer. */ "Creating link…" = "Creazione link…"; @@ -1014,12 +1450,18 @@ /* No comment provided by engineer. */ "creator" = "creatore"; +/* No comment provided by engineer. */ +"Current conditions text couldn't be loaded, you can review conditions via this link:" = "Il testo delle condizioni attuali testo non è stato caricato, puoi consultare le condizioni tramite questo link:"; + /* No comment provided by engineer. */ "Current Passcode" = "Codice di accesso attuale"; /* No comment provided by engineer. */ "Current passphrase…" = "Password attuale…"; +/* No comment provided by engineer. */ +"Current profile" = "Profilo attuale"; + /* No comment provided by engineer. */ "Currently maximum supported file size is %@." = "Attualmente la dimensione massima supportata è di %@."; @@ -1029,9 +1471,18 @@ /* No comment provided by engineer. */ "Custom time" = "Tempo personalizzato"; +/* No comment provided by engineer. */ +"Customizable message shape." = "Forma dei messaggi personalizzabile."; + +/* No comment provided by engineer. */ +"Customize theme" = "Personalizza il tema"; + /* No comment provided by engineer. */ "Dark" = "Scuro"; +/* No comment provided by engineer. */ +"Dark mode colors" = "Colori modalità scura"; + /* No comment provided by engineer. */ "Database downgrade" = "Downgrade del database"; @@ -1092,13 +1543,20 @@ /* time unit */ "days" = "giorni"; +/* No comment provided by engineer. */ +"Debug delivery" = "Debug della consegna"; + /* No comment provided by engineer. */ "Decentralized" = "Decentralizzato"; /* message decrypt error item */ "Decryption error" = "Errore di decifrazione"; -/* pref value */ +/* No comment provided by engineer. */ +"decryption errors" = "errori di decifrazione"; + +/* delete after time +pref value */ "default (%@)" = "predefinito (%@)"; /* No comment provided by engineer. */ @@ -1107,9 +1565,13 @@ /* No comment provided by engineer. */ "default (yes)" = "predefinito (sì)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "Elimina"; +/* No comment provided by engineer. */ +"Delete %lld messages of members?" = "Eliminare %lld messaggi dei membri?"; + /* No comment provided by engineer. */ "Delete %lld messages?" = "Eliminare %lld messaggi?"; @@ -1129,10 +1591,10 @@ "Delete and notify contact" = "Elimina e avvisa il contatto"; /* No comment provided by engineer. */ -"Delete archive" = "Elimina archivio"; +"Delete chat" = "Elimina chat"; /* No comment provided by engineer. */ -"Delete chat archive?" = "Eliminare l'archivio della chat?"; +"Delete chat messages from your device." = "Elimina i messaggi di chat dal tuo dispositivo."; /* No comment provided by engineer. */ "Delete chat profile" = "Elimina il profilo di chat"; @@ -1140,6 +1602,9 @@ /* No comment provided by engineer. */ "Delete chat profile?" = "Eliminare il profilo di chat?"; +/* No comment provided by engineer. */ +"Delete chat?" = "Eliminare la chat?"; + /* No comment provided by engineer. */ "Delete connection" = "Elimina connessione"; @@ -1147,14 +1612,14 @@ "Delete contact" = "Elimina contatto"; /* No comment provided by engineer. */ -"Delete Contact" = "Elimina contatto"; - -/* No comment provided by engineer. */ -"Delete contact?\nThis cannot be undone!" = "Eliminare il contatto?\nNon è reversibile!"; +"Delete contact?" = "Eliminare il contatto?"; /* No comment provided by engineer. */ "Delete database" = "Elimina database"; +/* No comment provided by engineer. */ +"Delete database from this device" = "Elimina il database da questo dispositivo"; + /* server test step */ "Delete file" = "Elimina file"; @@ -1185,13 +1650,16 @@ /* No comment provided by engineer. */ "Delete link?" = "Eliminare il link?"; +/* alert title */ +"Delete list?" = "Eliminare l'elenco?"; + /* No comment provided by engineer. */ "Delete member message?" = "Eliminare il messaggio del membro?"; /* No comment provided by engineer. */ "Delete message?" = "Eliminare il messaggio?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "Elimina messaggi"; /* No comment provided by engineer. */ @@ -1204,7 +1672,7 @@ "Delete old database?" = "Eliminare il database vecchio?"; /* No comment provided by engineer. */ -"Delete pending connection" = "Elimina connessione in attesa"; +"Delete or moderate up to 200 messages." = "Elimina o modera fino a 200 messaggi."; /* No comment provided by engineer. */ "Delete pending connection?" = "Eliminare la connessione in attesa?"; @@ -1215,12 +1683,24 @@ /* server test step */ "Delete queue" = "Elimina coda"; +/* No comment provided by engineer. */ +"Delete report" = "Elimina la segnalazione"; + +/* No comment provided by engineer. */ +"Delete up to 20 messages at once." = "Elimina fino a 20 messaggi contemporaneamente."; + /* No comment provided by engineer. */ "Delete user profile?" = "Eliminare il profilo utente?"; +/* No comment provided by engineer. */ +"Delete without notification" = "Elimina senza avvisare"; + /* deleted chat item */ "deleted" = "eliminato"; +/* No comment provided by engineer. */ +"Deleted" = "Eliminato"; + /* No comment provided by engineer. */ "Deleted at" = "Eliminato il"; @@ -1233,6 +1713,12 @@ /* rcv group event chat item */ "deleted group" = "gruppo eliminato"; +/* No comment provided by engineer. */ +"Deletion errors" = "Errori di eliminazione"; + +/* No comment provided by engineer. */ +"Delivered even when Apple drops them." = "Consegnati anche quando Apple li scarta."; + /* No comment provided by engineer. */ "Delivery" = "Consegna"; @@ -1254,9 +1740,27 @@ /* No comment provided by engineer. */ "Desktop devices" = "Dispositivi desktop"; +/* No comment provided by engineer. */ +"Destination server address of %@ is incompatible with forwarding server %@ settings." = "L'indirizzo del server di destinazione di %@ è incompatibile con le impostazioni del server di inoltro %@."; + +/* snd error text */ +"Destination server error: %@" = "Errore del server di destinazione: %@"; + +/* No comment provided by engineer. */ +"Destination server version of %@ is incompatible with forwarding server %@." = "La versione del server di destinazione di %@ è incompatibile con il server di inoltro %@."; + +/* No comment provided by engineer. */ +"Detailed statistics" = "Statistiche dettagliate"; + +/* No comment provided by engineer. */ +"Details" = "Dettagli"; + /* No comment provided by engineer. */ "Develop" = "Sviluppa"; +/* No comment provided by engineer. */ +"Developer options" = "Opzioni sviluppatore"; + /* No comment provided by engineer. */ "Developer tools" = "Strumenti di sviluppo"; @@ -1282,11 +1786,20 @@ "Direct messages" = "Messaggi diretti"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "I messaggi diretti tra i membri sono vietati in questo gruppo."; +"Direct messages between members are prohibited in this chat." = "I messaggi diretti tra i membri sono vietati in questa chat."; + +/* No comment provided by engineer. */ +"Direct messages between members are prohibited." = "I messaggi diretti tra i membri sono vietati in questo gruppo."; /* No comment provided by engineer. */ "Disable (keep overrides)" = "Disattiva (mantieni sostituzioni)"; +/* alert title */ +"Disable automatic message deletion?" = "Disattivare l'eliminazione automatica dei messaggi?"; + +/* alert button */ +"Disable delete messages" = "Disattiva eliminazione messaggi"; + /* No comment provided by engineer. */ "Disable for all" = "Disattiva per tutti"; @@ -1296,6 +1809,9 @@ /* No comment provided by engineer. */ "disabled" = "disattivato"; +/* No comment provided by engineer. */ +"Disabled" = "Disattivato"; + /* No comment provided by engineer. */ "Disappearing message" = "Messaggio a tempo"; @@ -1306,7 +1822,7 @@ "Disappearing messages are prohibited in this chat." = "I messaggi a tempo sono vietati in questa chat."; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "I messaggi a tempo sono vietati in questo gruppo."; +"Disappearing messages are prohibited." = "I messaggi a tempo sono vietati in questo gruppo."; /* No comment provided by engineer. */ "Disappears at" = "Scompare il"; @@ -1332,36 +1848,85 @@ /* No comment provided by engineer. */ "Do not send history to new members." = "Non inviare la cronologia ai nuovi membri."; +/* No comment provided by engineer. */ +"Do NOT send messages directly, even if your or destination server does not support private routing." = "NON inviare messaggi direttamente, anche se il tuo server o quello di destinazione non supporta l'instradamento privato."; + +/* No comment provided by engineer. */ +"Do not use credentials with proxy." = "Non usare credenziali con proxy."; + +/* No comment provided by engineer. */ +"Do NOT use private routing." = "NON usare l'instradamento privato."; + /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "NON usare SimpleX per chiamate di emergenza."; +/* No comment provided by engineer. */ +"Documents:" = "Documenti:"; + /* No comment provided by engineer. */ "Don't create address" = "Non creare un indirizzo"; /* No comment provided by engineer. */ "Don't enable" = "Non attivare"; +/* No comment provided by engineer. */ +"Don't miss important messages." = "Non perdere messaggi importanti."; + /* No comment provided by engineer. */ "Don't show again" = "Non mostrare più"; +/* No comment provided by engineer. */ +"Done" = "Fatto"; + /* No comment provided by engineer. */ "Downgrade and open chat" = "Esegui downgrade e apri chat"; +/* alert button +chat item action */ +"Download" = "Scarica"; + +/* No comment provided by engineer. */ +"Download errors" = "Errori di scaricamento"; + +/* No comment provided by engineer. */ +"Download failed" = "Scaricamento fallito"; + /* server test step */ "Download file" = "Scarica file"; +/* alert action */ +"Download files" = "Scarica i file"; + +/* No comment provided by engineer. */ +"Downloaded" = "Scaricato"; + +/* No comment provided by engineer. */ +"Downloaded files" = "File scaricati"; + +/* No comment provided by engineer. */ +"Downloading archive" = "Scaricamento archivio"; + +/* No comment provided by engineer. */ +"Downloading link details" = "Scaricamento dettagli del link"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Nome da mostrare doppio!"; /* integrity error chat item */ "duplicate message" = "messaggio duplicato"; +/* No comment provided by engineer. */ +"duplicates" = "doppi"; + /* No comment provided by engineer. */ "Duration" = "Durata"; /* No comment provided by engineer. */ "e2e encrypted" = "crittografato e2e"; +/* No comment provided by engineer. */ +"E2E encrypted notifications." = "Notifiche crittografate E2E."; + /* chat item action */ "Edit" = "Modifica"; @@ -1374,15 +1939,21 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "Attiva (mantieni sostituzioni)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "Attivare l'eliminazione automatica dei messaggi?"; /* No comment provided by engineer. */ "Enable camera access" = "Attiva l'accesso alla fotocamera"; +/* No comment provided by engineer. */ +"Enable Flux in Network & servers settings for better metadata privacy." = "Attiva Flux nelle impostazioni \"Rete e server\" per una migliore privacy dei metadati."; + /* No comment provided by engineer. */ "Enable for all" = "Attiva per tutti"; +/* No comment provided by engineer. */ +"Enable in direct chats (BETA)!" = "Attivala nelle chat dirette (BETA)!"; + /* No comment provided by engineer. */ "Enable instant notifications?" = "Attivare le notifiche istantanee?"; @@ -1410,6 +1981,12 @@ /* enabled status */ "enabled" = "attivato"; +/* No comment provided by engineer. */ +"Enabled" = "Attivato"; + +/* No comment provided by engineer. */ +"Enabled for" = "Attivo per"; + /* enabled status */ "enabled for contact" = "attivato per il contatto"; @@ -1482,6 +2059,9 @@ /* chat item text */ "encryption re-negotiation required for %@" = "richiesta rinegoziazione della crittografia per %@"; +/* No comment provided by engineer. */ +"Encryption renegotiation in progress." = "Rinegoziazione della crittografia in corso."; + /* No comment provided by engineer. */ "ended" = "terminata"; @@ -1497,6 +2077,9 @@ /* No comment provided by engineer. */ "Enter Passcode" = "Inserisci il codice di accesso"; +/* No comment provided by engineer. */ +"Enter passphrase" = "Inserisci password"; + /* No comment provided by engineer. */ "Enter passphrase…" = "Inserisci la password…"; @@ -1527,24 +2110,39 @@ /* No comment provided by engineer. */ "Error aborting address change" = "Errore nell'interruzione del cambio di indirizzo"; +/* alert title */ +"Error accepting conditions" = "Errore di accettazione delle condizioni"; + /* No comment provided by engineer. */ "Error accepting contact request" = "Errore nell'accettazione della richiesta di contatto"; -/* No comment provided by engineer. */ -"Error accessing database file" = "Errore nell'accesso al file del database"; - /* No comment provided by engineer. */ "Error adding member(s)" = "Errore di aggiunta membro/i"; +/* alert title */ +"Error adding server" = "Errore di aggiunta del server"; + /* No comment provided by engineer. */ "Error changing address" = "Errore nella modifica dell'indirizzo"; +/* No comment provided by engineer. */ +"Error changing connection profile" = "Errore nel cambio di profilo di connessione"; + /* No comment provided by engineer. */ "Error changing role" = "Errore nel cambio di ruolo"; /* No comment provided by engineer. */ "Error changing setting" = "Errore nella modifica dell'impostazione"; +/* No comment provided by engineer. */ +"Error changing to incognito!" = "Errore nel passaggio a incognito!"; + +/* No comment provided by engineer. */ +"Error checking token status" = "Errore di controllo dello stato del token"; + +/* No comment provided by engineer. */ +"Error connecting to forwarding server %@. Please try later." = "Errore di connessione al server di inoltro %@. Riprova più tardi."; + /* No comment provided by engineer. */ "Error creating address" = "Errore nella creazione dell'indirizzo"; @@ -1554,6 +2152,9 @@ /* No comment provided by engineer. */ "Error creating group link" = "Errore nella creazione del link del gruppo"; +/* alert title */ +"Error creating list" = "Errore nella creazione dell'elenco"; + /* No comment provided by engineer. */ "Error creating member contact" = "Errore di creazione del contatto"; @@ -1563,6 +2164,9 @@ /* No comment provided by engineer. */ "Error creating profile!" = "Errore nella creazione del profilo!"; +/* No comment provided by engineer. */ +"Error creating report" = "Errore nella creazione del resoconto"; + /* No comment provided by engineer. */ "Error decrypting file" = "Errore decifrando il file"; @@ -1575,9 +2179,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "Errore nell'eliminazione della connessione"; -/* No comment provided by engineer. */ -"Error deleting contact" = "Errore nell'eliminazione del contatto"; - /* No comment provided by engineer. */ "Error deleting database" = "Errore nell'eliminazione del database"; @@ -1590,6 +2191,9 @@ /* No comment provided by engineer. */ "Error deleting user profile" = "Errore nell'eliminazione del profilo utente"; +/* No comment provided by engineer. */ +"Error downloading the archive" = "Errore di scaricamento dell'archivio"; + /* No comment provided by engineer. */ "Error enabling delivery receipts!" = "Errore nell'attivazione delle ricevute di consegna!"; @@ -1602,26 +2206,47 @@ /* No comment provided by engineer. */ "Error exporting chat database" = "Errore nell'esportazione del database della chat"; +/* No comment provided by engineer. */ +"Error exporting theme: %@" = "Errore di esportazione del tema: %@"; + /* No comment provided by engineer. */ "Error importing chat database" = "Errore nell'importazione del database della chat"; /* No comment provided by engineer. */ "Error joining group" = "Errore di ingresso nel gruppo"; +/* alert title */ +"Error loading servers" = "Errore nel caricamento dei server"; + /* No comment provided by engineer. */ -"Error loading %@ servers" = "Errore nel caricamento dei server %@"; +"Error migrating settings" = "Errore nella migrazione delle impostazioni"; /* No comment provided by engineer. */ "Error opening chat" = "Errore di apertura della chat"; -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Errore nella ricezione del file"; +/* No comment provided by engineer. */ +"Error reconnecting server" = "Errore di riconnessione al server"; + +/* No comment provided by engineer. */ +"Error reconnecting servers" = "Errore di riconnessione ai server"; + +/* alert title */ +"Error registering for notifications" = "Errore di registrazione per le notifiche"; + /* No comment provided by engineer. */ "Error removing member" = "Errore nella rimozione del membro"; +/* alert title */ +"Error reordering lists" = "Errore riordinando gli elenchi"; + /* No comment provided by engineer. */ -"Error saving %@ servers" = "Errore nel salvataggio dei server %@"; +"Error resetting statistics" = "Errore di azzeramento statistiche"; + +/* alert title */ +"Error saving chat list" = "Errore nel salvataggio dell'elenco di chat"; /* No comment provided by engineer. */ "Error saving group profile" = "Errore nel salvataggio del profilo del gruppo"; @@ -1635,6 +2260,12 @@ /* No comment provided by engineer. */ "Error saving passphrase to keychain" = "Errore nel salvataggio della password nel portachiavi"; +/* alert title */ +"Error saving servers" = "Errore di salvataggio dei server"; + +/* when migrating */ +"Error saving settings" = "Errore di salvataggio delle impostazioni"; + /* No comment provided by engineer. */ "Error saving user password" = "Errore nel salvataggio della password utente"; @@ -1660,17 +2291,26 @@ "Error stopping chat" = "Errore nell'interruzione della chat"; /* No comment provided by engineer. */ +"Error switching profile" = "Errore nel cambio di profilo"; + +/* alertTitle */ "Error switching profile!" = "Errore nel cambio di profilo!"; /* No comment provided by engineer. */ "Error synchronizing connection" = "Errore nella sincronizzazione della connessione"; +/* No comment provided by engineer. */ +"Error testing server connection" = "Errore provando la connessione al server"; + /* No comment provided by engineer. */ "Error updating group link" = "Errore nell'aggiornamento del link del gruppo"; /* No comment provided by engineer. */ "Error updating message" = "Errore nell'aggiornamento del messaggio"; +/* alert title */ +"Error updating server" = "Errore di aggiornamento del server"; + /* No comment provided by engineer. */ "Error updating settings" = "Errore nell'aggiornamento delle impostazioni"; @@ -1678,9 +2318,17 @@ "Error updating user privacy" = "Errore nell'aggiornamento della privacy dell'utente"; /* No comment provided by engineer. */ -"Error: " = "Errore: "; +"Error uploading the archive" = "Errore di invio dell'archivio"; /* No comment provided by engineer. */ +"Error verifying passphrase:" = "Errore di verifica della password:"; + +/* No comment provided by engineer. */ +"Error: " = "Errore: "; + +/* alert message +file error text +snd error text */ "Error: %@" = "Errore: %@"; /* No comment provided by engineer. */ @@ -1690,10 +2338,13 @@ "Error: URL is invalid" = "Errore: l'URL non è valido"; /* No comment provided by engineer. */ -"Even when disabled in the conversation." = "Anche quando disattivato nella conversazione."; +"Errors" = "Errori"; + +/* servers error */ +"Errors in servers configuration." = "Errori nella configurazione dei server."; /* No comment provided by engineer. */ -"event happened" = "evento accaduto"; +"Even when disabled in the conversation." = "Anche quando disattivato nella conversazione."; /* No comment provided by engineer. */ "Exit without saving" = "Esci senza salvare"; @@ -1701,15 +2352,27 @@ /* chat item action */ "Expand" = "Espandi"; +/* No comment provided by engineer. */ +"expired" = "scaduto"; + +/* token status text */ +"Expired" = "Scaduto"; + /* No comment provided by engineer. */ "Export database" = "Esporta database"; /* No comment provided by engineer. */ "Export error:" = "Errore di esportazione:"; +/* No comment provided by engineer. */ +"Export theme" = "Esporta tema"; + /* No comment provided by engineer. */ "Exported database archive." = "Archivio database esportato."; +/* No comment provided by engineer. */ +"Exported file doesn't exist" = "Il file esportato non esiste"; + /* No comment provided by engineer. */ "Exporting database archive…" = "Esportazione archivio database…"; @@ -1719,12 +2382,42 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "Veloce e senza aspettare che il mittente sia in linea!"; +/* No comment provided by engineer. */ +"Faster deletion of groups." = "Eliminazione dei gruppi più veloce."; + /* No comment provided by engineer. */ "Faster joining and more reliable messages." = "Ingresso più veloce e messaggi più affidabili."; /* No comment provided by engineer. */ +"Faster sending messages." = "Invio dei messaggi più veloce."; + +/* swipe action */ "Favorite" = "Preferito"; +/* No comment provided by engineer. */ +"Favorites" = "Preferite"; + +/* file error alert title */ +"File error" = "Errore del file"; + +/* alert message */ +"File errors:\n%@" = "Errori di file:\n%@"; + +/* file error text */ +"File is blocked by server operator:\n%@." = "Il file è bloccato dall'operatore del server:\n%@."; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "File non trovato - probabilmente è stato eliminato o annullato."; + +/* file error text */ +"File server error: %@" = "Errore del server dei file: %@"; + +/* No comment provided by engineer. */ +"File status" = "Stato del file"; + +/* copied message info */ +"File status: %@" = "Stato del file: %@"; + /* No comment provided by engineer. */ "File will be deleted from servers." = "Il file verrà eliminato dai server."; @@ -1737,6 +2430,9 @@ /* No comment provided by engineer. */ "File: %@" = "File: %@"; +/* No comment provided by engineer. */ +"Files" = "File"; + /* No comment provided by engineer. */ "Files & media" = "File e multimediali"; @@ -1744,7 +2440,10 @@ "Files and media" = "File e multimediali"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "File e contenuti multimediali sono vietati in questo gruppo."; +"Files and media are prohibited." = "File e contenuti multimediali sono vietati in questo gruppo."; + +/* No comment provided by engineer. */ +"Files and media not allowed" = "File e multimediali non consentiti"; /* No comment provided by engineer. */ "Files and media prohibited!" = "File e contenuti multimediali vietati!"; @@ -1752,6 +2451,12 @@ /* No comment provided by engineer. */ "Filter unread and favorite chats." = "Filtra le chat non lette e preferite."; +/* No comment provided by engineer. */ +"Finalize migration" = "Finalizza la migrazione"; + +/* No comment provided by engineer. */ +"Finalize migration on another device." = "Finalizza la migrazione su un altro dispositivo."; + /* No comment provided by engineer. */ "Finally, we have them! 🚀" = "Finalmente le abbiamo! 🚀"; @@ -1776,9 +2481,72 @@ /* No comment provided by engineer. */ "Fix not supported by group member" = "Correzione non supportata dal membro del gruppo"; +/* No comment provided by engineer. */ +"For all moderators" = "Per tutti i moderatori"; + +/* servers error */ +"For chat profile %@:" = "Per il profilo di chat %@:"; + /* No comment provided by engineer. */ "For console" = "Per console"; +/* No comment provided by engineer. */ +"For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server." = "Ad esempio, se il tuo contatto riceve messaggi tramite un server di SimpleX Chat, la tua app li consegnerà tramite un server Flux."; + +/* No comment provided by engineer. */ +"For me" = "Per me"; + +/* No comment provided by engineer. */ +"For private routing" = "Per l'instradamento privato"; + +/* No comment provided by engineer. */ +"For social media" = "Per i social media"; + +/* chat item action */ +"Forward" = "Inoltra"; + +/* alert title */ +"Forward %d message(s)?" = "Inoltrare %d messaggio/i?"; + +/* No comment provided by engineer. */ +"Forward and save messages" = "Inoltra e salva i messaggi"; + +/* alert action */ +"Forward messages" = "Inoltra i messaggi"; + +/* alert message */ +"Forward messages without files?" = "Inoltrare i messaggi senza file?"; + +/* No comment provided by engineer. */ +"Forward up to 20 messages at once." = "Inoltra fino a 20 messaggi alla volta."; + +/* No comment provided by engineer. */ +"forwarded" = "inoltrato"; + +/* No comment provided by engineer. */ +"Forwarded" = "Inoltrato"; + +/* No comment provided by engineer. */ +"Forwarded from" = "Inoltrato da"; + +/* No comment provided by engineer. */ +"Forwarding %lld messages" = "Inoltro di %lld messaggi"; + +/* No comment provided by engineer. */ +"Forwarding server %@ failed to connect to destination server %@. Please try later." = "Il server di inoltro %@ non è riuscito a connettersi al server di destinazione %@. Riprova più tardi."; + +/* No comment provided by engineer. */ +"Forwarding server address is incompatible with network settings: %@." = "L'indirizzo del server di inoltro è incompatibile con le impostazioni di rete: %@."; + +/* No comment provided by engineer. */ +"Forwarding server version is incompatible with network settings: %@." = "La versione del server di inoltro è incompatibile con le impostazioni di rete: %@."; + +/* snd error text */ +"Forwarding server: %@\nDestination server error: %@" = "Server di inoltro: %1$@\nErrore del server di destinazione: %2$@"; + +/* snd error text */ +"Forwarding server: %@\nError: %@" = "Server di inoltro: %1$@\nErrore: %2$@"; + /* No comment provided by engineer. */ "Found desktop" = "Desktop trovato"; @@ -1791,9 +2559,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Nome completo (facoltativo)"; -/* No comment provided by engineer. */ -"Full name:" = "Nome completo:"; - /* No comment provided by engineer. */ "Fully decentralized – visible only to members." = "Completamente decentralizzato: visibile solo ai membri."; @@ -1803,9 +2568,18 @@ /* No comment provided by engineer. */ "Further reduced battery usage" = "Ulteriore riduzione del consumo della batteria"; +/* No comment provided by engineer. */ +"Get notified when mentioned." = "Ricevi una notifica quando menzionato."; + /* No comment provided by engineer. */ "GIFs and stickers" = "GIF e adesivi"; +/* message preview */ +"Good afternoon!" = "Buon pomeriggio!"; + +/* message preview */ +"Good morning!" = "Buongiorno!"; + /* No comment provided by engineer. */ "Group" = "Gruppo"; @@ -1842,24 +2616,6 @@ /* No comment provided by engineer. */ "Group links" = "Link del gruppo"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "I membri del gruppo possono aggiungere reazioni ai messaggi."; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "I membri del gruppo possono eliminare irreversibilmente i messaggi inviati. (24 ore)"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "I membri del gruppo possono inviare messaggi diretti."; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "I membri del gruppo possono inviare messaggi a tempo."; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "I membri del gruppo possono inviare file e contenuti multimediali."; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "I membri del gruppo possono inviare messaggi vocali."; - /* notification */ "Group message:" = "Messaggio del gruppo:"; @@ -1887,9 +2643,15 @@ /* No comment provided by engineer. */ "Group will be deleted for you - this cannot be undone!" = "Il gruppo verrà eliminato per te. Non è reversibile!"; +/* No comment provided by engineer. */ +"Groups" = "Gruppi"; + /* No comment provided by engineer. */ "Help" = "Aiuto"; +/* No comment provided by engineer. */ +"Help admins moderating their groups." = "Aiuta gli amministratori a moderare i loro gruppi."; + /* No comment provided by engineer. */ "Hidden" = "Nascosta"; @@ -1921,6 +2683,12 @@ "hours" = "ore"; /* No comment provided by engineer. */ +"How it affects privacy" = "Come influisce sulla privacy"; + +/* No comment provided by engineer. */ +"How it helps privacy" = "Come aiuta la privacy"; + +/* alert button */ "How it works" = "Come funziona"; /* No comment provided by engineer. */ @@ -1935,6 +2703,9 @@ /* No comment provided by engineer. */ "How to use your servers" = "Come usare i tuoi server"; +/* No comment provided by engineer. */ +"Hungarian interface" = "Interfaccia in ungherese"; + /* No comment provided by engineer. */ "ICE servers (one per line)" = "Server ICE (uno per riga)"; @@ -1963,7 +2734,7 @@ "Immediately" = "Immediatamente"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "Immune a spam e abusi"; +"Immune to spam" = "Immune a spam e abusi"; /* No comment provided by engineer. */ "Import" = "Importa"; @@ -1974,6 +2745,18 @@ /* No comment provided by engineer. */ "Import database" = "Importa database"; +/* No comment provided by engineer. */ +"Import failed" = "Importazione fallita"; + +/* No comment provided by engineer. */ +"Import theme" = "Importa tema"; + +/* No comment provided by engineer. */ +"Importing archive" = "Importazione archivio"; + +/* No comment provided by engineer. */ +"Improved delivery, reduced traffic usage.\nMore improvements are coming soon!" = "Consegna migliorata, utilizzo di traffico ridotto.\nAltri miglioramenti sono in arrivo!"; + /* No comment provided by engineer. */ "Improved message delivery" = "Consegna dei messaggi migliorata"; @@ -1983,9 +2766,24 @@ /* No comment provided by engineer. */ "Improved server configuration" = "Configurazione del server migliorata"; +/* No comment provided by engineer. */ +"In order to continue, chat should be stopped." = "Per continuare, la chat deve essere fermata."; + /* No comment provided by engineer. */ "In reply to" = "In risposta a"; +/* No comment provided by engineer. */ +"In-call sounds" = "Suoni nelle chiamate"; + +/* No comment provided by engineer. */ +"inactive" = "inattivo"; + +/* report reason */ +"Inappropriate content" = "Contenuto inappropriato"; + +/* report reason */ +"Inappropriate profile" = "Profilo inappropriato"; + /* No comment provided by engineer. */ "Incognito" = "Incognito"; @@ -2040,14 +2838,32 @@ /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Installa [Simplex Chat per terminale](https://github.com/simplex-chat/simplex-chat)"; +/* No comment provided by engineer. */ +"Instant" = "Istantaneamente"; + /* No comment provided by engineer. */ "Instant push notifications will be hidden!\n" = "Le notifiche push istantanee saranno nascoste!\n"; /* No comment provided by engineer. */ -"Instantly" = "Istantaneamente"; +"Interface" = "Interfaccia"; /* No comment provided by engineer. */ -"Interface" = "Interfaccia"; +"Interface colors" = "Colori dell'interfaccia"; + +/* token status text */ +"Invalid" = "Non valido"; + +/* token status text */ +"Invalid (bad token)" = "Non valido (token corrotto)"; + +/* token status text */ +"Invalid (expired)" = "Non valido (scaduto)"; + +/* token status text */ +"Invalid (unregistered)" = "Non valido (non registrato)"; + +/* token status text */ +"Invalid (wrong topic)" = "Non valido (argomento sbagliato)"; /* invalid chat data */ "invalid chat" = "chat non valida"; @@ -2067,6 +2883,9 @@ /* No comment provided by engineer. */ "Invalid link" = "Link non valido"; +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Conferma di migrazione non valida"; + /* No comment provided by engineer. */ "Invalid name!" = "Nome non valido!"; @@ -2076,7 +2895,7 @@ /* No comment provided by engineer. */ "Invalid response" = "Risposta non valida"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "Indirizzo del server non valido!"; /* item status text */ @@ -2088,12 +2907,18 @@ /* group name */ "invitation to group %@" = "invito al gruppo %@"; +/* No comment provided by engineer. */ +"invite" = "invita"; + /* No comment provided by engineer. */ "Invite friends" = "Invita amici"; /* No comment provided by engineer. */ "Invite members" = "Invita membri"; +/* No comment provided by engineer. */ +"Invite to chat" = "Invita in chat"; + /* No comment provided by engineer. */ "Invite to group" = "Invita al gruppo"; @@ -2115,6 +2940,9 @@ /* No comment provided by engineer. */ "iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "Il portachiavi di iOS verrà usato per archiviare in modo sicuro la password dopo il riavvio dell'app o la modifica della password; consentirà di ricevere notifiche push."; +/* No comment provided by engineer. */ +"IP address" = "Indirizzo IP"; + /* No comment provided by engineer. */ "Irreversible message deletion" = "Eliminazione irreversibile del messaggio"; @@ -2122,7 +2950,7 @@ "Irreversible message deletion is prohibited in this chat." = "L'eliminazione irreversibile dei messaggi è vietata in questa chat."; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "L'eliminazione irreversibile dei messaggi è vietata in questo gruppo."; +"Irreversible message deletion is prohibited." = "L'eliminazione irreversibile dei messaggi è vietata in questo gruppo."; /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "Permette di avere molte connessioni anonime senza dati condivisi tra di loro in un unico profilo di chat."; @@ -2133,6 +2961,9 @@ /* No comment provided by engineer. */ "It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Può accadere quando:\n1. I messaggi sono scaduti sul client mittente dopo 2 giorni o sul server dopo 30 giorni.\n2. La decifrazione del messaggio è fallita, perché tu o il tuo contatto avete usato un backup del database vecchio.\n3. La connessione è stata compromessa."; +/* No comment provided by engineer. */ +"It protects your IP address and connections." = "Protegge il tuo indirizzo IP e le connessioni."; + /* No comment provided by engineer. */ "It seems like you are already connected via this link. If it is not the case, there was an error (%@)." = "Sembra che tu sia già connesso tramite questo link. In caso contrario, c'è stato un errore (%@)."; @@ -2145,7 +2976,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "Interfaccia giapponese"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "Entra"; /* No comment provided by engineer. */ @@ -2172,13 +3003,16 @@ /* No comment provided by engineer. */ "Joining group" = "Ingresso nel gruppo"; -/* No comment provided by engineer. */ +/* alert action */ "Keep" = "Tieni"; +/* No comment provided by engineer. */ +"Keep conversation" = "Tieni la conversazione"; + /* No comment provided by engineer. */ "Keep the app open to use it from desktop" = "Tieni aperta l'app per usarla dal desktop"; -/* No comment provided by engineer. */ +/* alert title */ "Keep unused invitation?" = "Tenere l'invito inutilizzato?"; /* No comment provided by engineer. */ @@ -2196,9 +3030,15 @@ /* No comment provided by engineer. */ "Learn more" = "Maggiori informazioni"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "Esci"; +/* No comment provided by engineer. */ +"Leave chat" = "Esci dalla chat"; + +/* No comment provided by engineer. */ +"Leave chat?" = "Uscire dalla chat?"; + /* No comment provided by engineer. */ "Leave group" = "Esci dal gruppo"; @@ -2226,6 +3066,15 @@ /* No comment provided by engineer. */ "Linked desktops" = "Desktop collegati"; +/* swipe action */ +"List" = "Elenco"; + +/* No comment provided by engineer. */ +"List name and emoji should be different for all lists." = "Il nome dell'elenco e l'emoji dovrebbero essere diversi per tutte le liste."; + +/* No comment provided by engineer. */ +"List name..." = "Nome elenco..."; + /* No comment provided by engineer. */ "LIVE" = "IN DIRETTA"; @@ -2235,9 +3084,6 @@ /* No comment provided by engineer. */ "Live messages" = "Messaggi in diretta"; -/* No comment provided by engineer. */ -"Local" = "Locale"; - /* No comment provided by engineer. */ "Local name" = "Nome locale"; @@ -2250,24 +3096,15 @@ /* No comment provided by engineer. */ "Lock mode" = "Modalità di blocco"; -/* No comment provided by engineer. */ -"Make a private connection" = "Crea una connessione privata"; - /* No comment provided by engineer. */ "Make one message disappear" = "Fai sparire un messaggio"; /* No comment provided by engineer. */ "Make profile private!" = "Rendi privato il profilo!"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Assicurati che gli indirizzi dei server %@ siano nel formato corretto, uno per riga e non doppi (%@)."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Assicurati che gli indirizzi dei server WebRTC ICE siano nel formato corretto, uno per riga e non doppi."; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Molte persone hanno chiesto: *se SimpleX non ha identificatori utente, come può recapitare i messaggi?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "Contrassegna eliminato per tutti"; @@ -2286,6 +3123,12 @@ /* No comment provided by engineer. */ "Max 30 seconds, received instantly." = "Max 30 secondi, ricevuto istantaneamente."; +/* No comment provided by engineer. */ +"Media & file servers" = "Server di multimediali e file"; + +/* blur media */ +"Medium" = "Media"; + /* member role */ "member" = "membro"; @@ -2293,28 +3136,85 @@ "Member" = "Membro"; /* profile update event chat item */ -"member %@ changed to %@" = "membro %1$@ cambiato in %2$@"; +"member %@ changed to %@" = "il membro %1$@ è diventato %2$@"; /* rcv group event chat item */ "member connected" = "si è connesso/a"; +/* item status text */ +"Member inactive" = "Membro inattivo"; + +/* chat feature */ +"Member reports" = "Segnalazioni dei membri"; + +/* No comment provided by engineer. */ +"Member role will be changed to \"%@\". All chat members will be notified." = "Il ruolo del membro verrà cambiato in \"%@\". Verranno notificati tutti i membri della chat."; + /* No comment provided by engineer. */ "Member role will be changed to \"%@\". All group members will be notified." = "Il ruolo del membro verrà cambiato in \"%@\". Tutti i membri del gruppo verranno avvisati."; /* No comment provided by engineer. */ "Member role will be changed to \"%@\". The member will receive a new invitation." = "Il ruolo del membro verrà cambiato in \"%@\". Il membro riceverà un invito nuovo."; +/* No comment provided by engineer. */ +"Member will be removed from chat - this cannot be undone!" = "Il membro verrà rimosso dalla chat, non è reversibile!"; + /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "Il membro verrà rimosso dal gruppo, non è reversibile!"; +/* No comment provided by engineer. */ +"Members can add message reactions." = "I membri del gruppo possono aggiungere reazioni ai messaggi."; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "I membri del gruppo possono eliminare irreversibilmente i messaggi inviati. (24 ore)"; + +/* No comment provided by engineer. */ +"Members can report messsages to moderators." = "I membri possono segnalare messaggi ai moderatori."; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "I membri del gruppo possono inviare messaggi diretti."; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "I membri del gruppo possono inviare messaggi a tempo."; + +/* No comment provided by engineer. */ +"Members can send files and media." = "I membri del gruppo possono inviare file e contenuti multimediali."; + +/* No comment provided by engineer. */ +"Members can send SimpleX links." = "I membri del gruppo possono inviare link di Simplex."; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "I membri del gruppo possono inviare messaggi vocali."; + +/* No comment provided by engineer. */ +"Mention members 👋" = "Menziona i membri 👋"; + +/* No comment provided by engineer. */ +"Menus" = "Menu"; + +/* No comment provided by engineer. */ +"message" = "messaggio"; + /* item status text */ "Message delivery error" = "Errore di recapito del messaggio"; /* No comment provided by engineer. */ "Message delivery receipts!" = "Ricevute di consegna dei messaggi!"; +/* item status text */ +"Message delivery warning" = "Avviso di consegna del messaggio"; + /* No comment provided by engineer. */ -"Message draft" = "Bozza dei messaggi"; +"Message draft" = "Bozza del messaggio"; + +/* item status text */ +"Message forwarded" = "Messaggio inoltrato"; + +/* item status description */ +"Message may be delivered later if member becomes active." = "Il messaggio può essere consegnato più tardi se il membro diventa attivo."; + +/* No comment provided by engineer. */ +"Message queue info" = "Info coda messaggi"; /* chat feature */ "Message reactions" = "Reazioni ai messaggi"; @@ -2323,14 +3223,35 @@ "Message reactions are prohibited in this chat." = "Le reazioni ai messaggi sono vietate in questa chat."; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "Le reazioni ai messaggi sono vietate in questo gruppo."; +"Message reactions are prohibited." = "Le reazioni ai messaggi sono vietate in questo gruppo."; /* notification */ "message received" = "messaggio ricevuto"; +/* No comment provided by engineer. */ +"Message reception" = "Ricezione messaggi"; + +/* No comment provided by engineer. */ +"Message servers" = "Server dei messaggi"; + +/* No comment provided by engineer. */ +"Message shape" = "Forma del messaggio"; + +/* No comment provided by engineer. */ +"Message source remains private." = "La fonte del messaggio resta privata."; + +/* No comment provided by engineer. */ +"Message status" = "Stato del messaggio"; + +/* copied message info */ +"Message status: %@" = "Stato del messaggio: %@"; + /* No comment provided by engineer. */ "Message text" = "Testo del messaggio"; +/* No comment provided by engineer. */ +"Message too large" = "Messaggio troppo grande"; + /* No comment provided by engineer. */ "Messages" = "Messaggi"; @@ -2340,9 +3261,48 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "I messaggi da %@ verranno mostrati!"; +/* alert message */ +"Messages in this chat will never be deleted." = "I messaggi in questa chat non verranno mai eliminati."; + +/* No comment provided by engineer. */ +"Messages received" = "Messaggi ricevuti"; + +/* No comment provided by engineer. */ +"Messages sent" = "Messaggi inviati"; + +/* alert message */ +"Messages were deleted after you selected them." = "I messaggi sono stati eliminati dopo che li hai selezionati."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "I messaggi, i file e le chiamate sono protetti da **crittografia end-to-end** con perfect forward secrecy, ripudio e recupero da intrusione."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "I messaggi, i file e le chiamate sono protetti da **crittografia e2e resistente alla quantistica** con perfect forward secrecy, ripudio e recupero da intrusione."; + +/* No comment provided by engineer. */ +"Migrate device" = "Migra dispositivo"; + +/* No comment provided by engineer. */ +"Migrate from another device" = "Migra da un altro dispositivo"; + +/* No comment provided by engineer. */ +"Migrate here" = "Migra qui"; + +/* No comment provided by engineer. */ +"Migrate to another device" = "Migra ad un altro dispositivo"; + +/* No comment provided by engineer. */ +"Migrate to another device via QR code." = "Migra ad un altro dispositivo via codice QR."; + +/* No comment provided by engineer. */ +"Migrating" = "Migrazione"; + /* No comment provided by engineer. */ "Migrating database archive…" = "Migrazione archivio del database…"; +/* No comment provided by engineer. */ +"Migration complete" = "Migrazione completata"; + /* No comment provided by engineer. */ "Migration error:" = "Errore di migrazione:"; @@ -2353,7 +3313,7 @@ "Migration is completed" = "La migrazione è completata"; /* No comment provided by engineer. */ -"Migrations: %@" = "Migrazioni: %@"; +"Migrations:" = "Migrazioni:"; /* time unit */ "minutes" = "minuti"; @@ -2376,63 +3336,99 @@ /* marked deleted chat item preview text */ "moderated by %@" = "moderato da %@"; +/* member role */ +"moderator" = "moderatore"; + /* time unit */ "months" = "mesi"; +/* swipe action */ +"More" = "Altro"; + /* No comment provided by engineer. */ "More improvements are coming soon!" = "Altri miglioramenti sono in arrivo!"; +/* No comment provided by engineer. */ +"More reliable network connection." = "Connessione di rete più affidabile."; + +/* No comment provided by engineer. */ +"More reliable notifications" = "Notifiche più affidabili"; + /* item status description */ "Most likely this connection is deleted." = "Probabilmente questa connessione è stata eliminata."; -/* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "Probabilmente questo contatto ha eliminato la connessione con te."; - /* No comment provided by engineer. */ "Multiple chat profiles" = "Profili di chat multipli"; -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "Silenzia"; +/* notification label action */ +"Mute all" = "Silenzia tutto"; + /* No comment provided by engineer. */ "Muted when inactive!" = "Silenzioso quando inattivo!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "Nome"; /* No comment provided by engineer. */ "Network & servers" = "Rete e server"; +/* No comment provided by engineer. */ +"Network connection" = "Connessione di rete"; + +/* No comment provided by engineer. */ +"Network decentralization" = "Decentralizzazione della rete"; + +/* snd error text */ +"Network issues - message expired after many attempts to send it." = "Problemi di rete - messaggio scaduto dopo molti tentativi di inviarlo."; + +/* No comment provided by engineer. */ +"Network management" = "Gestione della rete"; + +/* No comment provided by engineer. */ +"Network operator" = "Operatore di rete"; + /* No comment provided by engineer. */ "Network settings" = "Impostazioni di rete"; /* No comment provided by engineer. */ "Network status" = "Stato della rete"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "mai"; +/* token status text */ +"New" = "Nuovo"; + /* No comment provided by engineer. */ "New chat" = "Nuova chat"; +/* No comment provided by engineer. */ +"New chat experience 🎉" = "Una nuova esperienza di chat 🎉"; + /* notification */ "New contact request" = "Nuova richiesta di contatto"; /* notification */ "New contact:" = "Nuovo contatto:"; -/* No comment provided by engineer. */ -"New database archive" = "Nuovo archivio database"; - /* No comment provided by engineer. */ "New desktop app!" = "Nuova app desktop!"; /* No comment provided by engineer. */ "New display name" = "Nuovo nome da mostrare"; +/* notification */ +"New events" = "Nuovi eventi"; + /* No comment provided by engineer. */ "New in %@" = "Novità nella %@"; +/* No comment provided by engineer. */ +"New media options" = "Nuove opzioni multimediali"; + /* No comment provided by engineer. */ "New member role" = "Nuovo ruolo del membro"; @@ -2448,6 +3444,15 @@ /* No comment provided by engineer. */ "New passphrase…" = "Nuova password…"; +/* No comment provided by engineer. */ +"New server" = "Nuovo server"; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used every time you start the app." = "Le nuove credenziali SOCKS verranno usate ogni volta che avvii l'app."; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used for each server." = "Le nuove credenziali SOCKS verranno usate per ogni server."; + /* pref value */ "no" = "no"; @@ -2457,6 +3462,15 @@ /* Authentication unavailable */ "No app password" = "Nessuna password dell'app"; +/* No comment provided by engineer. */ +"No chats" = "Nessuna chat"; + +/* No comment provided by engineer. */ +"No chats found" = "Nessuna chat trovata"; + +/* No comment provided by engineer. */ +"No chats in list %@" = "Nessuna chat nell'elenco %@"; + /* No comment provided by engineer. */ "No contacts selected" = "Nessun contatto selezionato"; @@ -2469,6 +3483,9 @@ /* No comment provided by engineer. */ "No device token!" = "Nessun token del dispositivo!"; +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Ancora nessuna connessione diretta, il messaggio viene inoltrato dall'amministratore."; + /* No comment provided by engineer. */ "no e2e encryption" = "nessuna crittografia e2e"; @@ -2481,24 +3498,87 @@ /* No comment provided by engineer. */ "No history" = "Nessuna cronologia"; +/* No comment provided by engineer. */ +"No info, try to reload" = "Nessuna informazione, prova a ricaricare"; + +/* servers error */ +"No media & file servers." = "Nessun server di multimediali e file."; + +/* No comment provided by engineer. */ +"No message" = "Nessun messaggio"; + +/* servers error */ +"No message servers." = "Nessun server dei messaggi."; + +/* No comment provided by engineer. */ +"No network connection" = "Nessuna connessione di rete"; + +/* No comment provided by engineer. */ +"No permission to record speech" = "Nessuna autorizzazione per registrare l'audio"; + +/* No comment provided by engineer. */ +"No permission to record video" = "Nessuna autorizzazione per registrare il video"; + /* No comment provided by engineer. */ "No permission to record voice message" = "Nessuna autorizzazione per registrare messaggi vocali"; +/* No comment provided by engineer. */ +"No push server" = "Locale"; + /* No comment provided by engineer. */ "No received or sent files" = "Nessun file ricevuto o inviato"; +/* servers error */ +"No servers for private message routing." = "Nessun server per l'instradamento dei messaggi privati."; + +/* servers error */ +"No servers to receive files." = "Nessun server per ricevere file."; + +/* servers error */ +"No servers to receive messages." = "Nessun server per ricevere messaggi."; + +/* servers error */ +"No servers to send files." = "Nessun server per inviare file."; + /* copied message info in history */ "no text" = "nessun testo"; +/* alert title */ +"No token!" = "Nessun token!"; + +/* No comment provided by engineer. */ +"No unread chats" = "Nessuna chat non letta"; + +/* No comment provided by engineer. */ +"No user identifiers." = "Nessun identificatore utente."; + /* No comment provided by engineer. */ "Not compatible!" = "Non compatibile!"; +/* No comment provided by engineer. */ +"Notes" = "Note"; + +/* No comment provided by engineer. */ +"Nothing selected" = "Nessuna selezione"; + +/* alert title */ +"Nothing to forward!" = "Niente da inoltrare!"; + /* No comment provided by engineer. */ "Notifications" = "Notifiche"; /* No comment provided by engineer. */ "Notifications are disabled!" = "Le notifiche sono disattivate!"; +/* alert title */ +"Notifications error" = "Errore delle notifiche"; + +/* No comment provided by engineer. */ +"Notifications privacy" = "Privacy delle notifiche"; + +/* alert title */ +"Notifications status" = "Stato delle notifiche"; + /* No comment provided by engineer. */ "Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Ora gli amministratori possono:\n- eliminare i messaggi dei membri.\n- disattivare i membri (ruolo \"osservatore\")"; @@ -2506,11 +3586,11 @@ "observer" = "osservatore"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "off"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Off"; /* feature offered item */ @@ -2519,7 +3599,7 @@ /* feature offered item */ "offered %@: %@" = "offerto %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ @@ -2528,9 +3608,6 @@ /* No comment provided by engineer. */ "Old database" = "Database vecchio"; -/* No comment provided by engineer. */ -"Old database archive" = "Vecchio archivio del database"; - /* group pref value */ "on" = "on"; @@ -2538,16 +3615,22 @@ "One-time invitation link" = "Link di invito una tantum"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "Gli host Onion saranno necessari per la connessione. Richiede l'attivazione della VPN."; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "Gli host Onion saranno **necessari** per la connessione.\nRichiede l'attivazione della VPN."; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "Gli host Onion verranno usati quando disponibili. Richiede l'attivazione della VPN."; +"Onion hosts will be used when available.\nRequires compatible VPN." = "Gli host Onion verranno usati quando disponibili.\nRichiede l'attivazione della VPN."; /* No comment provided by engineer. */ "Onion hosts will not be used." = "Gli host Onion non verranno usati."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Solo i dispositivi client archiviano profili utente, i contatti, i gruppi e i messaggi inviati con la **crittografia end-to-end a 2 livelli**."; +"Only chat owners can change preferences." = "Solo i proprietari della chat possono modificarne le preferenze."; + +/* No comment provided by engineer. */ +"Only client devices store user profiles, contacts, groups, and messages." = "Solo i dispositivi client archiviano profili utente, i contatti, i gruppi e i messaggi inviati con la **crittografia end-to-end a 2 livelli**."; + +/* No comment provided by engineer. */ +"Only delete conversation" = "Elimina solo la conversazione"; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Solo i proprietari del gruppo possono modificarne le preferenze."; @@ -2558,6 +3641,12 @@ /* No comment provided by engineer. */ "Only group owners can enable voice messages." = "Solo i proprietari del gruppo possono attivare i messaggi vocali."; +/* No comment provided by engineer. */ +"Only sender and moderators see it" = "Solo il mittente e i moderatori lo vedono"; + +/* No comment provided by engineer. */ +"Only you and moderators see it" = "Solo tu e i moderatori lo vedete"; + /* No comment provided by engineer. */ "Only you can add message reactions." = "Solo tu puoi aggiungere reazioni ai messaggi."; @@ -2588,39 +3677,78 @@ /* No comment provided by engineer. */ "Only your contact can send voice messages." = "Solo il tuo contatto può inviare messaggi vocali."; -/* No comment provided by engineer. */ +/* alert action */ "Open" = "Apri"; +/* No comment provided by engineer. */ +"Open changes" = "Apri le modifiche"; + /* No comment provided by engineer. */ "Open chat" = "Apri chat"; /* authentication reason */ "Open chat console" = "Apri la console della chat"; +/* No comment provided by engineer. */ +"Open conditions" = "Apri le condizioni"; + /* No comment provided by engineer. */ "Open group" = "Apri gruppo"; +/* authentication reason */ +"Open migration to another device" = "Apri migrazione ad un altro dispositivo"; + /* No comment provided by engineer. */ "Open Settings" = "Apri le impostazioni"; -/* authentication reason */ -"Open user profiles" = "Apri i profili utente"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "Protocollo e codice open source: chiunque può gestire i server."; - /* No comment provided by engineer. */ "Opening app…" = "Apertura dell'app…"; +/* No comment provided by engineer. */ +"Operator" = "Operatore"; + +/* alert title */ +"Operator server" = "Server dell'operatore"; + +/* No comment provided by engineer. */ +"Or import archive file" = "O importa file archivio"; + +/* No comment provided by engineer. */ +"Or paste archive link" = "O incolla il link dell'archivio"; + /* No comment provided by engineer. */ "Or scan QR code" = "O scansiona il codice QR"; +/* No comment provided by engineer. */ +"Or securely share this file link" = "O condividi in modo sicuro questo link del file"; + /* No comment provided by engineer. */ "Or show this code" = "O mostra questo codice"; +/* No comment provided by engineer. */ +"Or to share privately" = "O per condividere in modo privato"; + +/* No comment provided by engineer. */ +"Organize chats into lists" = "Organizza le chat in elenchi"; + +/* No comment provided by engineer. */ +"other" = "altro"; + +/* No comment provided by engineer. */ +"Other" = "Altro"; + +/* No comment provided by engineer. */ +"other errors" = "altri errori"; + +/* alert message */ +"Other file errors:\n%@" = "Altri errori di file:\n%@"; + /* member role */ "owner" = "proprietario"; +/* feature role */ +"owners" = "proprietari"; + /* No comment provided by engineer. */ "Passcode" = "Codice di accesso"; @@ -2636,6 +3764,9 @@ /* No comment provided by engineer. */ "Passcode set!" = "Codice di accesso impostato!"; +/* No comment provided by engineer. */ +"Password" = "Password"; + /* No comment provided by engineer. */ "Password to show" = "Password per mostrare"; @@ -2658,23 +3789,41 @@ "peer-to-peer" = "peer-to-peer"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "Le persone possono connettersi a te solo tramite i link che condividi."; +"pending" = "in attesa"; /* No comment provided by engineer. */ -"Periodically" = "Periodicamente"; +"Pending" = "In attesa"; + +/* No comment provided by engineer. */ +"pending approval" = "in attesa di approvazione"; + +/* No comment provided by engineer. */ +"Periodic" = "Periodicamente"; /* message decrypt error item */ "Permanent decryption error" = "Errore di decifrazione"; +/* No comment provided by engineer. */ +"Picture-in-picture calls" = "Chiamate picture-in-picture"; + /* No comment provided by engineer. */ "PING count" = "Conteggio PING"; /* No comment provided by engineer. */ "PING interval" = "Intervallo PING"; +/* No comment provided by engineer. */ +"Play from the chat list." = "Riproduci dall'elenco delle chat."; + +/* No comment provided by engineer. */ +"Please ask your contact to enable calls." = "Chiedi al contatto di attivare le chiamate."; + /* No comment provided by engineer. */ "Please ask your contact to enable sending voice messages." = "Chiedi al tuo contatto di attivare l'invio dei messaggi vocali."; +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Controlla che mobile e desktop siano collegati alla stessa rete locale e che il firewall del desktop consenta la connessione.\nSi prega di condividere qualsiasi altro problema con gli sviluppatori."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Controlla di aver usato il link giusto o chiedi al tuo contatto di inviartene un altro."; @@ -2684,6 +3833,9 @@ /* No comment provided by engineer. */ "Please check yours and your contact preferences." = "Controlla le preferenze tue e del tuo contatto."; +/* No comment provided by engineer. */ +"Please confirm that network settings are correct for this device." = "Conferma che le impostazioni di rete sono corrette per questo dispositivo."; + /* No comment provided by engineer. */ "Please contact developers.\nError: %@" = "Contatta gli sviluppatori.\nErrore: %@"; @@ -2711,9 +3863,21 @@ /* No comment provided by engineer. */ "Please store passphrase securely, you will NOT be able to change it if you lose it." = "Conserva la password in modo sicuro, NON potrai cambiarla se la perdi."; +/* token info */ +"Please try to disable and re-enable notfications." = "Prova a disattivare e riattivare le notifiche."; + +/* token info */ +"Please wait for token activation to complete." = "Attendi il completamento dell'attivazione del token."; + +/* token info */ +"Please wait for token to be registered." = "Attendi la registrazione del token."; + /* No comment provided by engineer. */ "Polish interface" = "Interfaccia polacca"; +/* No comment provided by engineer. */ +"Port" = "Porta"; + /* server test error */ "Possibly, certificate fingerprint in server address is incorrect" = "Probabilmente l'impronta del certificato nell'indirizzo del server è sbagliata"; @@ -2721,26 +3885,53 @@ "Preserve the last message draft, with attachments." = "Conserva la bozza dell'ultimo messaggio, con gli allegati."; /* No comment provided by engineer. */ -"Preset server" = "Server preimpostato"; +"Preset server address" = "Indirizzo server preimpostato"; /* No comment provided by engineer. */ -"Preset server address" = "Indirizzo server preimpostato"; +"Preset servers" = "Server preimpostati"; /* No comment provided by engineer. */ "Preview" = "Anteprima"; +/* No comment provided by engineer. */ +"Previously connected servers" = "Server precedentemente connessi"; + /* No comment provided by engineer. */ "Privacy & security" = "Privacy e sicurezza"; +/* No comment provided by engineer. */ +"Privacy for your customers." = "Privacy per i tuoi clienti."; + +/* No comment provided by engineer. */ +"Privacy policy and conditions of use." = "Informativa sulla privacy e condizioni d'uso."; + /* No comment provided by engineer. */ "Privacy redefined" = "Privacy ridefinita"; +/* No comment provided by engineer. */ +"Private chats, groups and your contacts are not accessible to server operators." = "Le chat private, i gruppi e i tuoi contatti non sono accessibili agli operatori dei server."; + /* No comment provided by engineer. */ "Private filenames" = "Nomi di file privati"; +/* No comment provided by engineer. */ +"Private media file names." = "Nomi privati dei file multimediali."; + +/* No comment provided by engineer. */ +"Private message routing" = "Instradamento privato dei messaggi"; + +/* No comment provided by engineer. */ +"Private message routing 🚀" = "Instradamento privato dei messaggi 🚀"; + /* name of notes to self */ "Private notes" = "Note private"; +/* No comment provided by engineer. */ +"Private routing" = "Instradamento privato"; + +/* No comment provided by engineer. */ +"Private routing error" = "Errore di instradamento privato"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profilo e connessioni al server"; @@ -2748,15 +3939,15 @@ "Profile image" = "Immagine del profilo"; /* No comment provided by engineer. */ -"Profile name" = "Nome del profilo"; - -/* No comment provided by engineer. */ -"Profile name:" = "Nome del profilo:"; +"Profile images" = "Immagini del profilo"; /* No comment provided by engineer. */ "Profile password" = "Password del profilo"; /* No comment provided by engineer. */ +"Profile theme" = "Tema del profilo"; + +/* alert message */ "Profile update will be sent to your contacts." = "L'aggiornamento del profilo verrà inviato ai tuoi contatti."; /* No comment provided by engineer. */ @@ -2771,6 +3962,9 @@ /* No comment provided by engineer. */ "Prohibit messages reactions." = "Proibisci le reazioni ai messaggi."; +/* No comment provided by engineer. */ +"Prohibit reporting messages to moderators." = "Vieta di segnalare messaggi ai moderatori."; + /* No comment provided by engineer. */ "Prohibit sending direct messages to members." = "Proibisci l'invio di messaggi diretti ai membri."; @@ -2780,41 +3974,71 @@ /* No comment provided by engineer. */ "Prohibit sending files and media." = "Proibisci l'invio di file e contenuti multimediali."; +/* No comment provided by engineer. */ +"Prohibit sending SimpleX links." = "Vieta l'invio di link di SimpleX."; + /* No comment provided by engineer. */ "Prohibit sending voice messages." = "Proibisci l'invio di messaggi vocali."; /* No comment provided by engineer. */ "Protect app screen" = "Proteggi la schermata dell'app"; +/* No comment provided by engineer. */ +"Protect IP address" = "Proteggi l'indirizzo IP"; + /* No comment provided by engineer. */ "Protect your chat profiles with a password!" = "Proteggi i tuoi profili di chat con una password!"; +/* No comment provided by engineer. */ +"Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." = "Proteggi il tuo indirizzo IP dai relay di messaggistica scelti dai tuoi contatti.\nAttivalo nelle impostazioni *Rete e server*."; + /* No comment provided by engineer. */ "Protocol timeout" = "Scadenza del protocollo"; /* No comment provided by engineer. */ "Protocol timeout per KB" = "Scadenza del protocollo per KB"; +/* No comment provided by engineer. */ +"Proxied" = "Via proxy"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Server via proxy"; + +/* No comment provided by engineer. */ +"Proxy requires password" = "Il proxy richiede una password"; + /* No comment provided by engineer. */ "Push notifications" = "Notifiche push"; +/* No comment provided by engineer. */ +"Push server" = "Server push"; + +/* chat item text */ +"quantum resistant e2e encryption" = "crittografia e2e resistente alla quantistica"; + +/* No comment provided by engineer. */ +"Quantum resistant encryption" = "Crittografia resistente alla quantistica"; + /* No comment provided by engineer. */ "Rate the app" = "Valuta l'app"; +/* No comment provided by engineer. */ +"Reachable chat toolbar" = "Barra degli strumenti di chat accessibile"; + /* chat item menu */ "React…" = "Reagisci…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "Leggi"; /* No comment provided by engineer. */ "Read more" = "Leggi tutto"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Maggiori informazioni nella [Guida per l'utente](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Leggi di più nella [Guida utente](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Leggi di più nella [Guida utente](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "Maggiori informazioni nella [Guida per l'utente](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)."; /* No comment provided by engineer. */ "Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Maggiori informazioni nella [Guida per l'utente](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; @@ -2823,10 +4047,10 @@ "Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Maggiori informazioni nel nostro [repository GitHub](https://github.com/simplex-chat/simplex-chat#readme)."; /* No comment provided by engineer. */ -"Read more in our GitHub repository." = "Maggiori informazioni nel nostro repository GitHub."; +"Receipts are disabled" = "Le ricevute sono disattivate"; /* No comment provided by engineer. */ -"Receipts are disabled" = "Le ricevute sono disattivate"; +"Receive errors" = "Errori di ricezione"; /* No comment provided by engineer. */ "received answer…" = "risposta ricevuta…"; @@ -2846,6 +4070,15 @@ /* message info title */ "Received message" = "Messaggio ricevuto"; +/* No comment provided by engineer. */ +"Received messages" = "Messaggi ricevuti"; + +/* No comment provided by engineer. */ +"Received reply" = "Risposta ricevuta"; + +/* No comment provided by engineer. */ +"Received total" = "Totale ricevuto"; + /* No comment provided by engineer. */ "Receiving address will be changed to a different server. Address change will complete after sender comes online." = "L'indirizzo di ricezione verrà cambiato in un server diverso. La modifica dell'indirizzo verrà completata dopo che il mittente sarà in linea."; @@ -2858,12 +4091,30 @@ /* No comment provided by engineer. */ "Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)." = "Cronologia recente e [bot della directory](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) migliorato."; +/* No comment provided by engineer. */ +"Recipient(s) can't see who this message is from." = "I destinatari non possono vedere da chi proviene questo messaggio."; + /* No comment provided by engineer. */ "Recipients see updates as you type them." = "I destinatari vedono gli aggiornamenti mentre li digiti."; +/* No comment provided by engineer. */ +"Reconnect" = "Riconnetti"; + /* No comment provided by engineer. */ "Reconnect all connected servers to force message delivery. It uses additional traffic." = "Riconnetti tutti i server connessi per imporre il recapito dei messaggi. Utilizza traffico aggiuntivo."; +/* No comment provided by engineer. */ +"Reconnect all servers" = "Riconnetti tutti i server"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "Riconnettere tutti i server?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "Riconnetti il server per forzare la consegna dei messaggi. Usa traffico aggiuntivo."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "Riconnettere il server?"; + /* No comment provided by engineer. */ "Reconnect servers?" = "Riconnettere i server?"; @@ -2876,7 +4127,17 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "Consumo di batteria ridotto"; -/* reject incoming call via notification */ +/* No comment provided by engineer. */ +"Register" = "Registra"; + +/* token info */ +"Register notification token?" = "Registrare il token di notifica?"; + +/* token status text */ +"Registered" = "Registrato"; + +/* reject incoming call via notification +swipe action */ "Reject" = "Rifiuta"; /* No comment provided by engineer. */ @@ -2885,6 +4146,9 @@ /* No comment provided by engineer. */ "Reject contact request" = "Rifiuta la richiesta di contatto"; +/* No comment provided by engineer. */ +"rejected" = "rifiutato"; + /* call status */ "rejected call" = "chiamata rifiutata"; @@ -2897,6 +4161,12 @@ /* No comment provided by engineer. */ "Remove" = "Rimuovi"; +/* No comment provided by engineer. */ +"Remove archive?" = "Rimuovere l'archivio?"; + +/* No comment provided by engineer. */ +"Remove image" = "Rimuovi immagine"; + /* No comment provided by engineer. */ "Remove member" = "Rimuovi membro"; @@ -2933,24 +4203,81 @@ /* No comment provided by engineer. */ "Repeat connection request?" = "Ripetere la richiesta di connessione?"; +/* No comment provided by engineer. */ +"Repeat download" = "Ripeti scaricamento"; + +/* No comment provided by engineer. */ +"Repeat import" = "Ripeti importazione"; + /* No comment provided by engineer. */ "Repeat join request?" = "Ripetere la richiesta di ingresso?"; +/* No comment provided by engineer. */ +"Repeat upload" = "Ripeti caricamento"; + /* chat item action */ "Reply" = "Rispondi"; +/* chat item action */ +"Report" = "Segnala"; + +/* report reason */ +"Report content: only group moderators will see it." = "Segnala contenuto: solo i moderatori del gruppo lo vedranno."; + +/* report reason */ +"Report member profile: only group moderators will see it." = "Segnala profilo: solo i moderatori del gruppo lo vedranno."; + +/* report reason */ +"Report other: only group moderators will see it." = "Segnala altro: solo i moderatori del gruppo lo vedranno."; + +/* No comment provided by engineer. */ +"Report reason?" = "Motivo della segnalazione?"; + +/* report reason */ +"Report spam: only group moderators will see it." = "Segnala spam: solo i moderatori del gruppo lo vedranno."; + +/* report reason */ +"Report violation: only group moderators will see it." = "Segnala violazione: solo i moderatori del gruppo lo vedranno."; + +/* report in notification */ +"Report: %@" = "Segnalazione: %@"; + +/* No comment provided by engineer. */ +"Reporting messages to moderators is prohibited." = "È vietato segnalare messaggi ai moderatori."; + +/* No comment provided by engineer. */ +"Reports" = "Segnalazioni"; + +/* chat list item title */ +"requested to connect" = "richiesto di connettersi"; + /* No comment provided by engineer. */ "Required" = "Obbligatorio"; /* No comment provided by engineer. */ "Reset" = "Ripristina"; +/* No comment provided by engineer. */ +"Reset all hints" = "Ripristina tutti i suggerimenti"; + +/* No comment provided by engineer. */ +"Reset all statistics" = "Azzera tutte le statistiche"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "Azzerare tutte le statistiche?"; + /* No comment provided by engineer. */ "Reset colors" = "Ripristina i colori"; +/* No comment provided by engineer. */ +"Reset to app theme" = "Ripristina al tema dell'app"; + /* No comment provided by engineer. */ "Reset to defaults" = "Ripristina i predefiniti"; +/* No comment provided by engineer. */ +"Reset to user theme" = "Ripristina al tema dell'utente"; + /* No comment provided by engineer. */ "Restart the app to create a new chat profile" = "Riavvia l'app per creare un nuovo profilo di chat"; @@ -2976,7 +4303,7 @@ "Reveal" = "Rivela"; /* No comment provided by engineer. */ -"Revert" = "Ripristina"; +"Review conditions" = "Leggi le condizioni"; /* No comment provided by engineer. */ "Revoke" = "Revoca"; @@ -2993,37 +4320,44 @@ /* No comment provided by engineer. */ "Run chat" = "Avvia chat"; -/* chat item action */ +/* No comment provided by engineer. */ +"Safely receive files" = "Ricevi i file in sicurezza"; + +/* No comment provided by engineer. */ +"Safer groups" = "Gruppi più sicuri"; + +/* alert button +chat item action */ "Save" = "Salva"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Salva (e avvisa i contatti)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "Salva e avvisa il contatto"; /* No comment provided by engineer. */ "Save and notify group members" = "Salva e avvisa i membri del gruppo"; +/* No comment provided by engineer. */ +"Save and reconnect" = "Salva e riconnetti"; + /* No comment provided by engineer. */ "Save and update group profile" = "Salva e aggiorna il profilo del gruppo"; -/* No comment provided by engineer. */ -"Save archive" = "Salva archivio"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Salva le impostazioni di accettazione automatica"; - /* No comment provided by engineer. */ "Save group profile" = "Salva il profilo del gruppo"; +/* No comment provided by engineer. */ +"Save list" = "Salva elenco"; + /* No comment provided by engineer. */ "Save passphrase and open chat" = "Salva la password e apri la chat"; /* No comment provided by engineer. */ "Save passphrase in Keychain" = "Salva password nel portachiavi"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "Salvare le preferenze?"; /* No comment provided by engineer. */ @@ -3032,14 +4366,26 @@ /* No comment provided by engineer. */ "Save servers" = "Salva i server"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "Salvare i server?"; /* No comment provided by engineer. */ -"Save settings?" = "Salvare le impostazioni?"; +"Save welcome message?" = "Salvare il messaggio di benvenuto?"; + +/* alert title */ +"Save your profile?" = "Salvare il profilo?"; /* No comment provided by engineer. */ -"Save welcome message?" = "Salvare il messaggio di benvenuto?"; +"saved" = "salvato"; + +/* No comment provided by engineer. */ +"Saved" = "Salvato"; + +/* No comment provided by engineer. */ +"Saved from" = "Salvato da"; + +/* No comment provided by engineer. */ +"saved from %@" = "salvato da %@"; /* message info title */ "Saved message" = "Messaggio salvato"; @@ -3047,6 +4393,15 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "I server WebRTC ICE salvati verranno rimossi"; +/* No comment provided by engineer. */ +"Saving %lld messages" = "Salvataggio di %lld messaggi"; + +/* No comment provided by engineer. */ +"Scale" = "Scala"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Scansiona / Incolla link"; + /* No comment provided by engineer. */ "Scan code" = "Scansiona codice"; @@ -3062,6 +4417,9 @@ /* No comment provided by engineer. */ "Scan server QR code" = "Scansiona codice QR del server"; +/* No comment provided by engineer. */ +"search" = "cerca"; + /* No comment provided by engineer. */ "Search" = "Cerca"; @@ -3074,6 +4432,9 @@ /* network option */ "sec" = "sec"; +/* No comment provided by engineer. */ +"Secondary" = "Secondario"; + /* time unit */ "seconds" = "secondi"; @@ -3083,6 +4444,9 @@ /* server test step */ "Secure queue" = "Coda sicura"; +/* No comment provided by engineer. */ +"Secured" = "Protetto"; + /* No comment provided by engineer. */ "Security assessment" = "Valutazione della sicurezza"; @@ -3092,9 +4456,18 @@ /* chat item text */ "security code changed" = "codice di sicurezza modificato"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "Seleziona"; +/* No comment provided by engineer. */ +"Select chat profile" = "Seleziona il profilo di chat"; + +/* No comment provided by engineer. */ +"Selected %lld" = "%lld selezionato"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Le preferenze della chat selezionata vietano questo messaggio."; + /* No comment provided by engineer. */ "Self-destruct" = "Autodistruzione"; @@ -3119,9 +4492,6 @@ /* No comment provided by engineer. */ "send direct message" = "invia messaggio diretto"; -/* No comment provided by engineer. */ -"Send direct message" = "Invia messaggio diretto"; - /* No comment provided by engineer. */ "Send direct message to connect" = "Invia messaggio diretto per connetterti"; @@ -3129,16 +4499,28 @@ "Send disappearing message" = "Invia messaggio a tempo"; /* No comment provided by engineer. */ -"Send link previews" = "Invia anteprime dei link"; +"Send errors" = "Errori di invio"; + +/* No comment provided by engineer. */ +"Send link previews" = "Invia le anteprime dei link"; /* No comment provided by engineer. */ "Send live message" = "Invia messaggio in diretta"; +/* No comment provided by engineer. */ +"Send message to enable calls." = "Invia un messaggio per attivare le chiamate."; + +/* No comment provided by engineer. */ +"Send messages directly when IP address is protected and your or destination server does not support private routing." = "Invia messaggi direttamente quando l'indirizzo IP è protetto e il tuo server o quello di destinazione non supporta l'instradamento privato."; + +/* No comment provided by engineer. */ +"Send messages directly when your or destination server does not support private routing." = "Invia messaggi direttamente quando il tuo server o quello di destinazione non supporta l'instradamento privato."; + /* No comment provided by engineer. */ "Send notifications" = "Invia notifiche"; /* No comment provided by engineer. */ -"Send notifications:" = "Invia notifiche:"; +"Send private reports" = "Invia segnalazioni private"; /* No comment provided by engineer. */ "Send questions and ideas" = "Invia domande e idee"; @@ -3152,7 +4534,7 @@ /* No comment provided by engineer. */ "Send up to 100 last messages to new members." = "Invia fino a 100 ultimi messaggi ai nuovi membri."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Il mittente ha annullato il trasferimento del file."; /* No comment provided by engineer. */ @@ -3188,15 +4570,57 @@ /* copied message info */ "Sent at: %@" = "Inviato il: %@"; +/* No comment provided by engineer. */ +"Sent directly" = "Inviato direttamente"; + /* notification */ "Sent file event" = "Evento file inviato"; /* message info title */ "Sent message" = "Messaggio inviato"; +/* No comment provided by engineer. */ +"Sent messages" = "Messaggi inviati"; + /* No comment provided by engineer. */ "Sent messages will be deleted after set time." = "I messaggi inviati verranno eliminati dopo il tempo impostato."; +/* No comment provided by engineer. */ +"Sent reply" = "Risposta inviata"; + +/* No comment provided by engineer. */ +"Sent total" = "Totale inviato"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Inviato via proxy"; + +/* No comment provided by engineer. */ +"Server" = "Server"; + +/* alert message */ +"Server added to operator %@." = "Server aggiunto all'operatore %@."; + +/* No comment provided by engineer. */ +"Server address" = "Indirizzo server"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "L'indirizzo del server è incompatibile con le impostazioni di rete: %@."; + +/* srv error text. */ +"Server address is incompatible with network settings." = "L'indirizzo del server non è compatibile con le impostazioni di rete."; + +/* alert title */ +"Server operator changed." = "L'operatore del server è cambiato."; + +/* No comment provided by engineer. */ +"Server operators" = "Operatori server"; + +/* alert title */ +"Server protocol changed." = "Il protocollo del server è cambiato."; + +/* queue info */ +"server queue info: %@\n\nlast received msg: %@" = "info coda server: %1$@\n\nultimo msg ricevuto: %2$@"; + /* server test error */ "Server requires authorization to create queues, check password" = "Il server richiede l'autorizzazione di creare code, controlla la password"; @@ -3206,33 +4630,60 @@ /* No comment provided by engineer. */ "Server test failed!" = "Test del server fallito!"; +/* No comment provided by engineer. */ +"Server type" = "Tipo server"; + +/* srv error text */ +"Server version is incompatible with network settings." = "La versione del server non è compatibile con le impostazioni di rete."; + +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "La versione del server è incompatibile con la tua app: %@."; + /* No comment provided by engineer. */ "Servers" = "Server"; +/* No comment provided by engineer. */ +"Servers info" = "Info dei server"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "Le statistiche dei server verranno azzerate - è irreversibile!"; + /* No comment provided by engineer. */ "Session code" = "Codice di sessione"; /* No comment provided by engineer. */ "Set 1 day" = "Imposta 1 giorno"; +/* No comment provided by engineer. */ +"Set chat name…" = "Imposta il nome della chat…"; + /* No comment provided by engineer. */ "Set contact name…" = "Imposta nome del contatto…"; +/* No comment provided by engineer. */ +"Set default theme" = "Imposta tema predefinito"; + /* No comment provided by engineer. */ "Set group preferences" = "Imposta le preferenze del gruppo"; /* No comment provided by engineer. */ "Set it instead of system authentication." = "Impostalo al posto dell'autenticazione di sistema."; +/* No comment provided by engineer. */ +"Set message expiration in chats." = "Imposta la scadenza dei messaggi nelle chat."; + /* profile update event chat item */ "set new contact address" = "impostato nuovo indirizzo di contatto"; /* profile update event chat item */ -"set new profile picture" = "impostata nuova immagine del profilo"; +"set new profile picture" = "ha impostato una nuova immagine del profilo"; /* No comment provided by engineer. */ "Set passcode" = "Imposta codice"; +/* No comment provided by engineer. */ +"Set passphrase" = "Imposta password"; + /* No comment provided by engineer. */ "Set passphrase to export" = "Imposta la password per esportare"; @@ -3245,27 +4696,58 @@ /* No comment provided by engineer. */ "Settings" = "Impostazioni"; -/* chat item action */ +/* alert message */ +"Settings were changed." = "Le impostazioni sono state cambiate."; + +/* No comment provided by engineer. */ +"Shape profile images" = "Forma delle immagini del profilo"; + +/* alert action +chat item action */ "Share" = "Condividi"; /* No comment provided by engineer. */ "Share 1-time link" = "Condividi link una tantum"; +/* No comment provided by engineer. */ +"Share 1-time link with a friend" = "Condividi link una tantum con un amico"; + /* No comment provided by engineer. */ "Share address" = "Condividi indirizzo"; /* No comment provided by engineer. */ +"Share address publicly" = "Condividi indirizzo pubblicamente"; + +/* alert title */ "Share address with contacts?" = "Condividere l'indirizzo con i contatti?"; +/* No comment provided by engineer. */ +"Share from other apps." = "Condividi da altre app."; + /* No comment provided by engineer. */ "Share link" = "Condividi link"; +/* No comment provided by engineer. */ +"Share profile" = "Condividi il profilo"; + +/* No comment provided by engineer. */ +"Share SimpleX address on social media." = "Condividi l'indirizzo SimpleX sui social media."; + /* No comment provided by engineer. */ "Share this 1-time invite link" = "Condividi questo link di invito una tantum"; +/* No comment provided by engineer. */ +"Share to SimpleX" = "Condividi in SimpleX"; + /* No comment provided by engineer. */ "Share with contacts" = "Condividi con i contatti"; +/* No comment provided by engineer. */ +"Short link" = "Link breve"; + +/* No comment provided by engineer. */ +"Show → on messages sent via private routing." = "Mostra → nei messaggi inviati via instradamento privato."; + /* No comment provided by engineer. */ "Show calls in phone history" = "Mostra le chiamate nella cronologia del telefono"; @@ -3275,18 +4757,42 @@ /* No comment provided by engineer. */ "Show last messages" = "Mostra ultimi messaggi"; +/* No comment provided by engineer. */ +"Show message status" = "Mostra stato del messaggio"; + +/* No comment provided by engineer. */ +"Show percentage" = "Mostra percentuale"; + /* No comment provided by engineer. */ "Show preview" = "Mostra anteprima"; +/* No comment provided by engineer. */ +"Show QR code" = "Mostra codice QR"; + /* No comment provided by engineer. */ "Show:" = "Mostra:"; +/* No comment provided by engineer. */ +"SimpleX" = "SimpleX"; + /* No comment provided by engineer. */ "SimpleX address" = "Indirizzo SimpleX"; /* No comment provided by engineer. */ "SimpleX Address" = "Indirizzo SimpleX"; +/* No comment provided by engineer. */ +"SimpleX address and 1-time links are safe to share via any messenger." = "L'indirizzo SimpleX e i link una tantum sono sicuri da condividere tramite qualsiasi messenger."; + +/* No comment provided by engineer. */ +"SimpleX address or 1-time link?" = "Indirizzo SimpleX o link una tantum?"; + +/* simplex link type */ +"SimpleX channel link" = "Link del canale SimpleX"; + +/* No comment provided by engineer. */ +"SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app." = "SimpleX Chat e Flux hanno concluso un accordo per includere server gestiti da Flux nell'app."; + /* No comment provided by engineer. */ "SimpleX Chat security was audited by Trail of Bits." = "La sicurezza di SimpleX Chat è stata verificata da Trail of Bits."; @@ -3299,9 +4805,15 @@ /* simplex link type */ "SimpleX group link" = "Link gruppo SimpleX"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "Link di SimpleX"; +/* No comment provided by engineer. */ +"SimpleX links are prohibited." = "I link di SimpleX sono vietati in questo gruppo."; + +/* No comment provided by engineer. */ +"SimpleX links not allowed" = "Link di SimpleX non consentiti"; + /* No comment provided by engineer. */ "SimpleX Lock" = "SimpleX Lock"; @@ -3317,9 +4829,15 @@ /* simplex link type */ "SimpleX one-time invitation" = "Invito SimpleX una tantum"; +/* No comment provided by engineer. */ +"SimpleX protocols reviewed by Trail of Bits." = "Protocolli di SimpleX esaminati da Trail of Bits."; + /* No comment provided by engineer. */ "Simplified incognito mode" = "Modalità incognito semplificata"; +/* No comment provided by engineer. */ +"Size" = "Dimensione"; + /* No comment provided by engineer. */ "Skip" = "Salta"; @@ -3330,14 +4848,42 @@ "Small groups (max 20)" = "Piccoli gruppi (max 20)"; /* No comment provided by engineer. */ -"SMP servers" = "Server SMP"; +"SMP server" = "Server SMP"; + +/* No comment provided by engineer. */ +"SOCKS proxy" = "Proxy SOCKS"; + +/* blur media */ +"Soft" = "Leggera"; + +/* No comment provided by engineer. */ +"Some app settings were not migrated." = "Alcune impostazioni dell'app non sono state migrate."; + +/* No comment provided by engineer. */ +"Some file(s) were not exported:" = "Alcuni file non sono stati esportati:"; /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "Si sono verificati alcuni errori non gravi durante l'importazione: vedi la console della chat per i dettagli."; +/* No comment provided by engineer. */ +"Some non-fatal errors occurred during import:" = "Si sono verificati alcuni errori non fatali durante l'importazione:"; + +/* alert message */ +"Some servers failed the test:\n%@" = "Alcuni server hanno fallito il test:\n%@"; + /* notification title */ "Somebody" = "Qualcuno"; +/* blocking reason +report reason */ +"Spam" = "Spam"; + +/* No comment provided by engineer. */ +"Square, circle, or anything in between." = "Quadrata, circolare o qualsiasi forma tra le due."; + +/* chat item text */ +"standard end-to-end encryption" = "crittografia end-to-end standard"; + /* No comment provided by engineer. */ "Start chat" = "Avvia chat"; @@ -3347,14 +4893,20 @@ /* No comment provided by engineer. */ "Start migration" = "Avvia la migrazione"; +/* No comment provided by engineer. */ +"Starting from %@." = "Inizio da %@."; + /* No comment provided by engineer. */ "starting…" = "avvio…"; +/* No comment provided by engineer. */ +"Statistics" = "Statistiche"; + /* No comment provided by engineer. */ "Stop" = "Ferma"; /* No comment provided by engineer. */ -"Stop chat to enable database actions" = "Ferma la chat per attivare le azioni del database"; +"Stop chat" = "Ferma la chat"; /* No comment provided by engineer. */ "Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Ferma la chat per esportare, importare o eliminare il database della chat. Non potrai ricevere e inviare messaggi mentre la chat è ferma."; @@ -3371,36 +4923,66 @@ /* No comment provided by engineer. */ "Stop sending file?" = "Fermare l'invio del file?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "Smetti di condividere"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "Smettere di condividere l'indirizzo?"; /* authentication reason */ "Stop SimpleX" = "Ferma SimpleX"; +/* No comment provided by engineer. */ +"Stopping chat" = "Arresto della chat"; + +/* No comment provided by engineer. */ +"Storage" = "Archiviazione"; + /* No comment provided by engineer. */ "strike" = "barrato"; +/* blur media */ +"Strong" = "Forte"; + /* No comment provided by engineer. */ "Submit" = "Invia"; +/* No comment provided by engineer. */ +"Subscribed" = "Iscritto"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Errori di iscrizione"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Iscrizioni ignorate"; + /* No comment provided by engineer. */ "Support SimpleX Chat" = "Supporta SimpleX Chat"; +/* No comment provided by engineer. */ +"Switch audio and video during the call." = "Cambia tra audio e video durante la chiamata."; + +/* No comment provided by engineer. */ +"Switch chat profile for 1-time invitations." = "Cambia profilo di chat per inviti una tantum."; + /* No comment provided by engineer. */ "System" = "Sistema"; /* No comment provided by engineer. */ "System authentication" = "Autenticazione di sistema"; +/* No comment provided by engineer. */ +"Tail" = "Coda"; + /* No comment provided by engineer. */ "Take picture" = "Scatta foto"; /* No comment provided by engineer. */ "Tap button " = "Tocca il pulsante "; +/* No comment provided by engineer. */ +"Tap Create SimpleX address in the menu to create it later." = "Tocca Crea indirizzo SimpleX nel menu per crearlo più tardi."; + /* No comment provided by engineer. */ "Tap to activate profile." = "Tocca per attivare il profilo."; @@ -3420,11 +5002,14 @@ "Tap to scan" = "Tocca per scansionare"; /* No comment provided by engineer. */ -"Tap to start a new chat" = "Tocca per iniziare una chat"; +"TCP connection" = "Connessione TCP"; /* No comment provided by engineer. */ "TCP connection timeout" = "Scadenza connessione TCP"; +/* No comment provided by engineer. */ +"TCP port for messaging" = "Porta TCP per i messaggi"; + /* No comment provided by engineer. */ "TCP_KEEPCNT" = "TCP_KEEPCNT"; @@ -3434,16 +5019,22 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* file error alert title */ +"Temporary file error" = "Errore del file temporaneo"; + /* server test failure */ "Test failed at step %@." = "Test fallito al passo %@."; +/* No comment provided by engineer. */ +"Test notifications" = "Prova le notifiche"; + /* No comment provided by engineer. */ "Test server" = "Prova server"; /* No comment provided by engineer. */ "Test servers" = "Prova i server"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "Test falliti!"; /* No comment provided by engineer. */ @@ -3456,10 +5047,13 @@ "Thanks to the users – contribute via Weblate!" = "Grazie agli utenti – contribuite via Weblate!"; /* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "La prima piattaforma senza alcun identificatore utente – privata by design."; +"The app can notify you when you receive messages or contact requests - please open settings to enable." = "L'app può avvisarti quando ricevi messaggi o richieste di contatto: apri le impostazioni per attivare."; /* No comment provided by engineer. */ -"The app can notify you when you receive messages or contact requests - please open settings to enable." = "L'app può avvisarti quando ricevi messaggi o richieste di contatto: apri le impostazioni per attivare."; +"The app protects your privacy by using different operators in each conversation." = "L'app protegge la tua privacy usando diversi operatori in ogni conversazione."; + +/* No comment provided by engineer. */ +"The app will ask to confirm downloads from unknown file servers (except .onion)." = "L'app chiederà di confermare i download da server di file sconosciuti (eccetto .onion)."; /* No comment provided by engineer. */ "The attempt to change database passphrase was not completed." = "Il tentativo di cambiare la password del database non è stato completato."; @@ -3467,6 +5061,9 @@ /* No comment provided by engineer. */ "The code you scanned is not a SimpleX link QR code." = "Il codice che hai scansionato non è un codice QR di link SimpleX."; +/* No comment provided by engineer. */ +"The connection reached the limit of undelivered messages, your contact may be offline." = "La connessione ha raggiunto il limite di messaggi non consegnati, il contatto potrebbe essere offline."; + /* No comment provided by engineer. */ "The connection you accepted will be cancelled!" = "La connessione che hai accettato verrà annullata!"; @@ -3479,6 +5076,9 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "La crittografia funziona e il nuovo accordo sulla crittografia non è richiesto. Potrebbero verificarsi errori di connessione!"; +/* No comment provided by engineer. */ +"The future of messaging" = "La nuova generazione di messaggistica privata"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "L'hash del messaggio precedente è diverso."; @@ -3492,13 +5092,22 @@ "The message will be marked as moderated for all members." = "Il messaggio sarà segnato come moderato per tutti i membri."; /* No comment provided by engineer. */ -"The next generation of private messaging" = "La nuova generazione di messaggistica privata"; +"The messages will be deleted for all members." = "I messaggi verranno eliminati per tutti i membri."; + +/* No comment provided by engineer. */ +"The messages will be marked as moderated for all members." = "I messaggi verranno contrassegnati come moderati per tutti i membri."; /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "Il database vecchio non è stato rimosso durante la migrazione, può essere eliminato."; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "Il profilo è condiviso solo con i tuoi contatti."; +"Your profile is stored on your device and only shared with your contacts." = "Il profilo è condiviso solo con i tuoi contatti."; + +/* No comment provided by engineer. */ +"The same conditions will apply to operator **%@**." = "Le stesse condizioni si applicheranno all'operatore **%@**."; + +/* No comment provided by engineer. */ +"The second preset operator in the app!" = "Il secondo operatore preimpostato nell'app!"; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "Il secondo segno di spunta che ci mancava! ✅"; @@ -3509,11 +5118,20 @@ /* No comment provided by engineer. */ "The servers for new connections of your current chat profile **%@**." = "I server per le nuove connessioni del profilo di chat attuale **%@**."; +/* No comment provided by engineer. */ +"The servers for new files of your current chat profile **%@**." = "I server per nuovi file del tuo profilo di chat attuale **%@**."; + /* No comment provided by engineer. */ "The text you pasted is not a SimpleX link." = "Il testo che hai incollato non è un link SimpleX."; /* No comment provided by engineer. */ -"Theme" = "Tema"; +"The uploaded database archive will be permanently removed from the servers." = "L'archivio del database caricato verrà rimosso definitivamente dai server."; + +/* No comment provided by engineer. */ +"Themes" = "Temi"; + +/* No comment provided by engineer. */ +"These conditions will also apply for: **%@**." = "Queste condizioni si applicheranno anche per: **%@**."; /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Queste impostazioni sono per il tuo profilo attuale **%@**."; @@ -3527,9 +5145,18 @@ /* No comment provided by engineer. */ "This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Questa azione non può essere annullata: i messaggi inviati e ricevuti prima di quanto selezionato verranno eliminati. Potrebbe richiedere diversi minuti."; +/* alert message */ +"This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted." = "Questa azione non è reversibile: i messaggi inviati e ricevuti in questa chat prima della selezione verranno eliminati."; + /* No comment provided by engineer. */ "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Questa azione non può essere annullata: il tuo profilo, i contatti, i messaggi e i file andranno persi in modo irreversibile."; +/* E2EE info chat item */ +"This chat is protected by end-to-end encryption." = "Questa chat è protetta da crittografia end-to-end."; + +/* E2EE info chat item */ +"This chat is protected by quantum resistant end-to-end encryption." = "Questa chat è protetta da crittografia end-to-end resistente alla quantistica."; + /* notification title */ "this contact" = "questo contatto"; @@ -3551,9 +5178,21 @@ /* No comment provided by engineer. */ "This is your own SimpleX address!" = "Questo è il tuo indirizzo SimpleX!"; +/* No comment provided by engineer. */ +"This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link." = "Questo link richiede una versione più recente dell'app. Aggiornala o chiedi al tuo contatto di inviare un link compatibile."; + +/* No comment provided by engineer. */ +"This link was used with another mobile device, please create a new link on the desktop." = "Questo link è stato usato con un altro dispositivo mobile, creane uno nuovo sul desktop."; + +/* No comment provided by engineer. */ +"This message was deleted or not received yet." = "Questo messaggio è stato eliminato o non ancora ricevuto."; + /* No comment provided by engineer. */ "This setting applies to messages in your current chat profile **%@**." = "Questa impostazione si applica ai messaggi del profilo di chat attuale **%@**."; +/* No comment provided by engineer. */ +"Title" = "Titoli"; + /* No comment provided by engineer. */ "To ask any questions and to receive updates:" = "Per porre domande e ricevere aggiornamenti:"; @@ -3567,7 +5206,7 @@ "To make a new connection" = "Per creare una nuova connessione"; /* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Per proteggere la privacy, invece degli ID utente utilizzati da tutte le altre piattaforme, SimpleX ha identificatori per le code di messaggi, separati per ciascuno dei tuoi contatti."; +"To protect against your link being replaced, you can compare contact security codes." = "Per proteggerti dalla sostituzione del tuo link, puoi confrontare i codici di sicurezza del contatto."; /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "Per proteggere il fuso orario, i file immagine/vocali usano UTC."; @@ -3575,24 +5214,60 @@ /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Per proteggere le tue informazioni, attiva SimpleX Lock.\nTi verrà chiesto di completare l'autenticazione prima di attivare questa funzionalità."; +/* No comment provided by engineer. */ +"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Per proteggere il tuo indirizzo IP, l'instradamento privato usa i tuoi server SMP per consegnare i messaggi."; + +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "Per proteggere la privacy, invece degli ID utente utilizzati da tutte le altre piattaforme, SimpleX ha identificatori per le code di messaggi, separati per ciascuno dei tuoi contatti."; + +/* No comment provided by engineer. */ +"To receive" = "Per ricevere"; + +/* No comment provided by engineer. */ +"To record speech please grant permission to use Microphone." = "Per registrare l'audio, concedi l'autorizzazione di usare il microfono."; + +/* No comment provided by engineer. */ +"To record video please grant permission to use Camera." = "Per registrare il video, concedi l'autorizzazione di usare la fotocamera."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Per registrare un messaggio vocale, concedi l'autorizzazione all'uso del microfono."; /* No comment provided by engineer. */ "To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Per rivelare il tuo profilo nascosto, inserisci una password completa in un campo di ricerca nella pagina **I tuoi profili di chat**."; +/* No comment provided by engineer. */ +"To send" = "Per inviare"; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Per supportare le notifiche push istantanee, il database della chat deve essere migrato."; +/* No comment provided by engineer. */ +"To use the servers of **%@**, accept conditions of use." = "Per usare i server di **%@**, accetta le condizioni d'uso."; + /* No comment provided by engineer. */ "To verify end-to-end encryption with your contact compare (or scan) the code on your devices." = "Per verificare la crittografia end-to-end con il tuo contatto, confrontate (o scansionate) il codice sui vostri dispositivi."; +/* No comment provided by engineer. */ +"Toggle chat list:" = "Cambia l'elenco delle chat:"; + /* No comment provided by engineer. */ "Toggle incognito when connecting." = "Attiva/disattiva l'incognito quando ti colleghi."; +/* token status */ +"Token status: %@." = "Stato del token: %@."; + +/* No comment provided by engineer. */ +"Toolbar opacity" = "Opacità barra degli strumenti"; + +/* No comment provided by engineer. */ +"Total" = "Totale"; + /* No comment provided by engineer. */ "Transport isolation" = "Isolamento del trasporto"; +/* No comment provided by engineer. */ +"Transport sessions" = "Sessioni di trasporto"; + /* No comment provided by engineer. */ "Trying to connect to the server used to receive messages from this contact (error: %@)." = "Tentativo di connessione al server usato per ricevere messaggi da questo contatto (errore: %@)."; @@ -3629,13 +5304,13 @@ /* rcv group event chat item */ "unblocked %@" = "ha sbloccato %@"; -/* item status description */ -"Unexpected error: %@" = "Errore imprevisto: % @"; +/* No comment provided by engineer. */ +"Undelivered messages" = "Messaggi non consegnati"; /* No comment provided by engineer. */ "Unexpected migration state" = "Stato di migrazione imprevisto"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "Non pref."; /* No comment provided by engineer. */ @@ -3662,6 +5337,12 @@ /* No comment provided by engineer. */ "Unknown error" = "Errore sconosciuto"; +/* No comment provided by engineer. */ +"unknown servers" = "relay sconosciuti"; + +/* alert title */ +"Unknown servers!" = "Server sconosciuti!"; + /* No comment provided by engineer. */ "unknown status" = "stato sconosciuto"; @@ -3683,21 +5364,24 @@ /* authentication reason */ "Unlock app" = "Sblocca l'app"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "Riattiva notifiche"; /* No comment provided by engineer. */ +"unprotected" = "non protetto"; + +/* swipe action */ "Unread" = "Non letto"; +/* No comment provided by engineer. */ +"Unsupported connection link" = "Link di connessione non supportato"; + /* No comment provided by engineer. */ "Up to 100 last messages are sent to new members." = "Vengono inviati ai nuovi membri fino a 100 ultimi messaggi."; /* No comment provided by engineer. */ "Update" = "Aggiorna"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = "Aggiornare l'impostazione degli host .onion?"; - /* No comment provided by engineer. */ "Update database passphrase" = "Aggiorna la password del database"; @@ -3705,7 +5389,10 @@ "Update network settings?" = "Aggiornare le impostazioni di rete?"; /* No comment provided by engineer. */ -"Update transport isolation mode?" = "Aggiornare la modalità di isolamento del trasporto?"; +"Update settings?" = "Aggiornare le impostazioni?"; + +/* No comment provided by engineer. */ +"Updated conditions" = "Condizioni aggiornate"; /* rcv group event chat item */ "updated group profile" = "ha aggiornato il profilo del gruppo"; @@ -3717,23 +5404,44 @@ "Updating settings will re-connect the client to all servers." = "L'aggiornamento delle impostazioni riconnetterà il client a tutti i server."; /* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "L'aggiornamento di questa impostazione riconnetterà il client a tutti i server."; +"Upgrade and open chat" = "Aggiorna e apri chat"; /* No comment provided by engineer. */ -"Upgrade and open chat" = "Aggiorna e apri chat"; +"Upload errors" = "Errori di invio"; + +/* No comment provided by engineer. */ +"Upload failed" = "Invio fallito"; /* server test step */ "Upload file" = "Invia file"; +/* No comment provided by engineer. */ +"Uploaded" = "Inviato"; + +/* No comment provided by engineer. */ +"Uploaded files" = "File inviati"; + +/* No comment provided by engineer. */ +"Uploading archive" = "Invio dell'archivio"; + /* No comment provided by engineer. */ "Use .onion hosts" = "Usa gli host .onion"; +/* No comment provided by engineer. */ +"Use %@" = "Usa %@"; + /* No comment provided by engineer. */ "Use chat" = "Usa la chat"; /* No comment provided by engineer. */ "Use current profile" = "Usa il profilo attuale"; +/* No comment provided by engineer. */ +"Use for files" = "Usa per i file"; + +/* No comment provided by engineer. */ +"Use for messages" = "Usa per i messaggi"; + /* No comment provided by engineer. */ "Use for new connections" = "Usa per connessioni nuove"; @@ -3749,17 +5457,47 @@ /* No comment provided by engineer. */ "Use only local notifications?" = "Usare solo notifiche locali?"; +/* No comment provided by engineer. */ +"Use private routing with unknown servers when IP address is not protected." = "Usa l'instradamento privato con server sconosciuti quando l'indirizzo IP non è protetto."; + +/* No comment provided by engineer. */ +"Use private routing with unknown servers." = "Usa l'instradamento privato con server sconosciuti."; + /* No comment provided by engineer. */ "Use server" = "Usa il server"; +/* No comment provided by engineer. */ +"Use servers" = "Usa i server"; + +/* No comment provided by engineer. */ +"Use short links (BETA)" = "Usa link brevi (BETA)"; + /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Usare i server di SimpleX Chat?"; /* No comment provided by engineer. */ -"User profile" = "Profilo utente"; +"Use SOCKS proxy" = "Usa proxy SOCKS"; /* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = "L'uso di host .onion richiede un fornitore di VPN compatibile."; +"Use TCP port %@ when no port is specified." = "Usa la porta TCP %@ quando non è specificata alcuna porta."; + +/* No comment provided by engineer. */ +"Use TCP port 443 for preset servers only." = "Usa la porta TCP 443 solo per i server preimpostati."; + +/* No comment provided by engineer. */ +"Use the app while in the call." = "Usa l'app mentre sei in chiamata."; + +/* No comment provided by engineer. */ +"Use the app with one hand." = "Usa l'app con una mano sola."; + +/* No comment provided by engineer. */ +"Use web port" = "Usa porta web"; + +/* No comment provided by engineer. */ +"User selection" = "Selezione utente"; + +/* No comment provided by engineer. */ +"Username" = "Nome utente"; /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Utilizzo dei server SimpleX Chat."; @@ -3782,6 +5520,12 @@ /* No comment provided by engineer. */ "Verify connections" = "Verifica le connessioni"; +/* No comment provided by engineer. */ +"Verify database passphrase" = "Verifica password del database"; + +/* No comment provided by engineer. */ +"Verify passphrase" = "Verifica password"; + /* No comment provided by engineer. */ "Verify security code" = "Verifica codice di sicurezza"; @@ -3803,6 +5547,9 @@ /* No comment provided by engineer. */ "Via secure quantum resistant protocol." = "Tramite protocollo sicuro resistente alla quantistica."; +/* No comment provided by engineer. */ +"video" = "video"; + /* No comment provided by engineer. */ "Video call" = "Videochiamata"; @@ -3818,9 +5565,15 @@ /* No comment provided by engineer. */ "Videos and files up to 1gb" = "Video e file fino a 1 GB"; +/* No comment provided by engineer. */ +"View conditions" = "Vedi le condizioni"; + /* No comment provided by engineer. */ "View security code" = "Vedi codice di sicurezza"; +/* No comment provided by engineer. */ +"View updated conditions" = "Vedi le condizioni aggiornate"; + /* chat feature */ "Visible history" = "Cronologia visibile"; @@ -3834,7 +5587,10 @@ "Voice messages are prohibited in this chat." = "I messaggi vocali sono vietati in questa chat."; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "I messaggi vocali sono vietati in questo gruppo."; +"Voice messages are prohibited." = "I messaggi vocali sono vietati in questo gruppo."; + +/* No comment provided by engineer. */ +"Voice messages not allowed" = "Messaggi vocali non consentiti"; /* No comment provided by engineer. */ "Voice messages prohibited!" = "Messaggi vocali vietati!"; @@ -3857,9 +5613,18 @@ /* No comment provided by engineer. */ "Waiting for video" = "In attesa del video"; +/* No comment provided by engineer. */ +"Wallpaper accent" = "Tinta dello sfondo"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Retro dello sfondo"; + /* No comment provided by engineer. */ "wants to connect to you!" = "vuole connettersi con te!"; +/* No comment provided by engineer. */ +"Warning: starting chat on multiple devices is not supported and will cause message delivery failures" = "Attenzione: avviare la chat su più dispositivi non è supportato e provocherà problemi di recapito dei messaggi"; + /* No comment provided by engineer. */ "Warning: you may lose some data!" = "Attenzione: potresti perdere alcuni dati!"; @@ -3875,6 +5640,9 @@ /* No comment provided by engineer. */ "Welcome message" = "Messaggio di benvenuto"; +/* No comment provided by engineer. */ +"Welcome message is too long" = "Il messaggio di benvenuto è troppo lungo"; + /* No comment provided by engineer. */ "What's new" = "Novità"; @@ -3882,11 +5650,26 @@ "When available" = "Quando disponibili"; /* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "Quando le persone chiedono di connettersi, puoi accettare o rifiutare."; +"When connecting audio and video calls." = "Quando si connettono le chiamate audio e video."; + +/* No comment provided by engineer. */ +"when IP hidden" = "quando l'IP è nascosto"; + +/* No comment provided by engineer. */ +"When more than one operator is enabled, none of them has metadata to learn who communicates with whom." = "Quando più di un operatore è attivato, nessuno di essi ha metadati per scoprire chi comunica con chi."; /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Quando condividi un profilo in incognito con qualcuno, questo profilo verrà utilizzato per i gruppi a cui ti invitano."; +/* No comment provided by engineer. */ +"WiFi" = "WiFi"; + +/* No comment provided by engineer. */ +"Will be enabled in direct chats!" = "Viene attivata nelle chat dirette!"; + +/* No comment provided by engineer. */ +"Wired ethernet" = "Cavo ethernet"; + /* No comment provided by engineer. */ "With encrypted files and media." = "Con file e multimediali criptati."; @@ -3896,20 +5679,35 @@ /* No comment provided by engineer. */ "With reduced battery usage." = "Con consumo di batteria ridotto."; +/* No comment provided by engineer. */ +"Without Tor or VPN, your IP address will be visible to file servers." = "Senza Tor o VPN, il tuo indirizzo IP sarà visibile ai server di file."; + +/* alert message */ +"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Senza Tor o VPN, il tuo indirizzo IP sarà visibile a questi relay XFTP: %@."; + /* No comment provided by engineer. */ "Wrong database passphrase" = "Password del database sbagliata"; +/* snd error text */ +"Wrong key or unknown connection - most likely this connection is deleted." = "Chiave sbagliata o connessione sconosciuta - molto probabilmente questa connessione è stata eliminata."; + +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Chiave sbagliata o indirizzo sconosciuto per frammento del file - probabilmente il file è stato eliminato."; + /* No comment provided by engineer. */ "Wrong passphrase!" = "Password sbagliata!"; /* No comment provided by engineer. */ -"XFTP servers" = "Server XFTP"; +"XFTP server" = "Server XFTP"; /* pref value */ "yes" = "sì"; /* No comment provided by engineer. */ -"You" = "Tu"; +"you" = "tu"; + +/* No comment provided by engineer. */ +"You **must not** use the same database on two devices." = "**Non devi** usare lo stesso database su due dispositivi."; /* No comment provided by engineer. */ "You accepted connection" = "Hai accettato la connessione"; @@ -3923,6 +5721,9 @@ /* No comment provided by engineer. */ "You are already connected to %@." = "Sei già connesso/a a %@."; +/* No comment provided by engineer. */ +"You are already connected with %@." = "Sei già connesso/a con %@."; + /* No comment provided by engineer. */ "You are already connecting to %@." = "Ti stai già connettendo a %@."; @@ -3953,6 +5754,9 @@ /* No comment provided by engineer. */ "You are invited to group" = "Sei stato/a invitato/a al gruppo"; +/* No comment provided by engineer. */ +"You are not connected to these servers. Private routing is used to deliver messages to them." = "Non sei connesso/a a questi server. L'instradamento privato è usato per consegnare loro i messaggi."; + /* No comment provided by engineer. */ "you are observer" = "sei un osservatore"; @@ -3962,6 +5766,12 @@ /* No comment provided by engineer. */ "You can accept calls from lock screen, without device and app authentication." = "Puoi accettare chiamate dalla schermata di blocco, senza l'autenticazione del dispositivo e dell'app."; +/* No comment provided by engineer. */ +"You can change it in Appearance settings." = "Puoi cambiarlo nelle impostazioni dell'aspetto."; + +/* No comment provided by engineer. */ +"You can configure servers via settings." = "Puoi configurare i server nelle impostazioni."; + /* No comment provided by engineer. */ "You can create it later" = "Puoi crearlo più tardi"; @@ -3971,6 +5781,9 @@ /* No comment provided by engineer. */ "You can enable them later via app Privacy & Security settings." = "Puoi attivarle più tardi nelle impostazioni di privacy e sicurezza dell'app."; +/* No comment provided by engineer. */ +"You can give another try." = "Puoi fare un altro tentativo."; + /* No comment provided by engineer. */ "You can hide or mute a user profile - swipe it to the right." = "Puoi nascondere o silenziare un profilo utente - scorrilo verso destra."; @@ -3978,7 +5791,13 @@ "You can make it visible to your SimpleX contacts via Settings." = "Puoi renderlo visibile ai tuoi contatti SimpleX nelle impostazioni."; /* notification body */ -"You can now send messages to %@" = "Ora puoi inviare messaggi a %@"; +"You can now chat with %@" = "Ora puoi inviare messaggi a %@"; + +/* No comment provided by engineer. */ +"You can send messages to %@ from Archived contacts." = "Puoi inviare messaggi a %@ dai contatti archiviati."; + +/* No comment provided by engineer. */ +"You can set connection name, to remember who the link was shared with." = "Puoi impostare il nome della connessione per ricordare con chi è stato condiviso il link."; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "Puoi impostare l'anteprima della notifica nella schermata di blocco tramite le impostazioni."; @@ -3990,10 +5809,10 @@ "You can share this address with your contacts to let them connect with **%@**." = "Puoi condividere questo indirizzo con i tuoi contatti per consentire loro di connettersi con **%@**."; /* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "Puoi condividere il tuo indirizzo come link o come codice QR: chiunque potrà connettersi a te."; +"You can start chat via app Settings / Database or by restarting the app" = "Puoi avviare la chat via Impostazioni / Database o riavviando l'app"; /* No comment provided by engineer. */ -"You can start chat via app Settings / Database or by restarting the app" = "Puoi avviare la chat via Impostazioni / Database o riavviando l'app"; +"You can still view conversation with %@ in the list of chats." = "Puoi ancora vedere la conversazione con %@ nell'elenco delle chat."; /* No comment provided by engineer. */ "You can turn on SimpleX Lock via Settings." = "Puoi attivare SimpleX Lock tramite le impostazioni."; @@ -4001,7 +5820,7 @@ /* No comment provided by engineer. */ "You can use markdown to format messages:" = "Puoi usare il markdown per formattare i messaggi:"; -/* No comment provided by engineer. */ +/* alert message */ "You can view invitation link again in connection details." = "Puoi vedere di nuovo il link di invito nei dettagli di connessione."; /* No comment provided by engineer. */ @@ -4020,10 +5839,10 @@ "you changed role of %@ to %@" = "hai cambiato il ruolo di %1$@ in %2$@"; /* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "Tu decidi attraverso quale/i server **ricevere** i messaggi, i tuoi contatti quali server usi per inviare loro i messaggi."; +"You could not be verified; please try again." = "Non è stato possibile verificarti, riprova."; /* No comment provided by engineer. */ -"You could not be verified; please try again." = "Non è stato possibile verificarti, riprova."; +"You decide who can connect." = "Sei tu a decidere chi può connettersi."; /* No comment provided by engineer. */ "You have already requested connection via this address!" = "Hai già richiesto la connessione tramite questo indirizzo!"; @@ -4031,9 +5850,6 @@ /* No comment provided by engineer. */ "You have already requested connection!\nRepeat connection request?" = "Hai già richiesto la connessione!\nRipetere la richiesta di connessione?"; -/* No comment provided by engineer. */ -"You have no chats" = "Non hai chat"; - /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "Devi inserire la password ogni volta che si avvia l'app: non viene memorizzata sul dispositivo."; @@ -4049,9 +5865,18 @@ /* snd group event chat item */ "you left" = "sei uscito/a"; +/* No comment provided by engineer. */ +"You may migrate the exported database." = "Puoi migrare il database esportato."; + +/* No comment provided by engineer. */ +"You may save the exported archive." = "Puoi salvare l'archivio esportato."; + /* No comment provided by engineer. */ "You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." = "Devi usare la versione più recente del tuo database della chat SOLO su un dispositivo, altrimenti potresti non ricevere più i messaggi da alcuni contatti."; +/* No comment provided by engineer. */ +"You need to allow your contact to call to be able to call them." = "Devi consentire le chiamate al tuo contatto per poterlo chiamare."; + /* No comment provided by engineer. */ "You need to allow your contact to send voice messages to be able to send them." = "Devi consentire al tuo contatto di inviare messaggi vocali per poterli inviare anche tu."; @@ -4070,6 +5895,9 @@ /* chat list item description */ "you shared one-time link incognito" = "hai condiviso un link incognito una tantum"; +/* token info */ +"You should receive notifications." = "Dovresti ricevere le notifiche."; + /* snd group event chat item */ "you unblocked %@" = "hai sbloccato %@"; @@ -4094,6 +5922,9 @@ /* No comment provided by engineer. */ "You will still receive calls and notifications from muted profiles when they are active." = "Continuerai a ricevere chiamate e notifiche da profili silenziati quando sono attivi."; +/* No comment provided by engineer. */ +"You will stop receiving messages from this chat. Chat history will be preserved." = "Non riceverai più messaggi da questa chat. La cronologia della chat verrà conservata."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Non riceverai più messaggi da questo gruppo. La cronologia della chat verrà conservata."; @@ -4109,9 +5940,6 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Stai usando un profilo in incognito per questo gruppo: per impedire la condivisione del tuo profilo principale non è consentito invitare contatti"; -/* No comment provided by engineer. */ -"Your %@ servers" = "I tuoi server %@"; - /* No comment provided by engineer. */ "Your calls" = "Le tue chiamate"; @@ -4121,11 +5949,14 @@ /* No comment provided by engineer. */ "Your chat database is not encrypted - set passphrase to encrypt it." = "Il tuo database della chat non è crittografato: imposta la password per crittografarlo."; +/* alert title */ +"Your chat preferences" = "Le tue preferenze della chat"; + /* No comment provided by engineer. */ "Your chat profiles" = "I tuoi profili di chat"; /* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Il tuo contatto deve essere in linea per completare la connessione.\nPuoi annullare questa connessione e rimuovere il contatto (e riprovare più tardi con un link nuovo)."; +"Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." = "La tua connessione è stata spostata a %@, ma si è verificato un errore imprevisto durante il reindirizzamento al profilo."; /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "Il tuo contatto ha inviato un file più grande della dimensione massima attualmente supportata (%@)."; @@ -4136,6 +5967,9 @@ /* No comment provided by engineer. */ "Your contacts will remain connected." = "I tuoi contatti resteranno connessi."; +/* No comment provided by engineer. */ +"Your credentials may be sent unencrypted." = "Le credenziali potrebbero essere inviate in chiaro."; + /* No comment provided by engineer. */ "Your current chat database will be DELETED and REPLACED with the imported one." = "Il tuo attuale database della chat verrà ELIMINATO e SOSTITUITO con quello importato."; @@ -4158,7 +5992,10 @@ "Your profile **%@** will be shared." = "Verrà condiviso il tuo profilo **%@**."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Il tuo profilo è memorizzato sul tuo dispositivo e condiviso solo con i tuoi contatti.\nI server di SimpleX non possono vedere il tuo profilo."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Il tuo profilo è memorizzato sul tuo dispositivo e condiviso solo con i tuoi contatti. I server di SimpleX non possono vedere il tuo profilo."; + +/* alert message */ +"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Il tuo profilo è stato cambiato. Se lo salvi, il profilo aggiornato verrà inviato a tutti i tuoi contatti."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Il tuo profilo, i contatti e i messaggi recapitati sono memorizzati sul tuo dispositivo."; @@ -4167,10 +6004,10 @@ "Your random profile" = "Il tuo profilo casuale"; /* No comment provided by engineer. */ -"Your server" = "Il tuo server"; +"Your server address" = "L'indirizzo del tuo server"; /* No comment provided by engineer. */ -"Your server address" = "L'indirizzo del tuo server"; +"Your servers" = "I tuoi server"; /* No comment provided by engineer. */ "Your settings" = "Le tue impostazioni"; @@ -4178,9 +6015,3 @@ /* No comment provided by engineer. */ "Your SimpleX address" = "Il tuo indirizzo SimpleX"; -/* No comment provided by engineer. */ -"Your SMP servers" = "I tuoi server SMP"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "I tuoi server XFTP"; - diff --git a/apps/ios/ja.lproj/Localizable.strings b/apps/ios/ja.lproj/Localizable.strings index 49797a6ac9..d214f88e1c 100644 --- a/apps/ios/ja.lproj/Localizable.strings +++ b/apps/ios/ja.lproj/Localizable.strings @@ -1,47 +1,29 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (コピー可能)"; /* No comment provided by engineer. */ "_italic_" = "\\_斜体_"; +/* No comment provided by engineer. */ +"- connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)!\n- delivery receipts (up to 20 members).\n- faster and more stable." = "- [ディレクトリサービス](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) に接続 (ベータ)!\n- 配信証明を送信する (最大 20 人まで)。\n- より速く、より安定。"; + /* No comment provided by engineer. */ "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- より安定したメッセージ配信。\n- 改良されたグループ。\n- などなど!"; +/* No comment provided by engineer. */ +"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- 任意で削除された連絡先へ通知します。\n- プロフィール名に空白を含めることができます。\n- and more!"; + /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- 最長 5 分間の音声メッセージ。\n- 消えるまでのカスタム時間。\n- 編集履歴。"; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 色付き!"; /* No comment provided by engineer. */ -"." = "."; +"(new)" = "(新規)"; /* No comment provided by engineer. */ -"(" = "("; - -/* No comment provided by engineer. */ -")" = ")"; +"(this device v%@)" = "(このデバイス v%@)"; /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[貢献する](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -53,29 +35,41 @@ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[GitHub でスターを付ける](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**新しい連絡先を追加**: 連絡先のワンタイム QR コードまたはリンクを作成します。"; +"**Create 1-time link**: to create and share a new invitation link." = "**コンタクトの追加**: 新しい招待リンクを作成するか、受け取ったリンクから接続します。"; /* No comment provided by engineer. */ -"**e2e encrypted** audio call" = "**e2e 暗号化**音声通話"; +"**Create group**: to create a new group." = "**グループ作成**: 新しいグループを作成する。"; /* No comment provided by engineer. */ -"**e2e encrypted** video call" = "**e2e暗号化**ビデオ通話"; +"**e2e encrypted** audio call" = "**エンドツーエンド暗号化済み**の音声通話"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**よりプライベート**: 20 分ごとに新しいメッセージを確認します。 デバイス トークンは SimpleX Chat サーバーと共有されますが、連絡先やメッセージの数は共有されません。"; +"**e2e encrypted** video call" = "**エンドツーエンド暗号化済み**の テレビ電話 通話"; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**最もプライベート**: SimpleX Chat 通知サーバーを使用せず、バックグラウンドで定期的にメッセージをチェックします (アプリの使用頻度によって異なります)。"; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**よりプライベート**: 20 分ごとに新しいメッセージを確認します。 デバイス トークンは SimpleX Chat サーバーと共有されますが、連絡先やメッセージの数は共有されません。"; + +/* No comment provided by engineer. */ +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**最もプライベート**: SimpleX Chat 通知サーバーを使用せず、バックグラウンドで定期的にメッセージをチェックします (アプリの使用頻度によって異なります)。"; + +/* No comment provided by engineer. */ +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**注意**: 2つの端末で同じデータベースを使用すると、セキュリティ保護として、あなたが接続しているメッセージの復号化が解除されます。"; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**注意**: パスフレーズを紛失すると、パスフレーズを復元または変更できなくなります。"; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**推奨**: デバイス トークンと通知は SimpleX Chat 通知サーバーに送信されますが、メッセージの内容、サイズ、送信者は送信されません。"; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**推奨**: デバイス トークンと通知は SimpleX Chat 通知サーバーに送信されますが、メッセージの内容、サイズ、送信者は送信されません。"; + +/* No comment provided by engineer. */ +"**Scan / Paste link**: to connect via a link you received." = "**QRスキャン / リンクの貼り付け**: 受け取ったリンクで接続する。"; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**警告**: 即時の プッシュ通知には、キーチェーンに保存されたパスフレーズが必要です。"; +/* No comment provided by engineer. */ +"**Warning**: the archive will be removed." = "**警告**: アーカイブデータは削除されます。"; + /* No comment provided by engineer. */ "*bold*" = "\\*太字*"; @@ -95,7 +89,7 @@ "%@" = "%@"; /* No comment provided by engineer. */ -"%@ (current)" = "%@ (現在)"; +"%@ (current)" = "%@ (現在)"; /* copied message info */ "%@ (current):" = "%@ (現在):"; @@ -118,6 +112,9 @@ /* No comment provided by engineer. */ "%@ connected" = "%@ 接続中"; +/* No comment provided by engineer. */ +"%@ downloaded" = "%@ ダウンロード済"; + /* notification title */ "%@ is connected!" = "%@ 接続中!"; @@ -127,12 +124,24 @@ /* No comment provided by engineer. */ "%@ is verified" = "%@ は検証されています"; +/* No comment provided by engineer. */ +"%@ server" = "%@ サーバー"; + /* No comment provided by engineer. */ "%@ servers" = "%@ サーバー"; +/* No comment provided by engineer. */ +"%@ uploaded" = "%@ アップロード済"; + /* notification title */ "%@ wants to connect!" = "%@ が接続を希望しています!"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + +/* No comment provided by engineer. */ +"%@, %@ and %lld members" = "%@や%@など%lld人のメンバー"; + /* No comment provided by engineer. */ "%@, %@ and %lld other members connected" = "%@, %@ および %lld 人のメンバーが接続中"; @@ -142,9 +151,24 @@ /* time interval */ "%d days" = "%d 日"; +/* forward confirmation reason */ +"%d file(s) are still being downloaded." = "%d 個のファイルをダウンロードしています。"; + +/* forward confirmation reason */ +"%d file(s) failed to download." = "%d 個のファイルがダウンロードに失敗しました。"; + +/* forward confirmation reason */ +"%d file(s) were deleted." = "%d 個のファイルが削除されました。"; + +/* forward confirmation reason */ +"%d file(s) were not downloaded." = "%d 個のファイルがダウンロードされていません。"; + /* time interval */ "%d hours" = "%d 時"; +/* alert title */ +"%d messages not forwarded" = "%d 個のメッセージが未転送"; + /* time interval */ "%d min" = "%d 分"; @@ -161,7 +185,7 @@ "%d weeks" = "%d 週"; /* No comment provided by engineer. */ -"%lld" = "%lld"; +"%lld" = ""; /* No comment provided by engineer. */ "%lld %@" = "%lld %@"; @@ -172,9 +196,18 @@ /* No comment provided by engineer. */ "%lld file(s) with total size of %@" = "%lld 個のファイル(合計サイズ: %@)"; +/* No comment provided by engineer. */ +"%lld group events" = "%lld件のグループイベント"; + /* No comment provided by engineer. */ "%lld members" = "%lld 人のメンバー"; +/* No comment provided by engineer. */ +"%lld messages blocked" = "%lld件のメッセージをブロック"; + +/* No comment provided by engineer. */ +"%lld messages blocked by admin" = "%lldのメッセージが管理者によりブロック済"; + /* No comment provided by engineer. */ "%lld messages marked deleted" = "%lld 件のメッセージが削除されました"; @@ -187,9 +220,6 @@ /* No comment provided by engineer. */ "%lld new interface languages" = "%lldつの新しいインターフェース言語"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld 秒"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld 秒"; @@ -229,10 +259,14 @@ /* No comment provided by engineer. */ "~strike~" = "\\~取り消し線~"; +/* time to disappear */ +"0 sec" = "0 秒"; + /* No comment provided by engineer. */ "0s" = "0秒"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "1日"; /* time interval */ @@ -241,12 +275,20 @@ /* No comment provided by engineer. */ "1 minute" = "1分"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "1ヶ月"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "1週間"; +/* No comment provided by engineer. */ +"1-time link" = "使い捨てリンク"; + +/* No comment provided by engineer. */ +"1-time link can be used *with one contact only* - share in person or via any messenger." = "使い捨てリンクは、*ひとつの連絡先にのみ* 使用できます - 対面または任意のチャットで共有してください。"; + /* No comment provided by engineer. */ "5 minutes" = "5分"; @@ -280,23 +322,15 @@ /* No comment provided by engineer. */ "Abort changing address?" = "アドレス変更を中止しますか?"; -/* No comment provided by engineer. */ -"About SimpleX" = "SimpleXについて"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "SimpleXアドレスについて"; - /* No comment provided by engineer. */ "About SimpleX Chat" = "SimpleX Chat について"; /* No comment provided by engineer. */ "above, then choose:" = "上で選んでください:"; -/* No comment provided by engineer. */ -"Accent color" = "アクセントカラー"; - /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "承諾"; /* No comment provided by engineer. */ @@ -305,7 +339,8 @@ /* notification body */ "Accept contact request from %@?" = "%@ からの連絡要求を受け入れますか?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "シークレットモードで承諾"; /* call status */ @@ -314,14 +349,11 @@ /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "プロフィールにアドレスを追加し、連絡先があなたのアドレスを他の人と共有できるようにします。プロフィールの更新は連絡先に送信されます。"; -/* No comment provided by engineer. */ -"Add preset servers" = "既存サーバを追加"; - /* No comment provided by engineer. */ "Add profile" = "プロフィールを追加"; /* No comment provided by engineer. */ -"Add server…" = "サーバを追加…"; +"Add server" = "サーバを追加"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "QRコードでサーバを追加する。"; @@ -332,6 +364,12 @@ /* No comment provided by engineer. */ "Add welcome message" = "ウェルカムメッセージを追加"; +/* No comment provided by engineer. */ +"Added media & file servers" = "追加されたメディア & ファイルサーバー"; + +/* No comment provided by engineer. */ +"Added message servers" = "追加されたメッセージサーバー"; + /* No comment provided by engineer. */ "Address" = "アドレス"; @@ -347,6 +385,9 @@ /* No comment provided by engineer. */ "Advanced network settings" = "ネットワーク詳細設定"; +/* No comment provided by engineer. */ +"Advanced settings" = "詳細設定"; + /* chat item text */ "agreeing encryption for %@…" = "%@の暗号化に同意しています…"; @@ -368,12 +409,18 @@ /* No comment provided by engineer. */ "All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "全てのメッセージが削除されます(※注意:元に戻せません!※)。削除されるのは片方あなたのメッセージのみ。"; +/* profile dropdown */ +"All profiles" = "すべてのプロフィール"; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "あなたの連絡先が繋がったまま継続します。"; /* No comment provided by engineer. */ "All your contacts will remain connected. Profile update will be sent to your contacts." = "すべての連絡先は維持されます。連絡先に更新されたプロフィールを送信します。"; +/* No comment provided by engineer. */ +"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "すべての連絡先、会話、ファイルは安全に暗号化され、設定されたXFTPリレーに分割でアップロードされます。"; + /* No comment provided by engineer. */ "Allow" = "許可"; @@ -384,7 +431,7 @@ "Allow disappearing messages only if your contact allows it to you." = "連絡先が許可している場合のみ消えるメッセージを許可する。"; /* No comment provided by engineer. */ -"Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "送信相手も永久メッセージ削除を許可する時のみに許可する。"; +"Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "送信相手も永久メッセージ削除を許可する時のみに許可する。(24時間)"; /* No comment provided by engineer. */ "Allow message reactions only if your contact allows them." = "連絡先が許可している場合にのみ、メッセージへのリアクションを許可します。"; @@ -399,11 +446,17 @@ "Allow sending disappearing messages." = "消えるメッセージの送信を許可する。"; /* No comment provided by engineer. */ -"Allow to irreversibly delete sent messages. (24 hours)" = "送信済みメッセージの永久削除を許可する。"; +"Allow sharing" = "共有を許可"; + +/* No comment provided by engineer. */ +"Allow to irreversibly delete sent messages. (24 hours)" = "送信済みメッセージの永久削除を許可する。(24時間)"; /* No comment provided by engineer. */ "Allow to send files and media." = "ファイルやメディアの送信を許可する。"; +/* No comment provided by engineer. */ +"Allow to send SimpleX links." = "SimpleXリンクの送信を許可。"; + /* No comment provided by engineer. */ "Allow to send voice messages." = "音声メッセージの送信を許可する。"; @@ -420,7 +473,7 @@ "Allow your contacts to call you." = "連絡先からの通話を許可する。"; /* No comment provided by engineer. */ -"Allow your contacts to irreversibly delete sent messages. (24 hours)" = "送信相手が永久メッセージ削除するのを許可する。"; +"Allow your contacts to irreversibly delete sent messages. (24 hours)" = "送信相手が永久メッセージ削除するのを許可する。(24時間)"; /* No comment provided by engineer. */ "Allow your contacts to send disappearing messages." = "送信相手が消えるメッセージを送るのを許可する。"; @@ -431,9 +484,18 @@ /* No comment provided by engineer. */ "Already connected?" = "すでに接続済みですか?"; +/* No comment provided by engineer. */ +"Already connecting!" = "既に接続中です!"; + +/* No comment provided by engineer. */ +"Already joining the group!" = "すでにグループに参加しています!"; + /* pref value */ "always" = "常に"; +/* No comment provided by engineer. */ +"Always use private routing." = "プライベートルーティングを常に使用する。"; + /* No comment provided by engineer. */ "Always use relay" = "常にリレーを経由する"; @@ -443,9 +505,15 @@ /* No comment provided by engineer. */ "Answer call" = "通話に応答"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "プロトコル技術とコードはオープンソースで、どなたでもご自分のサーバを運用できます。"; + /* No comment provided by engineer. */ "App build: %@" = "アプリのビルド: %@"; +/* No comment provided by engineer. */ +"App data migration" = "アプリデータの移行"; + /* No comment provided by engineer. */ "App encrypts new local files (except videos)." = "アプリは新しいローカルファイル(ビデオを除く)を暗号化します。"; @@ -465,7 +533,16 @@ "App version: v%@" = "アプリのバージョン: v%@"; /* No comment provided by engineer. */ -"Appearance" = "見た目"; +"Appearance" = "アピアランス"; + +/* No comment provided by engineer. */ +"Apply" = "適用"; + +/* No comment provided by engineer. */ +"Apply to" = "に適用する"; + +/* No comment provided by engineer. */ +"Archive and upload" = "アーカイブとアップロード"; /* No comment provided by engineer. */ "Attach" = "添付する"; @@ -531,7 +608,7 @@ "Both you and your contact can add message reactions." = "自分も相手もメッセージへのリアクションを追加できます。"; /* No comment provided by engineer. */ -"Both you and your contact can irreversibly delete sent messages. (24 hours)" = "あなたと連絡相手が送信済みメッセージを永久削除できます。"; +"Both you and your contact can irreversibly delete sent messages. (24 hours)" = "あなたと連絡相手が送信済みメッセージを永久削除できます。(24時間)"; /* No comment provided by engineer. */ "Both you and your contact can make calls." = "あなたからも連絡先からも通話ができます。"; @@ -569,7 +646,8 @@ /* No comment provided by engineer. */ "Can't invite contacts!" = "連絡先を招待できません!"; -/* No comment provided by engineer. */ +/* alert action +alert button */ "Cancel" = "中止"; /* feature offered item */ @@ -578,7 +656,7 @@ /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "データベースのパスワードを保存するためのキーチェーンにアクセスできません"; -/* No comment provided by engineer. */ +/* alert title */ "Cannot receive file" = "ファイル受信ができません"; /* No comment provided by engineer. */ @@ -609,7 +687,7 @@ "Change self-destruct mode" = "自己破壊モードの変更"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "自己破壊パスコードを変更する"; /* chat item text */ @@ -627,9 +705,6 @@ /* chat item text */ "changing address…" = "アドレスを変更しています…"; -/* No comment provided by engineer. */ -"Chat archive" = "チャットのアーカイブ"; - /* No comment provided by engineer. */ "Chat console" = "チャットのコンソール"; @@ -652,9 +727,15 @@ "Chat preferences" = "チャット設定"; /* No comment provided by engineer. */ -"Chats" = "チャット"; +"Chat profile" = "ユーザープロフィール"; /* No comment provided by engineer. */ +"Chat theme" = "チャットテーマ"; + +/* No comment provided by engineer. */ +"Chats" = "チャット"; + +/* alert title */ "Check server address and try again." = "サーバのアドレスを確認してから再度試してください。"; /* No comment provided by engineer. */ @@ -667,6 +748,12 @@ "Choose from library" = "ライブラリから選択"; /* No comment provided by engineer. */ +"Chunks deleted" = "チャンクが削除されました"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "チャンクがダウンロードされました"; + +/* swipe action */ "Clear" = "消す"; /* No comment provided by engineer. */ @@ -675,14 +762,17 @@ /* No comment provided by engineer. */ "Clear conversation?" = "ダイアログのクリアしますか?"; +/* No comment provided by engineer. */ +"Clear private notes?" = "プライベートノートを消しますか?"; + /* No comment provided by engineer. */ "Clear verification" = "検証を消す"; /* No comment provided by engineer. */ -"colored" = "色付き"; +"Color mode" = "色設定"; /* No comment provided by engineer. */ -"Colors" = "色"; +"colored" = "色付き"; /* server test step */ "Compare file" = "ファイルを比較"; @@ -693,6 +783,9 @@ /* No comment provided by engineer. */ "complete" = "完了"; +/* No comment provided by engineer. */ +"Completed" = "完了"; + /* No comment provided by engineer. */ "Configure ICE servers" = "ICEサーバを設定"; @@ -717,9 +810,15 @@ /* No comment provided by engineer. */ "Connect incognito" = "シークレットモードで接続"; +/* No comment provided by engineer. */ +"Connect to desktop" = "デスクトップに接続"; + /* No comment provided by engineer. */ "connect to SimpleX Chat developers." = "SimpleX Chat 開発者に接続します。"; +/* No comment provided by engineer. */ +"Connect to your friends faster." = "友達ともっと速くつながりましょう。"; + /* No comment provided by engineer. */ "Connect via link" = "リンク経由で接続"; @@ -729,9 +828,24 @@ /* No comment provided by engineer. */ "connected" = "接続中"; +/* No comment provided by engineer. */ +"Connected" = "接続中"; + +/* No comment provided by engineer. */ +"Connected desktop" = "デスクトップに接続済"; + +/* No comment provided by engineer. */ +"Connected servers" = "接続中のサーバ"; + +/* No comment provided by engineer. */ +"Connected to desktop" = "デスクトップに接続済"; + /* No comment provided by engineer. */ "connecting" = "接続待ち"; +/* No comment provided by engineer. */ +"Connecting" = "接続待ち"; + /* No comment provided by engineer. */ "connecting (accepted)" = "接続待ち (承諾済み)"; @@ -753,12 +867,21 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "サーバーに接続中… (エラー: %@)"; -/* chat list item title */ +/* No comment provided by engineer. */ +"Connecting to contact, please wait or check later!" = "連絡先に接続中です。しばらくお待ちいただくか、後で確認してください!"; + +/* No comment provided by engineer. */ +"Connecting to desktop" = "デスクトップに接続中"; + +/* No comment provided by engineer. */ "connecting…" = "接続待ち…"; /* No comment provided by engineer. */ "Connection" = "接続"; +/* No comment provided by engineer. */ +"Connection and servers status." = "接続とサーバーのステータス。"; + /* No comment provided by engineer. */ "Connection error" = "接続エラー"; @@ -771,6 +894,9 @@ /* No comment provided by engineer. */ "Connection request sent!" = "接続リクエストを送信しました!"; +/* No comment provided by engineer. */ +"Connection terminated" = "接続停止"; + /* No comment provided by engineer. */ "Connection timeout" = "接続タイムアウト"; @@ -795,9 +921,6 @@ /* notification */ "Contact is connected" = "連絡先は接続中"; -/* No comment provided by engineer. */ -"Contact is not connected yet!" = "連絡先がまだ繋がってません!"; - /* No comment provided by engineer. */ "Contact name" = "連絡先の名前"; @@ -813,7 +936,7 @@ /* No comment provided by engineer. */ "Continue" = "続ける"; -/* chat item action */ +/* No comment provided by engineer. */ "Copy" = "コピー"; /* No comment provided by engineer. */ @@ -822,9 +945,6 @@ /* No comment provided by engineer. */ "Create" = "作成"; -/* No comment provided by engineer. */ -"Create an address to let people connect with you." = "人とつながるためのアドレスを作成する。"; - /* server test step */ "Create file" = "ファイルを作成"; @@ -837,6 +957,9 @@ /* No comment provided by engineer. */ "Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" = "[デスクトップアプリ](https://simplex.chat/downloads/)で新しいプロファイルを作成します。 💻"; +/* No comment provided by engineer. */ +"Create profile" = "プロフィールを作成する"; + /* server test step */ "Create queue" = "キューの作成"; @@ -849,9 +972,6 @@ /* No comment provided by engineer. */ "Create your profile" = "プロフィールを作成する"; -/* No comment provided by engineer. */ -"Created on %@" = "%@ によって作成されました"; - /* No comment provided by engineer. */ "creator" = "作成者"; @@ -870,9 +990,15 @@ /* No comment provided by engineer. */ "Custom time" = "カスタム時間"; +/* No comment provided by engineer. */ +"Customize theme" = "カスタムテーマ"; + /* No comment provided by engineer. */ "Dark" = "ダークモード"; +/* No comment provided by engineer. */ +"Dark mode colors" = "ダークモードカラー"; + /* No comment provided by engineer. */ "Database downgrade" = "データーベースのダウングレード"; @@ -933,13 +1059,17 @@ /* time unit */ "days" = "日"; +/* No comment provided by engineer. */ +"Debug delivery" = "配信のデバッグ"; + /* No comment provided by engineer. */ "Decentralized" = "分散型"; /* message decrypt error item */ "Decryption error" = "復号化エラー"; -/* pref value */ +/* delete after time +pref value */ "default (%@)" = "デフォルト (%@)"; /* No comment provided by engineer. */ @@ -948,7 +1078,8 @@ /* No comment provided by engineer. */ "default (yes)" = "デフォルト(はい)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "削除"; /* No comment provided by engineer. */ @@ -963,12 +1094,6 @@ /* No comment provided by engineer. */ "Delete all files" = "ファイルを全て削除"; -/* No comment provided by engineer. */ -"Delete archive" = "アーカイブを削除"; - -/* No comment provided by engineer. */ -"Delete chat archive?" = "チャットのアーカイブを削除しますか?"; - /* No comment provided by engineer. */ "Delete chat profile" = "チャットのプロフィールを削除する"; @@ -981,9 +1106,6 @@ /* No comment provided by engineer. */ "Delete contact" = "連絡先を削除"; -/* No comment provided by engineer. */ -"Delete Contact" = "連絡先を削除"; - /* No comment provided by engineer. */ "Delete database" = "データベースを削除"; @@ -1023,7 +1145,7 @@ /* No comment provided by engineer. */ "Delete message?" = "メッセージを削除しますか?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "メッセージを削除"; /* No comment provided by engineer. */ @@ -1035,9 +1157,6 @@ /* No comment provided by engineer. */ "Delete old database?" = "古いデータベースを削除しますか?"; -/* No comment provided by engineer. */ -"Delete pending connection" = "確認待ちの接続を削除"; - /* No comment provided by engineer. */ "Delete pending connection?" = "接続待ちの接続を削除しますか?"; @@ -1074,9 +1193,15 @@ /* No comment provided by engineer. */ "Description" = "説明"; +/* No comment provided by engineer. */ +"Desktop devices" = "デスクトップ機器"; + /* No comment provided by engineer. */ "Develop" = "開発"; +/* No comment provided by engineer. */ +"Developer options" = "開発者向けの設定"; + /* No comment provided by engineer. */ "Developer tools" = "開発ツール"; @@ -1102,7 +1227,7 @@ "Direct messages" = "ダイレクトメッセージ"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "このグループではメンバー間のダイレクトメッセージが使用禁止です。"; +"Direct messages between members are prohibited." = "このグループではメンバー間のダイレクトメッセージが使用禁止です。"; /* No comment provided by engineer. */ "Disable (keep overrides)" = "無効にする(設定の優先を維持)"; @@ -1126,7 +1251,7 @@ "Disappearing messages are prohibited in this chat." = "このチャットでは消えるメッセージが使用禁止です。"; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "このグループでは消えるメッセージが使用禁止です。"; +"Disappearing messages are prohibited." = "このグループでは消えるメッセージが使用禁止です。"; /* No comment provided by engineer. */ "Disappears at" = "に消えます"; @@ -1147,7 +1272,7 @@ "Do NOT use SimpleX for emergency calls." = "緊急通報にSimpleXを使用しないでください。"; /* No comment provided by engineer. */ -"Don't create address" = "アドレスを作成しないでください"; +"Don't create address" = "アドレスを作成しない"; /* No comment provided by engineer. */ "Don't enable" = "有効にしない"; @@ -1185,7 +1310,7 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "有効にする(設定の優先を維持)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "自動メッセージ削除を有効にしますか?"; /* No comment provided by engineer. */ @@ -1320,9 +1445,6 @@ /* No comment provided by engineer. */ "Error accepting contact request" = "連絡先リクエストの承諾にエラー発生"; -/* No comment provided by engineer. */ -"Error accessing database file" = "データベースファイルへのアクセスエラー"; - /* No comment provided by engineer. */ "Error adding member(s)" = "メンバー追加にエラー発生"; @@ -1362,9 +1484,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "接続の削除エラー"; -/* No comment provided by engineer. */ -"Error deleting contact" = "連絡先の削除にエラー発生"; - /* No comment provided by engineer. */ "Error deleting database" = "データベースの削除にエラー発生"; @@ -1392,18 +1511,12 @@ /* No comment provided by engineer. */ "Error joining group" = "グループ参加にエラー発生"; -/* No comment provided by engineer. */ -"Error loading %@ servers" = "%@ サーバーのロード中にエラーが発生"; - -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "ファイル受信にエラー発生"; /* No comment provided by engineer. */ "Error removing member" = "メンバー除名にエラー発生"; -/* No comment provided by engineer. */ -"Error saving %@ servers" = "%@ サーバの保存エラー"; - /* No comment provided by engineer. */ "Error saving group profile" = "グループのプロフィール保存にエラー発生"; @@ -1434,7 +1547,7 @@ /* No comment provided by engineer. */ "Error stopping chat" = "チャット停止にエラー発生"; -/* No comment provided by engineer. */ +/* alertTitle */ "Error switching profile!" = "プロフィール切り替えにエラー発生!"; /* No comment provided by engineer. */ @@ -1455,7 +1568,9 @@ /* No comment provided by engineer. */ "Error: " = "エラー : "; -/* No comment provided by engineer. */ +/* alert message +file error text +snd error text */ "Error: %@" = "エラー : %@"; /* No comment provided by engineer. */ @@ -1467,9 +1582,6 @@ /* No comment provided by engineer. */ "Even when disabled in the conversation." = "会話中に無効になっている場合でも。"; -/* No comment provided by engineer. */ -"event happened" = "イベント発生"; - /* No comment provided by engineer. */ "Exit without saving" = "保存せずに閉じる"; @@ -1491,7 +1603,7 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "送信者がオンラインになるまでの待ち時間がなく、速い!"; -/* No comment provided by engineer. */ +/* swipe action */ "Favorite" = "お気に入り"; /* No comment provided by engineer. */ @@ -1513,7 +1625,7 @@ "Files and media" = "ファイルとメディア"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "このグループでは、ファイルとメディアは禁止されています。"; +"Files and media are prohibited." = "このグループでは、ファイルとメディアは禁止されています。"; /* No comment provided by engineer. */ "Files and media prohibited!" = "ファイルとメディアは禁止されています!"; @@ -1557,9 +1669,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "フルネーム (任意):"; -/* No comment provided by engineer. */ -"Full name:" = "フルネーム:"; - /* No comment provided by engineer. */ "Fully re-implemented - work in background!" = "完全に再実装されました - バックグラウンドで動作します!"; @@ -1599,24 +1708,6 @@ /* No comment provided by engineer. */ "Group links" = "グループのリンク"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "グループメンバーはメッセージへのリアクションを追加できます。"; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "グループのメンバーがメッセージを完全削除することができます。"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "グループのメンバーがダイレクトメッセージを送信できます。"; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "グループのメンバーが消えるメッセージを送信できます。"; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "グループメンバーはファイルやメディアを送信できます。"; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "グループのメンバーが音声メッセージを送信できます。"; - /* notification */ "Group message:" = "グループメッセージ:"; @@ -1674,9 +1765,6 @@ /* time unit */ "hours" = "時間"; -/* No comment provided by engineer. */ -"How it works" = "技術の説明"; - /* No comment provided by engineer. */ "How SimpleX works" = "SimpleX の仕組み"; @@ -1717,7 +1805,7 @@ "Immediately" = "即座に"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "スパムや悪質送信を防止"; +"Immune to spam" = "スパムや悪質送信を防止"; /* No comment provided by engineer. */ "Import" = "読み込む"; @@ -1786,10 +1874,10 @@ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "インストール [ターミナル用SimpleX Chat](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"Instant push notifications will be hidden!\n" = "インスタントプッシュ通知は非表示になります!\n"; +"Instant" = "即時"; /* No comment provided by engineer. */ -"Instantly" = "すぐに"; +"Instant push notifications will be hidden!\n" = "インスタントプッシュ通知は非表示になります!\n"; /* No comment provided by engineer. */ "Interface" = "インターフェース"; @@ -1806,7 +1894,7 @@ /* invalid chat item */ "invalid data" = "無効なデータ"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "無効なサーバアドレス!"; /* item status text */ @@ -1852,7 +1940,7 @@ "Irreversible message deletion is prohibited in this chat." = "このチャットではメッセージの完全削除が使用禁止です。"; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "このグループではメッセージの完全削除が使用禁止です。"; +"Irreversible message deletion is prohibited." = "このグループではメッセージの完全削除が使用禁止です。"; /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "これにより単一のチャット プロファイル内で、データを共有せずに多数の匿名の接続をすることができます。"; @@ -1875,7 +1963,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "日本語UI"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "参加"; /* No comment provided by engineer. */ @@ -1905,7 +1993,7 @@ /* No comment provided by engineer. */ "Learn more" = "さらに詳しく"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "脱退"; /* No comment provided by engineer. */ @@ -1935,9 +2023,6 @@ /* No comment provided by engineer. */ "Live messages" = "ライブメッセージ"; -/* No comment provided by engineer. */ -"Local" = "自分のみ"; - /* No comment provided by engineer. */ "Local name" = "ローカルネーム"; @@ -1950,24 +2035,15 @@ /* No comment provided by engineer. */ "Lock mode" = "ロックモード"; -/* No comment provided by engineer. */ -"Make a private connection" = "プライベートな接続をする"; - /* No comment provided by engineer. */ "Make one message disappear" = "メッセージを1つ消す"; /* No comment provided by engineer. */ "Make profile private!" = "プロフィールを非表示にできます!"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "%@ サーバー アドレスが正しい形式で、行が区切られており、重複していないことを確認してください (%@)。"; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "WebRTC ICEサーバのアドレスを正しく1行ずつに分けて、重複しないように、形式もご確認ください。"; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "多くの人が次のような質問をしました: *SimpleX にユーザー識別子がない場合、どうやってメッセージを配信できるのですか?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "全員に対して削除済みマークを付ける"; @@ -2004,6 +2080,24 @@ /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "メンバーをグループから除名する (※元に戻せません※)!"; +/* No comment provided by engineer. */ +"Members can add message reactions." = "グループメンバーはメッセージへのリアクションを追加できます。"; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "グループのメンバーがメッセージを完全削除することができます。(24時間)"; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "グループのメンバーがダイレクトメッセージを送信できます。"; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "グループのメンバーが消えるメッセージを送信できます。"; + +/* No comment provided by engineer. */ +"Members can send files and media." = "グループメンバーはファイルやメディアを送信できます。"; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "グループのメンバーが音声メッセージを送信できます。"; + /* item status text */ "Message delivery error" = "メッセージ送信エラー"; @@ -2017,7 +2111,7 @@ "Message reactions are prohibited in this chat." = "このチャットではメッセージへのリアクションは禁止されています。"; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "このグループではメッセージへのリアクションは禁止されています。"; +"Message reactions are prohibited." = "このグループではメッセージへのリアクションは禁止されています。"; /* notification */ "message received" = "メッセージを受信"; @@ -2031,6 +2125,15 @@ /* No comment provided by engineer. */ "Messages & files" = "メッセージ & ファイル"; +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "メッセージ、ファイル、通話は、前方秘匿性、否認可能性および侵入復元性を備えた**エンドツーエンドの暗号化**によって保護されます。"; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "メッセージ、ファイル、通話は、前方秘匿性、否認可能性および侵入復元性を備えた**耐量子E2E暗号化**によって保護されます。"; + +/* No comment provided by engineer. */ +"Migrate from another device" = "別の端末から移行"; + /* No comment provided by engineer. */ "Migrating database archive…" = "データベースのアーカイブを移行しています…"; @@ -2044,7 +2147,7 @@ "Migration is completed" = "移行が完了しました"; /* No comment provided by engineer. */ -"Migrations: %@" = "移行: %@"; +"Migrations:" = "移行:"; /* time unit */ "minutes" = "分"; @@ -2076,19 +2179,16 @@ /* item status description */ "Most likely this connection is deleted." = "おそらく、この接続は削除されています。"; -/* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "恐らくこの連絡先があなたとの接続を削除しました。"; - /* No comment provided by engineer. */ "Multiple chat profiles" = "複数チャットのプロフィール"; -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "ミュート"; /* No comment provided by engineer. */ "Muted when inactive!" = "非アクティブ時はミュート!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "名前"; /* No comment provided by engineer. */ @@ -2100,7 +2200,7 @@ /* No comment provided by engineer. */ "Network status" = "ネットワーク状況"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "一度も"; /* notification */ @@ -2109,9 +2209,6 @@ /* notification */ "New contact:" = "新しい連絡先:"; -/* No comment provided by engineer. */ -"New database archive" = "新しいデータベースのアーカイブ"; - /* No comment provided by engineer. */ "New desktop app!" = "新しいデスクトップアプリ!"; @@ -2172,12 +2269,18 @@ /* No comment provided by engineer. */ "No permission to record voice message" = "音声メッセージを録音する権限がありません"; +/* No comment provided by engineer. */ +"No push server" = "自分のみ"; + /* No comment provided by engineer. */ "No received or sent files" = "送受信済みのファイルがありません"; /* copied message info in history */ "no text" = "テキストなし"; +/* No comment provided by engineer. */ +"No user identifiers." = "世界初のユーザーIDのないプラットフォーム|設計も元からプライベート。"; + /* No comment provided by engineer. */ "Notifications" = "通知"; @@ -2191,11 +2294,11 @@ "observer" = "オブザーバー"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "オフ"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "オフ"; /* feature offered item */ @@ -2204,15 +2307,12 @@ /* feature offered item */ "offered %@: %@" = "提供された %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "OK"; /* No comment provided by engineer. */ "Old database" = "古いデータベース"; -/* No comment provided by engineer. */ -"Old database archive" = "過去のデータベースアーカイブ"; - /* group pref value */ "on" = "オン"; @@ -2220,16 +2320,16 @@ "One-time invitation link" = "使い捨ての招待リンク"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "接続にオニオンのホストが必要となります。VPN を有効にする必要があります。"; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "接続にオニオンのホストが必要となります。\nVPN を有効にする必要があります。"; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "オニオンのホストが利用可能時に使われます。VPN を有効にする必要があります。"; +"Onion hosts will be used when available.\nRequires compatible VPN." = "オニオンのホストが利用可能時に使われます。\nVPN を有効にする必要があります。"; /* No comment provided by engineer. */ "Onion hosts will not be used." = "オニオンのホストが使われません。"; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "**2 レイヤーのエンドツーエンド暗号化**を使用して送信されたユーザー プロファイル、連絡先、グループ、メッセージを保存できるのはクライアント デバイスのみです。"; +"Only client devices store user profiles, contacts, groups, and messages." = "**2 レイヤーのエンドツーエンド暗号化**を使用して送信されたユーザー プロファイル、連絡先、グループ、メッセージを保存できるのはクライアント デバイスのみです。"; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "グループ設定を変えられるのはグループのオーナーだけです。"; @@ -2244,7 +2344,7 @@ "Only you can add message reactions." = "メッセージへのリアクションを追加できるのは、あなただけです。"; /* No comment provided by engineer. */ -"Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours)" = "メッセージの完全削除はあなたにしかできません (あなたの連絡先は削除対象とすることができます)。"; +"Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours)" = "メッセージの完全削除はあなたにしかできません (あなたの連絡先は削除対象とすることができます)。(24時間)"; /* No comment provided by engineer. */ "Only you can make calls." = "自分からのみ通話ができます。"; @@ -2259,7 +2359,7 @@ "Only your contact can add message reactions." = "メッセージへのリアクションを追加できるのは連絡先だけです。"; /* No comment provided by engineer. */ -"Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours)" = "メッセージを完全削除できるのはあなたの連絡相手だけです (あなたは削除対象とすることができます)。"; +"Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours)" = "メッセージを完全削除できるのはあなたの連絡相手だけです (あなたは削除対象とすることができます)。(24時間)"; /* No comment provided by engineer. */ "Only your contact can make calls." = "連絡先からのみ通話ができます。"; @@ -2270,7 +2370,7 @@ /* No comment provided by engineer. */ "Only your contact can send voice messages." = "音声メッセージを送れるのはあなたの連絡相手だけです。"; -/* No comment provided by engineer. */ +/* alert action */ "Open" = "開く"; /* No comment provided by engineer. */ @@ -2282,12 +2382,6 @@ /* No comment provided by engineer. */ "Open Settings" = "設定を開く"; -/* authentication reason */ -"Open user profiles" = "ユーザープロフィールを開く"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "プロトコル技術とコードはオープンソースで、どなたでもご自分のサーバを運用できます。"; - /* member role */ "owner" = "オーナー"; @@ -2316,10 +2410,7 @@ "peer-to-peer" = "P2P"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "あなたと繋がることができるのは、あなたからリンクを頂いた方のみです。"; - -/* No comment provided by engineer. */ -"Periodically" = "定期的に"; +"Periodic" = "定期的に"; /* message decrypt error item */ "Permanent decryption error" = "永続的な復号化エラー"; @@ -2375,9 +2466,6 @@ /* No comment provided by engineer. */ "Preserve the last message draft, with attachments." = "添付を含めて、下書きを保存する。"; -/* No comment provided by engineer. */ -"Preset server" = "プレセットサーバ"; - /* No comment provided by engineer. */ "Preset server address" = "プレセットサーバのアドレス"; @@ -2393,6 +2481,9 @@ /* No comment provided by engineer. */ "Private filenames" = "プライベートなファイル名"; +/* name of notes to self */ +"Private notes" = "プライベートノート"; + /* No comment provided by engineer. */ "Profile and server connections" = "プロフィールとサーバ接続"; @@ -2402,7 +2493,7 @@ /* No comment provided by engineer. */ "Profile password" = "プロフィールのパスワード"; -/* No comment provided by engineer. */ +/* alert message */ "Profile update will be sent to your contacts." = "連絡先にプロフィール更新のお知らせが届きます。"; /* No comment provided by engineer. */ @@ -2450,23 +2541,20 @@ /* chat item menu */ "React…" = "反応する…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "読む"; /* No comment provided by engineer. */ "Read more" = "続きを読む"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "詳しくは[ユーザーガイド](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)をご覧ください。"; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "詳しくは[ユーザーガイド](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)をご覧ください。"; /* No comment provided by engineer. */ "Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "詳しくは[ユーザーガイド](https://simplex.chat/docs/guide/readme.html#connect-to-friends)をご覧ください。"; /* No comment provided by engineer. */ -"Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "詳しくは[GitHubリポジトリ](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)をご覧ください。"; - -/* No comment provided by engineer. */ -"Read more in our GitHub repository." = "GitHubリポジトリで詳細をご確認ください。"; +"Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "詳しくは[GitHubリポジトリ](https://github.com/simplex-chat/simplex-chat#readme)をご覧ください。"; /* No comment provided by engineer. */ "received answer…" = "回答を受け取りました…"; @@ -2513,7 +2601,8 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "電池使用量低減"; -/* reject incoming call via notification */ +/* reject incoming call via notification +swipe action */ "Reject" = "拒否"; /* No comment provided by engineer. */ @@ -2597,9 +2686,6 @@ /* chat item action */ "Reveal" = "開示する"; -/* No comment provided by engineer. */ -"Revert" = "元に戻す"; - /* No comment provided by engineer. */ "Revoke" = "取り消す"; @@ -2615,13 +2701,14 @@ /* No comment provided by engineer. */ "Run chat" = "チャット起動"; -/* chat item action */ +/* alert button +chat item action */ "Save" = "保存"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "保存(連絡先に通知)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "保存して、連絡先にに知らせる"; /* No comment provided by engineer. */ @@ -2630,12 +2717,6 @@ /* No comment provided by engineer. */ "Save and update group profile" = "グループプロファイルの保存と更新"; -/* No comment provided by engineer. */ -"Save archive" = "アーカイブを保存"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "自動受け入れ設定を保存する"; - /* No comment provided by engineer. */ "Save group profile" = "グループプロフィールの保存"; @@ -2645,7 +2726,7 @@ /* No comment provided by engineer. */ "Save passphrase in Keychain" = "パスフレーズをキーチェーンに保存"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "この設定でよろしいですか?"; /* No comment provided by engineer. */ @@ -2654,12 +2735,9 @@ /* No comment provided by engineer. */ "Save servers" = "サーバを保存"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "サーバを保存しますか?"; -/* No comment provided by engineer. */ -"Save settings?" = "設定を保存しますか?"; - /* No comment provided by engineer. */ "Save welcome message?" = "ウェルカムメッセージを保存しますか?"; @@ -2702,7 +2780,7 @@ /* chat item text */ "security code changed" = "セキュリティコードが変更されました"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "選択"; /* No comment provided by engineer. */ @@ -2723,9 +2801,6 @@ /* No comment provided by engineer. */ "Send a live message - it will update for the recipient(s) as you type it" = "ライブメッセージを送信 (入力しながら宛先の画面で更新される)"; -/* No comment provided by engineer. */ -"Send direct message" = "ダイレクトメッセージを送信"; - /* No comment provided by engineer. */ "Send direct message to connect" = "ダイレクトメッセージを送信して接続する"; @@ -2741,16 +2816,13 @@ /* No comment provided by engineer. */ "Send notifications" = "通知を送信する"; -/* No comment provided by engineer. */ -"Send notifications:" = "通知を送信する:"; - /* No comment provided by engineer. */ "Send questions and ideas" = "質問やアイデアを送る"; /* No comment provided by engineer. */ "Send them from gallery or custom keyboards." = "ギャラリーまたはカスタム キーボードから送信します。"; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "送信者がファイル転送をキャンセルしました。"; /* No comment provided by engineer. */ @@ -2816,7 +2888,8 @@ /* No comment provided by engineer. */ "Settings" = "設定"; -/* chat item action */ +/* alert action +chat item action */ "Share" = "共有する"; /* No comment provided by engineer. */ @@ -2825,7 +2898,7 @@ /* No comment provided by engineer. */ "Share address" = "アドレスを共有する"; -/* No comment provided by engineer. */ +/* alert title */ "Share address with contacts?" = "アドレスを連絡先と共有しますか?"; /* No comment provided by engineer. */ @@ -2867,7 +2940,7 @@ /* simplex link type */ "SimpleX group link" = "SimpleXグループリンク"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "SimpleXリンク"; /* No comment provided by engineer. */ @@ -2897,9 +2970,6 @@ /* No comment provided by engineer. */ "Small groups (max 20)" = "小グループ(最大20名)"; -/* No comment provided by engineer. */ -"SMP servers" = "SMPサーバ"; - /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "インポート中に致命的でないエラーが発生しました - 詳細はチャットコンソールを参照してください。"; @@ -2918,9 +2988,6 @@ /* No comment provided by engineer. */ "Stop" = "停止"; -/* No comment provided by engineer. */ -"Stop chat to enable database actions" = "チャットを停止してデータベースアクションを有効にします"; - /* No comment provided by engineer. */ "Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "データベースのエクスポート、読み込み、削除するにはチャットを閉じてからです。チャットを閉じると送受信ができなくなります。"; @@ -2936,10 +3003,10 @@ /* No comment provided by engineer. */ "Stop sending file?" = "ファイルの送信を停止しますか?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "共有を停止"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "アドレスの共有を停止しますか?"; /* authentication reason */ @@ -2975,9 +3042,6 @@ /* No comment provided by engineer. */ "Tap to join incognito" = "タップしてシークレットモードで参加"; -/* No comment provided by engineer. */ -"Tap to start a new chat" = "タップして新しいチャットを始める"; - /* No comment provided by engineer. */ "TCP connection timeout" = "TCP接続タイムアウト"; @@ -2999,7 +3063,7 @@ /* No comment provided by engineer. */ "Test servers" = "テストサーバ"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "テストは失敗しました!"; /* No comment provided by engineer. */ @@ -3011,9 +3075,6 @@ /* No comment provided by engineer. */ "Thanks to the users – contribute via Weblate!" = "ユーザーに感謝します – Weblate 経由で貢献してください!"; -/* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "世界初のユーザーIDのないプラットフォーム|設計も元からプライベート。"; - /* No comment provided by engineer. */ "The app can notify you when you receive messages or contact requests - please open settings to enable." = "アプリは、メッセージや連絡先のリクエストを受信したときに通知することができます - 設定を開いて有効にしてください。"; @@ -3032,6 +3093,9 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "暗号化は機能しており、新しい暗号化への同意は必要ありません。接続エラーが発生する可能性があります!"; +/* No comment provided by engineer. */ +"The future of messaging" = "次世代のプライバシー・メッセンジャー"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "以前のメッセージとハッシュ値が異なります。"; @@ -3044,14 +3108,11 @@ /* No comment provided by engineer. */ "The message will be marked as moderated for all members." = "メッセージは、すべてのメンバーに対してモデレートされたものとして表示されます。"; -/* No comment provided by engineer. */ -"The next generation of private messaging" = "次世代のプライバシー・メッセンジャー"; - /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "古いデータベースは移行時に削除されなかったので、削除することができます。"; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "プロフィールは連絡先にしか共有されません。"; +"Your profile is stored on your device and only shared with your contacts." = "プロフィールは連絡先にしか共有されません。"; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "長らくお待たせしました! ✅"; @@ -3062,9 +3123,6 @@ /* No comment provided by engineer. */ "The servers for new connections of your current chat profile **%@**." = "現在のチャットプロフィールの新しい接続のサーバ **%@**。"; -/* No comment provided by engineer. */ -"Theme" = "テーマ"; - /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "これらの設定は現在のプロファイル **%@** 用です。"; @@ -3098,15 +3156,15 @@ /* No comment provided by engineer. */ "To make a new connection" = "新規に接続する場合"; -/* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "プライバシーを保護するために、SimpleX には、他のすべてのプラットフォームで使用されるユーザー ID の代わりに、連絡先ごとに個別のメッセージ キューの識別子があります。"; - /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "時間帯を漏らさないために、画像と音声ファイルはUTCを使います。"; /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "あなたのデータを守るために、SimpleXロックをオンにしてください。\nオンにするには、認証ステップが行われます。"; +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "プライバシーを保護するために、SimpleX には、他のすべてのプラットフォームで使用されるユーザー ID の代わりに、連絡先ごとに個別のメッセージ キューの識別子があります。"; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "音声メッセージを録音する場合は、マイクの使用を許可してください。"; @@ -3137,13 +3195,10 @@ /* No comment provided by engineer. */ "Unable to record voice message" = "音声メッセージを録音できません"; -/* item status description */ -"Unexpected error: %@" = "予期しないエラー: %@"; - /* No comment provided by engineer. */ "Unexpected migration state" = "予期しない移行状態"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "お気に入りを取り消す。"; /* No comment provided by engineer. */ @@ -3182,36 +3237,27 @@ /* authentication reason */ "Unlock app" = "アプリのロック解除"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "ミュート解除"; -/* No comment provided by engineer. */ +/* swipe action */ "Unread" = "未読"; /* No comment provided by engineer. */ "Update" = "更新"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = ".onionのホスト設定を更新しますか?"; - /* No comment provided by engineer. */ "Update database passphrase" = "データベースのパスフレーズを更新"; /* No comment provided by engineer. */ "Update network settings?" = "ネットワーク設定を更新しますか?"; -/* No comment provided by engineer. */ -"Update transport isolation mode?" = "トランスポート隔離モードを更新しますか?"; - /* rcv group event chat item */ "updated group profile" = "グループプロフィールを更新しました"; /* No comment provided by engineer. */ "Updating settings will re-connect the client to all servers." = "設定を更新すると、全サーバにクライントの再接続が行われます。"; -/* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "設定を更新すると、全サーバにクライントの再接続が行われます。"; - /* No comment provided by engineer. */ "Upgrade and open chat" = "アップグレードしてチャットを開く"; @@ -3242,12 +3288,6 @@ /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "SimpleX チャット サーバーを使用しますか?"; -/* No comment provided by engineer. */ -"User profile" = "ユーザープロフィール"; - -/* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = ".onionホストを使用するには、互換性のあるVPNプロバイダーが必要です。"; - /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "SimpleX チャット サーバーを使用する。"; @@ -3303,7 +3343,7 @@ "Voice messages are prohibited in this chat." = "このチャットでは音声メッセージが使用禁止です。"; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "このグループでは音声メッセージが使用禁止です。"; +"Voice messages are prohibited." = "このグループでは音声メッセージが使用禁止です。"; /* No comment provided by engineer. */ "Voice messages prohibited!" = "音声メッセージは使用禁止です!"; @@ -3347,9 +3387,6 @@ /* No comment provided by engineer. */ "When available" = "利用可能時に"; -/* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "接続が要求されたら、それを受け入れるか拒否するかを選択できます。"; - /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "連絡相手にシークレットモードのプロフィールを共有すると、その連絡相手に招待されたグループでも同じプロフィールが使われます。"; @@ -3362,15 +3399,9 @@ /* No comment provided by engineer. */ "Wrong passphrase!" = "パスフレーズが違います!"; -/* No comment provided by engineer. */ -"XFTP servers" = "XFTPサーバ"; - /* pref value */ "yes" = "はい"; -/* No comment provided by engineer. */ -"You" = "あなた"; - /* No comment provided by engineer. */ "You accepted connection" = "接続を承認しました"; @@ -3410,8 +3441,11 @@ /* No comment provided by engineer. */ "You can hide or mute a user profile - swipe it to the right." = "ユーザープロファイルを右にスワイプすると、非表示またはミュートにすることができます。"; +/* No comment provided by engineer. */ +"You can make it visible to your SimpleX contacts via Settings." = "設定でSimpleXの連絡先に表示させることができます。"; + /* notification body */ -"You can now send messages to %@" = "%@ にメッセージを送信できるようになりました"; +"You can now chat with %@" = "%@ にメッセージを送信できるようになりました"; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "設定からロック画面の通知プレビューを設定できます。"; @@ -3422,9 +3456,6 @@ /* No comment provided by engineer. */ "You can share this address with your contacts to let them connect with **%@**." = "このアドレスを連絡先と共有して、**%@** に接続できるようにすることができます。"; -/* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "アドレスをリンクやQRコードとして共有することで、誰でも接続することができます。"; - /* No comment provided by engineer. */ "You can start chat via app Settings / Database or by restarting the app" = "アプリの設定/データベースから、またはアプリを再起動することでチャットを開始できます"; @@ -3449,14 +3480,11 @@ /* snd group event chat item */ "you changed role of %@ to %@" = "%1$@ の役割を %2$@ に変更しました"; -/* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "あなたはメッセージの受信に使用するサーバーを制御し、連絡先はあなたがメッセージの送信に使用するサーバーを使用することができます。"; - /* No comment provided by engineer. */ "You could not be verified; please try again." = "確認できませんでした。 もう一度お試しください。"; /* No comment provided by engineer. */ -"You have no chats" = "あなたはチャットがありません"; +"You decide who can connect." = "あなたと繋がることができるのは、あなたからリンクを頂いた方のみです。"; /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "アプリ起動時にパスフレーズを入力しなければなりません。端末に保存されてません。"; @@ -3524,9 +3552,6 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "シークレットモードのプロフィールでこのグループに参加しています。メインのプロフィールを守るために、招待することができません"; -/* No comment provided by engineer. */ -"Your %@ servers" = "あなたの %@ サーバー"; - /* No comment provided by engineer. */ "Your calls" = "あなたの通話"; @@ -3539,9 +3564,6 @@ /* No comment provided by engineer. */ "Your chat profiles" = "あなたのチャットプロフィール"; -/* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "接続を完了するには、連絡相手がオンラインになる必要があります。\nこの接続をキャンセルして、連絡先を削除をすることもできます (後でやり直すこともできます)。"; - /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "連絡先が現在サポートされている最大サイズ (%@) より大きいファイルを送信しました。"; @@ -3570,7 +3592,7 @@ "Your profile **%@** will be shared." = "あなたのプロファイル **%@** が共有されます。"; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "プロフィールはデバイスに保存され、連絡先とのみ共有されます。\nSimpleX サーバーはあなたのプロファイルを参照できません。"; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "プロフィールはデバイスに保存され、連絡先とのみ共有されます。 SimpleX サーバーはあなたのプロファイルを参照できません。"; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "あなたのプロフィール、連絡先、送信したメッセージがご自分の端末に保存されます。"; @@ -3578,9 +3600,6 @@ /* No comment provided by engineer. */ "Your random profile" = "あなたのランダム・プロフィール"; -/* No comment provided by engineer. */ -"Your server" = "あなたのサーバ"; - /* No comment provided by engineer. */ "Your server address" = "あなたのサーバアドレス"; @@ -3590,9 +3609,3 @@ /* No comment provided by engineer. */ "Your SimpleX address" = "あなたのSimpleXアドレス"; -/* No comment provided by engineer. */ -"Your SMP servers" = "あなたのSMPサーバ"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "あなたのXFTPサーバ"; - diff --git a/apps/ios/nl.lproj/Localizable.strings b/apps/ios/nl.lproj/Localizable.strings index 7c4f487f99..232de56641 100644 --- a/apps/ios/nl.lproj/Localizable.strings +++ b/apps/ios/nl.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (kan gekopieerd worden)"; @@ -31,30 +16,15 @@ /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- spraakberichten tot 5 minuten.\n- aangepaste tijd om te verdwijnen.\n- bewerkingsgeschiedenis."; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 gekleurd!"; -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - /* No comment provided by engineer. */ "(new)" = "(nieuw)"; /* No comment provided by engineer. */ "(this device v%@)" = "(dit apparaat v%@)"; -/* No comment provided by engineer. */ -")" = ")"; - /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Bijdragen](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -65,10 +35,7 @@ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add contact**: to create a new invitation link, or connect via a link you received." = "**Contact toevoegen**: om een nieuwe uitnodigingslink aan te maken, of verbinding te maken via een link die u heeft ontvangen."; - -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Nieuw contact toevoegen**: om uw eenmalige QR-code of link voor uw contact te maken."; +"**Create 1-time link**: to create and share a new invitation link." = "**Contact toevoegen**: om een nieuwe uitnodigingslink aan te maken, of verbinding te maken via een link die u heeft ontvangen."; /* No comment provided by engineer. */ "**Create group**: to create a new group." = "**Groep aanmaken**: om een nieuwe groep aan te maken."; @@ -80,20 +47,29 @@ "**e2e encrypted** video call" = "**e2e versleuteld** video gesprek"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Meer privé**: bekijk elke 20 minuten nieuwe berichten. Apparaattoken wordt gedeeld met de SimpleX Chat-server, maar niet hoeveel contacten of berichten u heeft."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Meer privé**: bekijk elke 20 minuten nieuwe berichten. Apparaattoken wordt gedeeld met de SimpleX Chat-server, maar niet hoeveel contacten of berichten u heeft."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Meest privé**: gebruik geen SimpleX Chat-notificatie server, controleer berichten regelmatig op de achtergrond (afhankelijk van hoe vaak u de app gebruikt)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**Meest privé**: gebruik geen SimpleX Chat-notificatie server, controleer berichten regelmatig op de achtergrond (afhankelijk van hoe vaak u de app gebruikt)."; + +/* No comment provided by engineer. */ +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Let op**: als u dezelfde database op twee apparaten gebruikt, wordt de decodering van berichten van uw verbindingen verbroken, als veiligheidsmaatregel."; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Let op**: u kunt het wachtwoord NIET herstellen of wijzigen als u het kwijtraakt."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Aanbevolen**: apparaattoken en meldingen worden naar de SimpleX Chat-meldingsserver gestuurd, maar niet de berichtinhoud, -grootte of van wie het afkomstig is."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Aanbevolen**: apparaattoken en meldingen worden naar de SimpleX Chat-meldingsserver gestuurd, maar niet de berichtinhoud, -grootte of van wie het afkomstig is."; + +/* No comment provided by engineer. */ +"**Scan / Paste link**: to connect via a link you received." = "**Link scannen/plakken**: om verbinding te maken via een link die u hebt ontvangen."; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Waarschuwing**: voor directe push meldingen is een wachtwoord vereist dat is opgeslagen in de Keychain."; +/* No comment provided by engineer. */ +"**Warning**: the archive will be removed." = "**Waarschuwing**: het archief wordt verwijderd."; + /* No comment provided by engineer. */ "*bold*" = "\\*vetgedrukt*"; @@ -136,6 +112,9 @@ /* No comment provided by engineer. */ "%@ connected" = "%@ verbonden"; +/* No comment provided by engineer. */ +"%@ downloaded" = "%@ gedownload"; + /* notification title */ "%@ is connected!" = "%@ is verbonden!"; @@ -145,12 +124,21 @@ /* No comment provided by engineer. */ "%@ is verified" = "%@ is geverifieerd"; +/* No comment provided by engineer. */ +"%@ server" = "%@ server"; + /* No comment provided by engineer. */ "%@ servers" = "%@ servers"; +/* No comment provided by engineer. */ +"%@ uploaded" = "%@ geüpload"; + /* notification title */ "%@ wants to connect!" = "%@ wil verbinding maken!"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + /* No comment provided by engineer. */ "%@, %@ and %lld members" = "%@, %@ en %lld leden"; @@ -163,9 +151,24 @@ /* time interval */ "%d days" = "%d dagen"; +/* forward confirmation reason */ +"%d file(s) are still being downloaded." = "%d bestand(en) worden nog gedownload."; + +/* forward confirmation reason */ +"%d file(s) failed to download." = "%d bestand(en) konden niet worden gedownload."; + +/* forward confirmation reason */ +"%d file(s) were deleted." = "%d bestand(en) zijn verwijderd."; + +/* forward confirmation reason */ +"%d file(s) were not downloaded." = "%d bestand(en) zijn niet gedownload."; + /* time interval */ "%d hours" = "%d uren"; +/* alert title */ +"%d messages not forwarded" = "%d berichten niet doorgestuurd"; + /* time interval */ "%d min" = "%d min"; @@ -175,6 +178,9 @@ /* time interval */ "%d sec" = "%d sec"; +/* delete after time */ +"%d seconds(s)" = "%d seconden"; + /* integrity error chat item */ "%d skipped message(s)" = "%d overgeslagen bericht(en)"; @@ -217,9 +223,6 @@ /* No comment provided by engineer. */ "%lld new interface languages" = "%lld nieuwe interface-talen"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld seconde(n)"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld seconden"; @@ -265,7 +268,8 @@ /* No comment provided by engineer. */ "0s" = "0s"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "1 dag"; /* time interval */ @@ -274,12 +278,23 @@ /* No comment provided by engineer. */ "1 minute" = "1 minuut"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "1 maand"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "1 week"; +/* delete after time */ +"1 year" = "1 jaar"; + +/* No comment provided by engineer. */ +"1-time link" = "Eenmalige link"; + +/* No comment provided by engineer. */ +"1-time link can be used *with one contact only* - share in person or via any messenger." = "Eenmalige link die *slechts met één contactpersoon* kan worden gebruikt - deel persoonlijk of via een messenger."; + /* No comment provided by engineer. */ "5 minutes" = "5 minuten"; @@ -299,7 +314,7 @@ "A new random profile will be shared." = "Een nieuw willekeurig profiel wordt gedeeld."; /* No comment provided by engineer. */ -"A separate TCP connection will be used **for each chat profile you have in the app**." = "Er wordt een aparte TCP-verbinding gebruikt **voor elk chat profiel dat je in de app hebt**."; +"A separate TCP connection will be used **for each chat profile you have in the app**." = "Er wordt een aparte TCP-verbinding gebruikt **voor elk chatprofiel dat je in de app hebt**."; /* No comment provided by engineer. */ "A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "Er wordt een aparte TCP-verbinding gebruikt **voor elk contact en groepslid**.\n**Let op**: als u veel verbindingen heeft, kan uw batterij- en verkeersverbruik aanzienlijk hoger zijn en kunnen sommige verbindingen uitvallen."; @@ -314,10 +329,7 @@ "Abort changing address?" = "Adres wijziging afbreken?"; /* No comment provided by engineer. */ -"About SimpleX" = "Over SimpleX"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "Over SimpleX adres"; +"About operators" = "Over operatoren"; /* No comment provided by engineer. */ "About SimpleX Chat" = "Over SimpleX Chat"; @@ -326,47 +338,94 @@ "above, then choose:" = "hier boven, kies dan:"; /* No comment provided by engineer. */ -"Accent color" = "Accent kleur"; +"Accent" = "Accent"; /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "Accepteer"; +/* No comment provided by engineer. */ +"Accept conditions" = "Accepteer voorwaarden"; + /* No comment provided by engineer. */ "Accept connection request?" = "Accepteer contact"; /* notification body */ "Accept contact request from %@?" = "Accepteer contactverzoek van %@?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "Accepteer incognito"; /* call status */ "accepted call" = "geaccepteerde oproep"; +/* No comment provided by engineer. */ +"Accepted conditions" = "Geaccepteerde voorwaarden"; + +/* chat list item title */ +"accepted invitation" = "geaccepteerde uitnodiging"; + +/* No comment provided by engineer. */ +"Acknowledged" = "Erkend"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Bevestigingsfouten"; + +/* token status text */ +"Active" = "actief"; + +/* No comment provided by engineer. */ +"Active connections" = "Actieve verbindingen"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Voeg een adres toe aan uw profiel, zodat uw contacten het met andere mensen kunnen delen. Profiel update wordt naar uw contacten verzonden."; /* No comment provided by engineer. */ -"Add contact" = "Contact toevoegen"; +"Add friends" = "Vrienden toevoegen"; /* No comment provided by engineer. */ -"Add preset servers" = "Vooraf ingestelde servers toevoegen"; +"Add list" = "Lijst toevoegen"; /* No comment provided by engineer. */ "Add profile" = "Profiel toevoegen"; /* No comment provided by engineer. */ -"Add server…" = "Server toevoegen…"; +"Add server" = "Server toevoegen"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "Servers toevoegen door QR-codes te scannen."; +/* No comment provided by engineer. */ +"Add team members" = "Teamleden toevoegen"; + /* No comment provided by engineer. */ "Add to another device" = "Toevoegen aan een ander apparaat"; /* No comment provided by engineer. */ -"Add welcome message" = "Welkomst bericht toevoegen"; +"Add to list" = "Toevoegen aan lijst"; + +/* No comment provided by engineer. */ +"Add welcome message" = "Welkom bericht toevoegen"; + +/* No comment provided by engineer. */ +"Add your team members to the conversations." = "Voeg uw teamleden toe aan de gesprekken."; + +/* No comment provided by engineer. */ +"Added media & file servers" = "Media- en bestandsservers toegevoegd"; + +/* No comment provided by engineer. */ +"Added message servers" = "Berichtservers toegevoegd"; + +/* No comment provided by engineer. */ +"Additional accent" = "Extra accent"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Extra accent 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Extra secundair"; /* No comment provided by engineer. */ "Address" = "Adres"; @@ -374,33 +433,63 @@ /* No comment provided by engineer. */ "Address change will be aborted. Old receiving address will be used." = "Adres wijziging wordt afgebroken. Het oude ontvangstadres wordt gebruikt."; +/* No comment provided by engineer. */ +"Address or 1-time link?" = "Adres of eenmalige link?"; + +/* No comment provided by engineer. */ +"Address settings" = "Adres instellingen"; + /* member role */ "admin" = "Beheerder"; +/* feature role */ +"admins" = "beheerders"; + +/* No comment provided by engineer. */ +"Admins can block a member for all." = "Beheerders kunnen een lid voor iedereen blokkeren."; + /* No comment provided by engineer. */ "Admins can create the links to join groups." = "Beheerders kunnen de uitnodiging links naar groepen aanmaken."; /* No comment provided by engineer. */ "Advanced network settings" = "Geavanceerde netwerk instellingen"; +/* No comment provided by engineer. */ +"Advanced settings" = "Geavanceerde instellingen"; + /* chat item text */ "agreeing encryption for %@…" = "versleuteling overeenkomen voor %@…"; /* chat item text */ "agreeing encryption…" = "versleuteling overeenkomen…"; +/* No comment provided by engineer. */ +"All" = "alle"; + /* No comment provided by engineer. */ "All app data is deleted." = "Alle app-gegevens worden verwijderd."; /* No comment provided by engineer. */ "All chats and messages will be deleted - this cannot be undone!" = "Alle chats en berichten worden verwijderd, dit kan niet ongedaan worden gemaakt!"; +/* alert message */ +"All chats will be removed from the list %@, and the list deleted." = "Alle chats worden uit de lijst %@ verwijderd en de lijst wordt verwijderd."; + /* No comment provided by engineer. */ "All data is erased when it is entered." = "Alle gegevens worden bij het invoeren gewist."; +/* No comment provided by engineer. */ +"All data is kept private on your device." = "Alle gegevens zijn privé op uw apparaat."; + /* No comment provided by engineer. */ "All group members will remain connected." = "Alle groepsleden blijven verbonden."; +/* feature role */ +"all members" = "alle leden"; + +/* No comment provided by engineer. */ +"All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages." = "Alle berichten en bestanden worden **end-to-end versleuteld** verzonden, met post-quantumbeveiliging in directe berichten."; + /* No comment provided by engineer. */ "All messages will be deleted - this cannot be undone!" = "Alle berichten worden verwijderd. Dit kan niet ongedaan worden gemaakt!"; @@ -410,29 +499,44 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Alle nieuwe berichten van %@ worden verborgen!"; +/* profile dropdown */ +"All profiles" = "Alle profielen"; + +/* No comment provided by engineer. */ +"All reports will be archived for you." = "Alle rapporten worden voor u gearchiveerd."; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Al uw contacten blijven verbonden."; /* No comment provided by engineer. */ "All your contacts will remain connected. Profile update will be sent to your contacts." = "Al uw contacten blijven verbonden. Profiel update wordt naar uw contacten verzonden."; +/* No comment provided by engineer. */ +"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Al uw contacten, gesprekken en bestanden worden veilig gecodeerd en in delen geüpload naar geconfigureerde XFTP-relays."; + /* No comment provided by engineer. */ "Allow" = "Toestaan"; /* No comment provided by engineer. */ "Allow calls only if your contact allows them." = "Sta oproepen alleen toe als uw contact dit toestaat."; +/* No comment provided by engineer. */ +"Allow calls?" = "Oproepen toestaan?"; + /* No comment provided by engineer. */ "Allow disappearing messages only if your contact allows it to you." = "Sta verdwijnende berichten alleen toe als uw contact dit toestaat."; /* No comment provided by engineer. */ -"Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Sta het onomkeerbaar verwijderen van berichten alleen toe als uw contact dit toestaat. (24 uur)"; +"Allow downgrade" = "Downgraden toestaan"; /* No comment provided by engineer. */ -"Allow message reactions only if your contact allows them." = "Sta berichtreacties alleen toe als uw contact dit toestaat."; +"Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Sta het definitief verwijderen van berichten alleen toe als uw contact dit toestaat. (24 uur)"; /* No comment provided by engineer. */ -"Allow message reactions." = "Sta berichtreacties toe."; +"Allow message reactions only if your contact allows them." = "Sta bericht reacties alleen toe als uw contact dit toestaat."; + +/* No comment provided by engineer. */ +"Allow message reactions." = "Sta bericht reacties toe."; /* No comment provided by engineer. */ "Allow sending direct messages to members." = "Sta het verzenden van directe berichten naar leden toe."; @@ -441,11 +545,20 @@ "Allow sending disappearing messages." = "Toestaan dat verdwijnende berichten worden verzonden."; /* No comment provided by engineer. */ -"Allow to irreversibly delete sent messages. (24 hours)" = "Sta toe om verzonden berichten onomkeerbaar te verwijderen. (24 uur)"; +"Allow sharing" = "Delen toestaan"; + +/* No comment provided by engineer. */ +"Allow to irreversibly delete sent messages. (24 hours)" = "Sta toe om verzonden berichten definitief te verwijderen. (24 uur)"; + +/* No comment provided by engineer. */ +"Allow to report messsages to moderators." = "Hiermee kunt u berichten rapporteren aan moderators."; /* No comment provided by engineer. */ "Allow to send files and media." = "Sta toe om bestanden en media te verzenden."; +/* No comment provided by engineer. */ +"Allow to send SimpleX links." = "Sta toe dat SimpleX-links worden verzonden."; + /* No comment provided by engineer. */ "Allow to send voice messages." = "Sta toe om spraak berichten te verzenden."; @@ -456,13 +569,13 @@ "Allow voice messages?" = "Spraak berichten toestaan?"; /* No comment provided by engineer. */ -"Allow your contacts adding message reactions." = "Sta uw contactpersonen toe om berichtreacties toe te voegen."; +"Allow your contacts adding message reactions." = "Sta uw contactpersonen toe om bericht reacties toe te voegen."; /* No comment provided by engineer. */ "Allow your contacts to call you." = "Sta toe dat uw contacten u bellen."; /* No comment provided by engineer. */ -"Allow your contacts to irreversibly delete sent messages. (24 hours)" = "Laat uw contacten verzonden berichten onomkeerbaar verwijderen. (24 uur)"; +"Allow your contacts to irreversibly delete sent messages. (24 hours)" = "Laat uw contacten verzonden berichten definitief verwijderen. (24 uur)"; /* No comment provided by engineer. */ "Allow your contacts to send disappearing messages." = "Sta toe dat uw contacten verdwijnende berichten verzenden."; @@ -482,6 +595,9 @@ /* pref value */ "always" = "altijd"; +/* No comment provided by engineer. */ +"Always use private routing." = "Gebruik altijd privéroutering."; + /* No comment provided by engineer. */ "Always use relay" = "Altijd relay gebruiken"; @@ -491,15 +607,27 @@ /* No comment provided by engineer. */ "and %lld other events" = "en %lld andere gebeurtenissen"; +/* report reason */ +"Another reason" = "Een andere reden"; + /* No comment provided by engineer. */ "Answer call" = "Beantwoord oproep"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "Iedereen kan servers hosten."; + /* No comment provided by engineer. */ "App build: %@" = "App build: %@"; +/* No comment provided by engineer. */ +"App data migration" = "Migratie van app-gegevens"; + /* No comment provided by engineer. */ "App encrypts new local files (except videos)." = "App versleutelt nieuwe lokale bestanden (behalve video's)."; +/* No comment provided by engineer. */ +"App group:" = "App-groep:"; + /* No comment provided by engineer. */ "App icon" = "App icon"; @@ -509,6 +637,9 @@ /* No comment provided by engineer. */ "App passcode is replaced with self-destruct passcode." = "De app-toegangscode wordt vervangen door een zelfvernietigings wachtwoord."; +/* No comment provided by engineer. */ +"App session" = "Appsessie"; + /* No comment provided by engineer. */ "App version" = "App versie"; @@ -518,9 +649,51 @@ /* No comment provided by engineer. */ "Appearance" = "Uiterlijk"; +/* No comment provided by engineer. */ +"Apply" = "Toepassen"; + +/* No comment provided by engineer. */ +"Apply to" = "Toepassen op"; + +/* No comment provided by engineer. */ +"Archive" = "Archief"; + +/* No comment provided by engineer. */ +"Archive %lld reports?" = "%lld rapporten archiveren?"; + +/* No comment provided by engineer. */ +"Archive all reports?" = "Alle rapporten archiveren?"; + +/* No comment provided by engineer. */ +"Archive and upload" = "Archiveren en uploaden"; + +/* No comment provided by engineer. */ +"Archive contacts to chat later." = "Archiveer contacten om later te chatten."; + +/* No comment provided by engineer. */ +"Archive report" = "Rapport archiveren"; + +/* No comment provided by engineer. */ +"Archive report?" = "Rapport archiveren?"; + +/* swipe action */ +"Archive reports" = "Rapporten archiveren"; + +/* No comment provided by engineer. */ +"Archived contacts" = "Gearchiveerde contacten"; + +/* No comment provided by engineer. */ +"archived report" = "gearchiveerd rapport"; + +/* No comment provided by engineer. */ +"Archiving database" = "Database archiveren"; + /* No comment provided by engineer. */ "Attach" = "Bijvoegen"; +/* No comment provided by engineer. */ +"attempts" = "pogingen"; + /* No comment provided by engineer. */ "Audio & video calls" = "Audio en video gesprekken"; @@ -534,7 +707,7 @@ "Audio/video calls" = "Audio/video oproepen"; /* No comment provided by engineer. */ -"Audio/video calls are prohibited." = "Audio/video gesprekken zijn verboden."; +"Audio/video calls are prohibited." = "Audio/video gesprekken zijn niet toegestaan."; /* PIN entry */ "Authentication cancelled" = "Verificatie geannuleerd"; @@ -560,9 +733,15 @@ /* No comment provided by engineer. */ "Auto-accept images" = "Afbeeldingen automatisch accepteren"; +/* alert title */ +"Auto-accept settings" = "Instellingen automatisch accepteren"; + /* No comment provided by engineer. */ "Back" = "Terug"; +/* No comment provided by engineer. */ +"Background" = "Achtergrond"; + /* No comment provided by engineer. */ "Bad desktop address" = "Onjuist desktopadres"; @@ -578,12 +757,39 @@ /* No comment provided by engineer. */ "Bad message ID" = "Onjuiste bericht-ID"; +/* No comment provided by engineer. */ +"Better calls" = "Betere gesprekken"; + /* No comment provided by engineer. */ "Better groups" = "Betere groepen"; +/* No comment provided by engineer. */ +"Better groups performance" = "Betere prestaties van groepen"; + +/* No comment provided by engineer. */ +"Better message dates." = "Betere datums voor berichten."; + /* No comment provided by engineer. */ "Better messages" = "Betere berichten"; +/* No comment provided by engineer. */ +"Better networking" = "Beter netwerk"; + +/* No comment provided by engineer. */ +"Better notifications" = "Betere meldingen"; + +/* No comment provided by engineer. */ +"Better privacy and security" = "Betere privacy en veiligheid"; + +/* No comment provided by engineer. */ +"Better security ✅" = "Betere beveiliging ✅"; + +/* No comment provided by engineer. */ +"Better user experience" = "Betere gebruikerservaring"; + +/* No comment provided by engineer. */ +"Black" = "Zwart"; + /* No comment provided by engineer. */ "Block" = "Blokkeren"; @@ -606,19 +812,26 @@ "blocked" = "geblokkeerd"; /* rcv group event chat item */ -"blocked %@" = "geblokkeerd %@"; +"blocked %@" = "blokkeerde %@"; -/* marked deleted chat item preview text */ +/* blocked chat item +marked deleted chat item preview text */ "blocked by admin" = "geblokkeerd door beheerder"; /* No comment provided by engineer. */ "Blocked by admin" = "Geblokkeerd door beheerder"; +/* No comment provided by engineer. */ +"Blur for better privacy." = "Vervagen voor betere privacy."; + +/* No comment provided by engineer. */ +"Blur media" = "Vervaag media"; + /* No comment provided by engineer. */ "bold" = "vetgedrukt"; /* No comment provided by engineer. */ -"Both you and your contact can add message reactions." = "Zowel u als uw contact kunnen berichtreacties toevoegen."; +"Both you and your contact can add message reactions." = "Zowel u als uw contact kunnen bericht reacties toevoegen."; /* No comment provided by engineer. */ "Both you and your contact can irreversibly delete sent messages. (24 hours)" = "Zowel jij als je contact kunnen verzonden berichten onherroepelijk verwijderen. (24 uur)"; @@ -636,7 +849,22 @@ "Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Bulgaars, Fins, Thais en Oekraïens - dankzij de gebruikers en [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"; /* No comment provided by engineer. */ -"By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Via chat profiel (standaard) of [via verbinding](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)."; +"Business address" = "Zakelijk adres"; + +/* No comment provided by engineer. */ +"Business chats" = "Zakelijke chats"; + +/* No comment provided by engineer. */ +"Businesses" = "bedrijven"; + +/* No comment provided by engineer. */ +"By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Via chatprofiel (standaard) of [via verbinding](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)."; + +/* No comment provided by engineer. */ +"By using SimpleX Chat you agree to:\n- send only legal content in public groups.\n- respect other users – no spam." = "Door SimpleX Chat te gebruiken, gaat u ermee akkoord:\n- alleen legale content te versturen in openbare groepen.\n- andere gebruikers te respecteren – geen spam."; + +/* No comment provided by engineer. */ +"call" = "bellen"; /* No comment provided by engineer. */ "Call already ended!" = "Oproep al beëindigd!"; @@ -653,9 +881,18 @@ /* No comment provided by engineer. */ "Calls" = "Oproepen"; +/* No comment provided by engineer. */ +"Calls prohibited!" = "Bellen niet toegestaan!"; + /* No comment provided by engineer. */ "Camera not available" = "Camera niet beschikbaar"; +/* No comment provided by engineer. */ +"Can't call contact" = "Kan contact niet bellen"; + +/* No comment provided by engineer. */ +"Can't call member" = "Kan lid niet bellen"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Kan contact niet uitnodigen!"; @@ -663,8 +900,15 @@ "Can't invite contacts!" = "Kan geen contacten uitnodigen!"; /* No comment provided by engineer. */ +"Can't message member" = "Kan geen bericht sturen naar lid"; + +/* alert action +alert button */ "Cancel" = "Annuleren"; +/* No comment provided by engineer. */ +"Cancel migration" = "Migratie annuleren"; + /* feature offered item */ "cancelled %@" = "geannuleerd %@"; @@ -672,11 +916,26 @@ "Cannot access keychain to save database password" = "Geen toegang tot de keychain om database wachtwoord op te slaan"; /* No comment provided by engineer. */ +"Cannot forward message" = "Kan bericht niet doorsturen"; + +/* alert title */ "Cannot receive file" = "Kan bestand niet ontvangen"; +/* snd error text */ +"Capacity exceeded - recipient did not receive previously sent messages." = "Capaciteit overschreden - ontvanger heeft eerder verzonden berichten niet ontvangen."; + +/* No comment provided by engineer. */ +"Cellular" = "Mobiel"; + /* No comment provided by engineer. */ "Change" = "Veranderen"; +/* alert title */ +"Change automatic message deletion?" = "Automatisch verwijderen van berichten wijzigen?"; + +/* authentication reason */ +"Change chat profiles" = "Gebruikersprofielen wijzigen"; + /* No comment provided by engineer. */ "Change database passphrase?" = "Wachtwoord database wijzigen?"; @@ -702,7 +961,7 @@ "Change self-destruct mode" = "Zelfvernietigings modus wijzigen"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "Zelfvernietigings code wijzigen"; /* chat item text */ @@ -721,7 +980,16 @@ "changing address…" = "adres wijzigen…"; /* No comment provided by engineer. */ -"Chat archive" = "Gesprek archief"; +"Chat" = "Chat"; + +/* No comment provided by engineer. */ +"Chat already exists" = "Chat bestaat al"; + +/* No comment provided by engineer. */ +"Chat already exists!" = "Chat bestaat al!"; + +/* No comment provided by engineer. */ +"Chat colors" = "Chat kleuren"; /* No comment provided by engineer. */ "Chat console" = "Chat console"; @@ -732,6 +1000,9 @@ /* No comment provided by engineer. */ "Chat database deleted" = "Chat database verwijderd"; +/* No comment provided by engineer. */ +"Chat database exported" = "Chat database geëxporteerd"; + /* No comment provided by engineer. */ "Chat database imported" = "Chat database geïmporteerd"; @@ -744,18 +1015,48 @@ /* No comment provided by engineer. */ "Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "Chat is gestopt. Als je deze database al op een ander apparaat hebt gebruikt, moet je deze terugzetten voordat je met chatten begint."; +/* No comment provided by engineer. */ +"Chat list" = "Chatlijst"; + +/* No comment provided by engineer. */ +"Chat migrated!" = "Chat gemigreerd!"; + /* No comment provided by engineer. */ "Chat preferences" = "Gesprek voorkeuren"; -/* No comment provided by engineer. */ -"Chats" = "Gesprekken"; +/* alert message */ +"Chat preferences were changed." = "Chatvoorkeuren zijn gewijzigd."; /* No comment provided by engineer. */ +"Chat profile" = "Gebruikers profiel"; + +/* No comment provided by engineer. */ +"Chat theme" = "Chat thema"; + +/* No comment provided by engineer. */ +"Chat will be deleted for all members - this cannot be undone!" = "De chat wordt voor alle leden verwijderd - dit kan niet ongedaan worden gemaakt!"; + +/* No comment provided by engineer. */ +"Chat will be deleted for you - this cannot be undone!" = "De chat wordt voor je verwijderd - dit kan niet ongedaan worden gemaakt!"; + +/* No comment provided by engineer. */ +"Chats" = "Chats"; + +/* No comment provided by engineer. */ +"Check messages every 20 min." = "Controleer uw berichten elke 20 minuten."; + +/* No comment provided by engineer. */ +"Check messages when allowed." = "Controleer berichten indien toegestaan."; + +/* alert title */ "Check server address and try again." = "Controleer het server adres en probeer het opnieuw."; /* No comment provided by engineer. */ "Chinese and Spanish interface" = "Chinese en Spaanse interface"; +/* No comment provided by engineer. */ +"Choose _Migrate from another device_ on the new device and scan QR code." = "Kies _Migreren vanaf een ander apparaat_ op het nieuwe apparaat en scan de QR-code."; + /* No comment provided by engineer. */ "Choose file" = "Kies bestand"; @@ -763,6 +1064,15 @@ "Choose from library" = "Kies uit bibliotheek"; /* No comment provided by engineer. */ +"Chunks deleted" = "Stukken verwijderd"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "Stukken gedownload"; + +/* No comment provided by engineer. */ +"Chunks uploaded" = "Stukken geüpload"; + +/* swipe action */ "Clear" = "Wissen"; /* No comment provided by engineer. */ @@ -771,6 +1081,12 @@ /* No comment provided by engineer. */ "Clear conversation?" = "Gesprek wissen?"; +/* No comment provided by engineer. */ +"Clear group?" = "Groep wissen?"; + +/* No comment provided by engineer. */ +"Clear or delete group?" = "Groep wissen of verwijderen?"; + /* No comment provided by engineer. */ "Clear private notes?" = "Privénotities verwijderen?"; @@ -778,10 +1094,16 @@ "Clear verification" = "Verwijderd verificatie"; /* No comment provided by engineer. */ -"colored" = "gekleurd"; +"Color chats with the new themes." = "Kleurchats met de nieuwe thema's."; /* No comment provided by engineer. */ -"Colors" = "Kleuren"; +"Color mode" = "Kleur mode"; + +/* No comment provided by engineer. */ +"colored" = "gekleurd"; + +/* report reason */ +"Community guidelines violation" = "Schending van de communityrichtlijnen"; /* server test step */ "Compare file" = "Bestand vergelijken"; @@ -792,15 +1114,51 @@ /* No comment provided by engineer. */ "complete" = "compleet"; +/* No comment provided by engineer. */ +"Completed" = "Voltooid"; + +/* No comment provided by engineer. */ +"Conditions accepted on: %@." = "Voorwaarden geaccepteerd op: %@."; + +/* No comment provided by engineer. */ +"Conditions are accepted for the operator(s): **%@**." = "Voorwaarden worden geaccepteerd voor de operator(s): **%@**."; + +/* No comment provided by engineer. */ +"Conditions are already accepted for these operator(s): **%@**." = "Voorwaarden zijn reeds geaccepteerd voor de volgende operator(s): **%@**."; + +/* No comment provided by engineer. */ +"Conditions of use" = "Gebruiksvoorwaarden"; + +/* No comment provided by engineer. */ +"Conditions will be accepted for the operator(s): **%@**." = "Voorwaarden worden geaccepteerd voor de operator(s): **%@**."; + +/* No comment provided by engineer. */ +"Conditions will be accepted on: %@." = "Voorwaarden worden geaccepteerd op: %@."; + +/* No comment provided by engineer. */ +"Conditions will be automatically accepted for enabled operators on: %@." = "Voorwaarden worden automatisch geaccepteerd voor ingeschakelde operators op: %@."; + /* No comment provided by engineer. */ "Configure ICE servers" = "ICE servers configureren"; +/* No comment provided by engineer. */ +"Configure server operators" = "Serveroperators configureren"; + /* No comment provided by engineer. */ "Confirm" = "Bevestigen"; +/* No comment provided by engineer. */ +"Confirm contact deletion?" = "Contact verwijderen bevestigen?"; + /* No comment provided by engineer. */ "Confirm database upgrades" = "Bevestig database upgrades"; +/* No comment provided by engineer. */ +"Confirm files from unknown servers." = "Bevestig bestanden van onbekende servers."; + +/* No comment provided by engineer. */ +"Confirm network settings" = "Bevestig netwerk instellingen"; + /* No comment provided by engineer. */ "Confirm new passphrase…" = "Bevestig nieuw wachtwoord…"; @@ -810,6 +1168,15 @@ /* No comment provided by engineer. */ "Confirm password" = "Bevestig wachtwoord"; +/* No comment provided by engineer. */ +"Confirm that you remember database passphrase to migrate it." = "Bevestig dat u het wachtwoord voor de database onthoudt om deze te migreren."; + +/* No comment provided by engineer. */ +"Confirm upload" = "Bevestig het uploaden"; + +/* token status text */ +"Confirmed" = "Bevestigd"; + /* server test step */ "Connect" = "Verbind"; @@ -825,6 +1192,9 @@ /* No comment provided by engineer. */ "connect to SimpleX Chat developers." = "maak verbinding met SimpleX Chat-ontwikkelaars."; +/* No comment provided by engineer. */ +"Connect to your friends faster." = "Maak sneller verbinding met je vrienden."; + /* No comment provided by engineer. */ "Connect to yourself?" = "Verbinding maken met jezelf?"; @@ -849,18 +1219,27 @@ /* No comment provided by engineer. */ "connected" = "verbonden"; +/* No comment provided by engineer. */ +"Connected" = "Verbonden"; + /* No comment provided by engineer. */ "Connected desktop" = "Verbonden desktop"; /* rcv group event chat item */ "connected directly" = "direct verbonden"; +/* No comment provided by engineer. */ +"Connected servers" = "Verbonden servers"; + /* No comment provided by engineer. */ "Connected to desktop" = "Verbonden met desktop"; /* No comment provided by engineer. */ "connecting" = "Verbinden"; +/* No comment provided by engineer. */ +"Connecting" = "Verbinden"; + /* No comment provided by engineer. */ "connecting (accepted)" = "verbinden (geaccepteerd)"; @@ -882,15 +1261,24 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "Verbinden met server... (fout: %@)"; +/* No comment provided by engineer. */ +"Connecting to contact, please wait or check later!" = "Er wordt verbinding gemaakt met het contact. Even geduld of controleer het later!"; + /* No comment provided by engineer. */ "Connecting to desktop" = "Verbinding maken met desktop"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "Verbinden…"; /* No comment provided by engineer. */ "Connection" = "Verbinding"; +/* No comment provided by engineer. */ +"Connection and servers status." = "Verbindings- en serverstatus."; + +/* No comment provided by engineer. */ +"Connection blocked" = "Verbinding geblokkeerd"; + /* No comment provided by engineer. */ "Connection error" = "Verbindingsfout"; @@ -900,18 +1288,39 @@ /* chat list item title (it should not be shown */ "connection established" = "verbinding gemaakt"; +/* No comment provided by engineer. */ +"Connection is blocked by server operator:\n%@" = "Verbinding is geblokkeerd door serveroperator:\n%@"; + +/* No comment provided by engineer. */ +"Connection not ready." = "Verbinding nog niet klaar."; + +/* No comment provided by engineer. */ +"Connection notifications" = "Verbindingsmeldingen"; + /* No comment provided by engineer. */ "Connection request sent!" = "Verbindingsverzoek verzonden!"; +/* No comment provided by engineer. */ +"Connection requires encryption renegotiation." = "Verbinding vereist heronderhandeling over encryptie."; + +/* No comment provided by engineer. */ +"Connection security" = "Beveiliging van de verbinding"; + /* No comment provided by engineer. */ "Connection terminated" = "Verbinding beëindigd"; /* No comment provided by engineer. */ "Connection timeout" = "Timeout verbinding"; +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "Verbinding met desktop is gestopt"; + /* connection information */ "connection:%@" = "verbinding:%@"; +/* No comment provided by engineer. */ +"Connections" = "Verbindingen"; + /* profile update event chat item */ "contact %@ changed to %@" = "contactpersoon %1$@ gewijzigd in %2$@"; @@ -921,6 +1330,9 @@ /* No comment provided by engineer. */ "Contact already exists" = "Contact bestaat al"; +/* No comment provided by engineer. */ +"Contact deleted!" = "Contact verwijderd!"; + /* No comment provided by engineer. */ "contact has e2e encryption" = "contact heeft e2e-codering"; @@ -934,7 +1346,7 @@ "Contact is connected" = "Contact is verbonden"; /* No comment provided by engineer. */ -"Contact is not connected yet!" = "Contact is nog niet verbonden!"; +"Contact is deleted." = "Contact is verwijderd."; /* No comment provided by engineer. */ "Contact name" = "Contact naam"; @@ -942,21 +1354,36 @@ /* No comment provided by engineer. */ "Contact preferences" = "Contact voorkeuren"; +/* No comment provided by engineer. */ +"Contact will be deleted - this cannot be undone!" = "Het contact wordt verwijderd. Dit kan niet ongedaan worden gemaakt!"; + /* No comment provided by engineer. */ "Contacts" = "Contacten"; /* No comment provided by engineer. */ "Contacts can mark messages for deletion; you will be able to view them." = "Contact personen kunnen berichten markeren voor verwijdering; u kunt ze wel bekijken."; +/* blocking reason */ +"Content violates conditions of use" = "Inhoud schendt de gebruiksvoorwaarden"; + /* No comment provided by engineer. */ "Continue" = "Doorgaan"; -/* chat item action */ +/* No comment provided by engineer. */ +"Conversation deleted!" = "Gesprek verwijderd!"; + +/* No comment provided by engineer. */ "Copy" = "Kopiëren"; +/* No comment provided by engineer. */ +"Copy error" = "Kopieerfout"; + /* No comment provided by engineer. */ "Core version: v%@" = "Core versie: v% @"; +/* No comment provided by engineer. */ +"Corner" = "Hoek"; + /* No comment provided by engineer. */ "Correct name to %@?" = "Juiste naam voor %@?"; @@ -964,10 +1391,10 @@ "Create" = "Maak"; /* No comment provided by engineer. */ -"Create a group using a random profile." = "Maak een groep met een willekeurig profiel."; +"Create 1-time link" = "Eenmalige link maken"; /* No comment provided by engineer. */ -"Create an address to let people connect with you." = "Maak een adres aan zodat mensen contact met je kunnen opnemen."; +"Create a group using a random profile." = "Maak een groep met een willekeurig profiel."; /* server test step */ "Create file" = "Bestand maken"; @@ -981,6 +1408,9 @@ /* No comment provided by engineer. */ "Create link" = "Maak link"; +/* No comment provided by engineer. */ +"Create list" = "Maak een lijst"; + /* No comment provided by engineer. */ "Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" = "Maak een nieuw profiel aan in [desktop-app](https://simplex.chat/downloads/). 💻"; @@ -999,6 +1429,9 @@ /* No comment provided by engineer. */ "Create your profile" = "Maak je profiel aan"; +/* No comment provided by engineer. */ +"Created" = "Gemaakt"; + /* No comment provided by engineer. */ "Created at" = "Gemaakt op"; @@ -1006,7 +1439,7 @@ "Created at: %@" = "Aangemaakt op: %@"; /* No comment provided by engineer. */ -"Created on %@" = "Gemaakt op %@"; +"Creating archive link" = "Archief link maken"; /* No comment provided by engineer. */ "Creating link…" = "Link maken…"; @@ -1014,12 +1447,18 @@ /* No comment provided by engineer. */ "creator" = "creator"; +/* No comment provided by engineer. */ +"Current conditions text couldn't be loaded, you can review conditions via this link:" = "De tekst van de huidige voorwaarden kon niet worden geladen. U kunt de voorwaarden bekijken via deze link:"; + /* No comment provided by engineer. */ "Current Passcode" = "Huidige toegangscode"; /* No comment provided by engineer. */ "Current passphrase…" = "Huidige wachtwoord…"; +/* No comment provided by engineer. */ +"Current profile" = "Huidig profiel"; + /* No comment provided by engineer. */ "Currently maximum supported file size is %@." = "De momenteel maximaal ondersteunde bestandsgrootte is %@."; @@ -1029,9 +1468,18 @@ /* No comment provided by engineer. */ "Custom time" = "Aangepaste tijd"; +/* No comment provided by engineer. */ +"Customizable message shape." = "Aanpasbare berichtvorm."; + +/* No comment provided by engineer. */ +"Customize theme" = "Thema aanpassen"; + /* No comment provided by engineer. */ "Dark" = "Donker"; +/* No comment provided by engineer. */ +"Dark mode colors" = "Kleuren in donkere modus"; + /* No comment provided by engineer. */ "Database downgrade" = "Database downgraden"; @@ -1072,7 +1520,7 @@ "Database passphrase is different from saved in the keychain." = "Het wachtwoord van de database verschilt van het wachtwoord dat is opgeslagen in de keychain."; /* No comment provided by engineer. */ -"Database passphrase is required to open chat." = "Database wachtwoord is vereist om je gesprekken te openen."; +"Database passphrase is required to open chat." = "Database wachtwoord is vereist om je chats te openen."; /* No comment provided by engineer. */ "Database upgrade" = "Database upgrade"; @@ -1092,13 +1540,20 @@ /* time unit */ "days" = "dagen"; +/* No comment provided by engineer. */ +"Debug delivery" = "Foutopsporing bezorging"; + /* No comment provided by engineer. */ "Decentralized" = "Gedecentraliseerd"; /* message decrypt error item */ "Decryption error" = "Decodering fout"; -/* pref value */ +/* No comment provided by engineer. */ +"decryption errors" = "decoderingsfouten"; + +/* delete after time +pref value */ "default (%@)" = "standaard (%@)"; /* No comment provided by engineer. */ @@ -1107,9 +1562,13 @@ /* No comment provided by engineer. */ "default (yes)" = "standaard (ja)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "Verwijderen"; +/* No comment provided by engineer. */ +"Delete %lld messages of members?" = "%lld berichten van leden verwijderen?"; + /* No comment provided by engineer. */ "Delete %lld messages?" = "%lld berichten verwijderen?"; @@ -1129,16 +1588,19 @@ "Delete and notify contact" = "Verwijderen en contact op de hoogte stellen"; /* No comment provided by engineer. */ -"Delete archive" = "Archief verwijderen"; +"Delete chat" = "Chat verwijderen"; /* No comment provided by engineer. */ -"Delete chat archive?" = "Chat archief verwijderen?"; +"Delete chat messages from your device." = "Verwijder chatberichten van uw apparaat."; /* No comment provided by engineer. */ -"Delete chat profile" = "Chat profiel verwijderen"; +"Delete chat profile" = "Chatprofiel verwijderen"; /* No comment provided by engineer. */ -"Delete chat profile?" = "Chat profiel verwijderen?"; +"Delete chat profile?" = "Chatprofiel verwijderen?"; + +/* No comment provided by engineer. */ +"Delete chat?" = "Chat verwijderen?"; /* No comment provided by engineer. */ "Delete connection" = "Verbinding verwijderen"; @@ -1147,14 +1609,14 @@ "Delete contact" = "Verwijder contact"; /* No comment provided by engineer. */ -"Delete Contact" = "Verwijder contact"; - -/* No comment provided by engineer. */ -"Delete contact?\nThis cannot be undone!" = "Verwijder contact?\nDit kan niet ongedaan gemaakt worden!"; +"Delete contact?" = "Verwijder contact?"; /* No comment provided by engineer. */ "Delete database" = "Database verwijderen"; +/* No comment provided by engineer. */ +"Delete database from this device" = "Verwijder de database van dit apparaat"; + /* server test step */ "Delete file" = "Verwijder bestand"; @@ -1162,7 +1624,7 @@ "Delete files and media?" = "Bestanden en media verwijderen?"; /* No comment provided by engineer. */ -"Delete files for all chat profiles" = "Verwijder bestanden voor alle chat profielen"; +"Delete files for all chat profiles" = "Verwijder bestanden voor alle chatprofielen"; /* chat feature */ "Delete for everyone" = "Verwijderen voor iedereen"; @@ -1185,13 +1647,16 @@ /* No comment provided by engineer. */ "Delete link?" = "Link verwijderen?"; +/* alert title */ +"Delete list?" = "Lijst verwijderen?"; + /* No comment provided by engineer. */ "Delete member message?" = "Bericht van lid verwijderen?"; /* No comment provided by engineer. */ "Delete message?" = "Verwijder bericht?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "Verwijder berichten"; /* No comment provided by engineer. */ @@ -1204,7 +1669,7 @@ "Delete old database?" = "Oude database verwijderen?"; /* No comment provided by engineer. */ -"Delete pending connection" = "Wachtende verbinding verwijderen"; +"Delete or moderate up to 200 messages." = "Maximaal 200 berichten verwijderen of modereren."; /* No comment provided by engineer. */ "Delete pending connection?" = "Wachtende verbinding verwijderen?"; @@ -1215,12 +1680,24 @@ /* server test step */ "Delete queue" = "Wachtrij verwijderen"; +/* No comment provided by engineer. */ +"Delete report" = "Rapport verwijderen"; + +/* No comment provided by engineer. */ +"Delete up to 20 messages at once." = "Verwijder maximaal 20 berichten tegelijk."; + /* No comment provided by engineer. */ "Delete user profile?" = "Gebruikers profiel verwijderen?"; +/* No comment provided by engineer. */ +"Delete without notification" = "Verwijderen zonder melding"; + /* deleted chat item */ "deleted" = "verwijderd"; +/* No comment provided by engineer. */ +"Deleted" = "Verwijderd"; + /* No comment provided by engineer. */ "Deleted at" = "Verwijderd om"; @@ -1233,6 +1710,12 @@ /* rcv group event chat item */ "deleted group" = "verwijderde groep"; +/* No comment provided by engineer. */ +"Deletion errors" = "Verwijderingsfouten"; + +/* No comment provided by engineer. */ +"Delivered even when Apple drops them." = "Geleverd ook als Apple ze verliest"; + /* No comment provided by engineer. */ "Delivery" = "Bezorging"; @@ -1254,9 +1737,27 @@ /* No comment provided by engineer. */ "Desktop devices" = "Desktop apparaten"; +/* No comment provided by engineer. */ +"Destination server address of %@ is incompatible with forwarding server %@ settings." = "Het bestemmingsserveradres van %@ is niet compatibel met de doorstuurserverinstellingen %@."; + +/* snd error text */ +"Destination server error: %@" = "Bestemmingsserverfout: %@"; + +/* No comment provided by engineer. */ +"Destination server version of %@ is incompatible with forwarding server %@." = "De versie van de bestemmingsserver %@ is niet compatibel met de doorstuurserver %@."; + +/* No comment provided by engineer. */ +"Detailed statistics" = "Gedetailleerde statistieken"; + +/* No comment provided by engineer. */ +"Details" = "Details"; + /* No comment provided by engineer. */ "Develop" = "Ontwikkelen"; +/* No comment provided by engineer. */ +"Developer options" = "Ontwikkelaars opties"; + /* No comment provided by engineer. */ "Developer tools" = "Ontwikkel gereedschap"; @@ -1282,11 +1783,20 @@ "Direct messages" = "Directe berichten"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "Directe berichten tussen leden zijn verboden in deze groep."; +"Direct messages between members are prohibited in this chat." = "Directe berichten tussen leden zijn in deze chat niet toegestaan."; + +/* No comment provided by engineer. */ +"Direct messages between members are prohibited." = "Directe berichten tussen leden zijn niet toegestaan."; /* No comment provided by engineer. */ "Disable (keep overrides)" = "Uitschakelen (overschrijvingen behouden)"; +/* alert title */ +"Disable automatic message deletion?" = "Automatisch verwijderen van berichten uitschakelen?"; + +/* alert button */ +"Disable delete messages" = "Berichten verwijderen uitschakelen"; + /* No comment provided by engineer. */ "Disable for all" = "Uitschakelen voor iedereen"; @@ -1296,6 +1806,9 @@ /* No comment provided by engineer. */ "disabled" = "uitgeschakeld"; +/* No comment provided by engineer. */ +"Disabled" = "Uitgeschakeld"; + /* No comment provided by engineer. */ "Disappearing message" = "Verdwijnend bericht"; @@ -1303,10 +1816,10 @@ "Disappearing messages" = "Verdwijnende berichten"; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this chat." = "Verdwijnende berichten zijn verboden in dit gesprek."; +"Disappearing messages are prohibited in this chat." = "Verdwijnende berichten zijn niet toegestaan in dit gesprek."; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "Verdwijnende berichten zijn verboden in deze groep."; +"Disappearing messages are prohibited." = "Verdwijnende berichten zijn niet toegestaan."; /* No comment provided by engineer. */ "Disappears at" = "Verdwijnt op"; @@ -1332,36 +1845,85 @@ /* No comment provided by engineer. */ "Do not send history to new members." = "Stuur geen geschiedenis naar nieuwe leden."; +/* No comment provided by engineer. */ +"Do NOT send messages directly, even if your or destination server does not support private routing." = "Stuur GEEN berichten rechtstreeks, zelfs als uw of de bestemmingsserver geen privéroutering ondersteunt."; + +/* No comment provided by engineer. */ +"Do not use credentials with proxy." = "Gebruik geen inloggegevens met proxy."; + +/* No comment provided by engineer. */ +"Do NOT use private routing." = "Gebruik GEEN privéroutering."; + /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "Gebruik SimpleX NIET voor noodoproepen."; +/* No comment provided by engineer. */ +"Documents:" = "Documenten:"; + /* No comment provided by engineer. */ "Don't create address" = "Maak geen adres aan"; /* No comment provided by engineer. */ "Don't enable" = "Niet inschakelen"; +/* No comment provided by engineer. */ +"Don't miss important messages." = "Mis geen belangrijke berichten."; + /* No comment provided by engineer. */ "Don't show again" = "Niet meer weergeven"; +/* No comment provided by engineer. */ +"Done" = "Klaar"; + /* No comment provided by engineer. */ "Downgrade and open chat" = "Downgraden en chat openen"; +/* alert button +chat item action */ +"Download" = "Downloaden"; + +/* No comment provided by engineer. */ +"Download errors" = "Downloadfouten"; + +/* No comment provided by engineer. */ +"Download failed" = "Download mislukt"; + /* server test step */ "Download file" = "Download bestand"; +/* alert action */ +"Download files" = "‐Bestanden downloaden"; + +/* No comment provided by engineer. */ +"Downloaded" = "Gedownload"; + +/* No comment provided by engineer. */ +"Downloaded files" = "Gedownloade bestanden"; + +/* No comment provided by engineer. */ +"Downloading archive" = "Archief downloaden"; + +/* No comment provided by engineer. */ +"Downloading link details" = "Link gegevens downloaden"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Dubbele weergavenaam!"; /* integrity error chat item */ "duplicate message" = "dubbel bericht"; +/* No comment provided by engineer. */ +"duplicates" = "duplicaten"; + /* No comment provided by engineer. */ "Duration" = "Duur"; /* No comment provided by engineer. */ "e2e encrypted" = "e2e versleuteld"; +/* No comment provided by engineer. */ +"E2E encrypted notifications." = "E2E versleutelde meldingen."; + /* chat item action */ "Edit" = "Bewerk"; @@ -1374,15 +1936,21 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "Inschakelen (overschrijvingen behouden)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "Automatisch verwijderen van berichten aanzetten?"; /* No comment provided by engineer. */ "Enable camera access" = "Schakel cameratoegang in"; +/* No comment provided by engineer. */ +"Enable Flux in Network & servers settings for better metadata privacy." = "Schakel Flux in bij Netwerk- en serverinstellingen voor betere privacy van metagegevens."; + /* No comment provided by engineer. */ "Enable for all" = "Inschakelen voor iedereen"; +/* No comment provided by engineer. */ +"Enable in direct chats (BETA)!" = "Activeer in directe chats (BETA)!"; + /* No comment provided by engineer. */ "Enable instant notifications?" = "Onmiddellijke meldingen inschakelen?"; @@ -1410,6 +1978,12 @@ /* enabled status */ "enabled" = "ingeschakeld"; +/* No comment provided by engineer. */ +"Enabled" = "Ingeschakeld"; + +/* No comment provided by engineer. */ +"Enabled for" = "Ingeschakeld voor"; + /* enabled status */ "enabled for contact" = "ingeschakeld voor contact"; @@ -1482,6 +2056,9 @@ /* chat item text */ "encryption re-negotiation required for %@" = "heronderhandeling van versleuteling vereist voor % @"; +/* No comment provided by engineer. */ +"Encryption renegotiation in progress." = "Er wordt opnieuw onderhandeld over de encryptie."; + /* No comment provided by engineer. */ "ended" = "geëindigd"; @@ -1497,6 +2074,9 @@ /* No comment provided by engineer. */ "Enter Passcode" = "Voer toegangscode in"; +/* No comment provided by engineer. */ +"Enter passphrase" = "Voer het wachtwoord in"; + /* No comment provided by engineer. */ "Enter passphrase…" = "Voer wachtwoord in…"; @@ -1510,10 +2090,10 @@ "Enter this device name…" = "Voer deze apparaatnaam in…"; /* placeholder */ -"Enter welcome message…" = "Welkomst bericht invoeren…"; +"Enter welcome message…" = "Welkom bericht invoeren…"; /* placeholder */ -"Enter welcome message… (optional)" = "Voer welkomst bericht in... (optioneel)"; +"Enter welcome message… (optional)" = "Voer welkom bericht in... (optioneel)"; /* No comment provided by engineer. */ "Enter your name…" = "Vul uw naam in…"; @@ -1527,24 +2107,39 @@ /* No comment provided by engineer. */ "Error aborting address change" = "Fout bij het afbreken van adres wijziging"; +/* alert title */ +"Error accepting conditions" = "Fout bij het accepteren van voorwaarden"; + /* No comment provided by engineer. */ "Error accepting contact request" = "Fout bij het accepteren van een contactverzoek"; -/* No comment provided by engineer. */ -"Error accessing database file" = "Fout bij toegang tot database bestand"; - /* No comment provided by engineer. */ "Error adding member(s)" = "Fout bij het toevoegen van leden"; +/* alert title */ +"Error adding server" = "Fout bij toevoegen server"; + /* No comment provided by engineer. */ "Error changing address" = "Fout bij wijzigen van adres"; +/* No comment provided by engineer. */ +"Error changing connection profile" = "Fout bij wijzigen van verbindingsprofiel"; + /* No comment provided by engineer. */ "Error changing role" = "Fout bij wisselen van rol"; /* No comment provided by engineer. */ "Error changing setting" = "Fout bij wijzigen van instelling"; +/* No comment provided by engineer. */ +"Error changing to incognito!" = "Fout bij het overschakelen naar incognito!"; + +/* No comment provided by engineer. */ +"Error checking token status" = "Fout bij het controleren van de tokenstatus"; + +/* No comment provided by engineer. */ +"Error connecting to forwarding server %@. Please try later." = "Fout bij het verbinden met doorstuurserver %@. Probeer het later opnieuw."; + /* No comment provided by engineer. */ "Error creating address" = "Fout bij aanmaken van adres"; @@ -1554,6 +2149,9 @@ /* No comment provided by engineer. */ "Error creating group link" = "Fout bij maken van groep link"; +/* alert title */ +"Error creating list" = "Fout bij het aanmaken van de lijst"; + /* No comment provided by engineer. */ "Error creating member contact" = "Fout bij aanmaken contact"; @@ -1563,6 +2161,9 @@ /* No comment provided by engineer. */ "Error creating profile!" = "Fout bij aanmaken van profiel!"; +/* No comment provided by engineer. */ +"Error creating report" = "Fout bij het rapporteren"; + /* No comment provided by engineer. */ "Error decrypting file" = "Fout bij het ontsleutelen van bestand"; @@ -1575,9 +2176,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "Fout bij verwijderen van verbinding"; -/* No comment provided by engineer. */ -"Error deleting contact" = "Fout bij het verwijderen van contact"; - /* No comment provided by engineer. */ "Error deleting database" = "Fout bij het verwijderen van de database"; @@ -1590,6 +2188,9 @@ /* No comment provided by engineer. */ "Error deleting user profile" = "Fout bij het verwijderen van gebruikers profiel"; +/* No comment provided by engineer. */ +"Error downloading the archive" = "Fout bij het downloaden van het archief"; + /* No comment provided by engineer. */ "Error enabling delivery receipts!" = "Fout bij het inschakelen van ontvangst bevestiging!"; @@ -1602,26 +2203,47 @@ /* No comment provided by engineer. */ "Error exporting chat database" = "Fout bij het exporteren van de chat database"; +/* No comment provided by engineer. */ +"Error exporting theme: %@" = "Fout bij exporteren van thema: %@"; + /* No comment provided by engineer. */ "Error importing chat database" = "Fout bij het importeren van de chat database"; /* No comment provided by engineer. */ "Error joining group" = "Fout bij lid worden van groep"; +/* alert title */ +"Error loading servers" = "Fout bij het laden van servers"; + /* No comment provided by engineer. */ -"Error loading %@ servers" = "Fout bij het laden van %@ servers"; +"Error migrating settings" = "Fout bij migreren van instellingen"; /* No comment provided by engineer. */ "Error opening chat" = "Fout bij het openen van de chat"; -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Fout bij ontvangen van bestand"; +/* No comment provided by engineer. */ +"Error reconnecting server" = "Fout bij opnieuw verbinding maken met de server"; + +/* No comment provided by engineer. */ +"Error reconnecting servers" = "Fout bij opnieuw verbinden van servers"; + +/* alert title */ +"Error registering for notifications" = "Fout bij registreren voor meldingen"; + /* No comment provided by engineer. */ "Error removing member" = "Fout bij verwijderen van lid"; +/* alert title */ +"Error reordering lists" = "Fout bij het opnieuw ordenen van lijsten"; + /* No comment provided by engineer. */ -"Error saving %@ servers" = "Fout bij opslaan van %@ servers"; +"Error resetting statistics" = "Fout bij het resetten van statistieken"; + +/* alert title */ +"Error saving chat list" = "Fout bij het opslaan van chatlijst"; /* No comment provided by engineer. */ "Error saving group profile" = "Fout bij opslaan van groep profiel"; @@ -1635,6 +2257,12 @@ /* No comment provided by engineer. */ "Error saving passphrase to keychain" = "Fout bij opslaan van wachtwoord in de keychain"; +/* alert title */ +"Error saving servers" = "Fout bij het opslaan van servers"; + +/* when migrating */ +"Error saving settings" = "Fout bij opslaan van instellingen"; + /* No comment provided by engineer. */ "Error saving user password" = "Fout bij opslaan gebruikers wachtwoord"; @@ -1660,17 +2288,26 @@ "Error stopping chat" = "Fout bij het stoppen van de chat"; /* No comment provided by engineer. */ +"Error switching profile" = "Fout bij wisselen van profiel"; + +/* alertTitle */ "Error switching profile!" = "Fout bij wisselen van profiel!"; /* No comment provided by engineer. */ "Error synchronizing connection" = "Fout bij het synchroniseren van de verbinding"; +/* No comment provided by engineer. */ +"Error testing server connection" = "Fout bij het testen van de serververbinding"; + /* No comment provided by engineer. */ "Error updating group link" = "Fout bij bijwerken van groep link"; /* No comment provided by engineer. */ "Error updating message" = "Fout bij updaten van bericht"; +/* alert title */ +"Error updating server" = "Fout bij het updaten van de server"; + /* No comment provided by engineer. */ "Error updating settings" = "Fout bij bijwerken van instellingen"; @@ -1678,9 +2315,17 @@ "Error updating user privacy" = "Fout bij updaten van gebruikers privacy"; /* No comment provided by engineer. */ -"Error: " = "Fout: "; +"Error uploading the archive" = "Fout bij het uploaden van het archief"; /* No comment provided by engineer. */ +"Error verifying passphrase:" = "Fout bij het verifiëren van het wachtwoord:"; + +/* No comment provided by engineer. */ +"Error: " = "Fout: "; + +/* alert message +file error text +snd error text */ "Error: %@" = "Fout: %@"; /* No comment provided by engineer. */ @@ -1690,10 +2335,13 @@ "Error: URL is invalid" = "Fout: URL is ongeldig"; /* No comment provided by engineer. */ -"Even when disabled in the conversation." = "Zelfs wanneer uitgeschakeld in het gesprek."; +"Errors" = "Fouten"; + +/* servers error */ +"Errors in servers configuration." = "Fouten in de serverconfiguratie."; /* No comment provided by engineer. */ -"event happened" = "gebeurtenis gebeurd"; +"Even when disabled in the conversation." = "Zelfs wanneer uitgeschakeld in het gesprek."; /* No comment provided by engineer. */ "Exit without saving" = "Afsluiten zonder opslaan"; @@ -1701,15 +2349,27 @@ /* chat item action */ "Expand" = "Uitklappen"; +/* No comment provided by engineer. */ +"expired" = "verlopen"; + +/* token status text */ +"Expired" = "Verlopen"; + /* No comment provided by engineer. */ "Export database" = "Database exporteren"; /* No comment provided by engineer. */ "Export error:" = "Exportfout:"; +/* No comment provided by engineer. */ +"Export theme" = "Exporteer thema"; + /* No comment provided by engineer. */ "Exported database archive." = "Geëxporteerd database archief."; +/* No comment provided by engineer. */ +"Exported file doesn't exist" = "Geëxporteerd bestand bestaat niet"; + /* No comment provided by engineer. */ "Exporting database archive…" = "Database archief exporteren…"; @@ -1719,12 +2379,42 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "Snel en niet wachten tot de afzender online is!"; +/* No comment provided by engineer. */ +"Faster deletion of groups." = "Sneller verwijderen van groepen."; + /* No comment provided by engineer. */ "Faster joining and more reliable messages." = "Snellere deelname en betrouwbaardere berichten."; /* No comment provided by engineer. */ +"Faster sending messages." = "Sneller verzenden van berichten."; + +/* swipe action */ "Favorite" = "Favoriet"; +/* No comment provided by engineer. */ +"Favorites" = "Favorieten"; + +/* file error alert title */ +"File error" = "Bestandsfout"; + +/* alert message */ +"File errors:\n%@" = "Bestandsfouten:\n%@"; + +/* file error text */ +"File is blocked by server operator:\n%@." = "Bestand is geblokkeerd door serveroperator:\n%@."; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "Bestand niet gevonden - hoogstwaarschijnlijk is het bestand verwijderd of geannuleerd."; + +/* file error text */ +"File server error: %@" = "Bestandsserverfout: %@"; + +/* No comment provided by engineer. */ +"File status" = "Bestandsstatus"; + +/* copied message info */ +"File status: %@" = "Bestandsstatus: %@"; + /* No comment provided by engineer. */ "File will be deleted from servers." = "Het bestand wordt van de servers verwijderd."; @@ -1737,6 +2427,9 @@ /* No comment provided by engineer. */ "File: %@" = "Bestand: %@"; +/* No comment provided by engineer. */ +"Files" = "Bestanden"; + /* No comment provided by engineer. */ "Files & media" = "Bestanden en media"; @@ -1744,19 +2437,28 @@ "Files and media" = "Bestanden en media"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "Bestanden en media zijn verboden in deze groep."; +"Files and media are prohibited." = "Bestanden en media zijn niet toegestaan."; /* No comment provided by engineer. */ -"Files and media prohibited!" = "Bestanden en media verboden!"; +"Files and media not allowed" = "Bestanden en media niet toegestaan"; + +/* No comment provided by engineer. */ +"Files and media prohibited!" = "Bestanden en media niet toegestaan!"; /* No comment provided by engineer. */ "Filter unread and favorite chats." = "Filter ongelezen en favoriete chats."; +/* No comment provided by engineer. */ +"Finalize migration" = "Voltooi de migratie"; + +/* No comment provided by engineer. */ +"Finalize migration on another device." = "Voltooi de migratie op een ander apparaat."; + /* No comment provided by engineer. */ "Finally, we have them! 🚀" = "Eindelijk, we hebben ze! 🚀"; /* No comment provided by engineer. */ -"Find chats faster" = "Vind gesprekken sneller"; +"Find chats faster" = "Vind chats sneller"; /* No comment provided by engineer. */ "Fix" = "Herstel"; @@ -1776,9 +2478,72 @@ /* No comment provided by engineer. */ "Fix not supported by group member" = "Herstel wordt niet ondersteund door groepslid"; +/* No comment provided by engineer. */ +"For all moderators" = "Voor alle moderators"; + +/* servers error */ +"For chat profile %@:" = "Voor chatprofiel %@:"; + /* No comment provided by engineer. */ "For console" = "Voor console"; +/* No comment provided by engineer. */ +"For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server." = "Als uw contactpersoon bijvoorbeeld berichten ontvangt via een SimpleX Chat-server, worden deze door uw app via een Flux-server verzonden."; + +/* No comment provided by engineer. */ +"For me" = "Voor mij"; + +/* No comment provided by engineer. */ +"For private routing" = "Voor privé-routering"; + +/* No comment provided by engineer. */ +"For social media" = "Voor social media"; + +/* chat item action */ +"Forward" = "Doorsturen"; + +/* alert title */ +"Forward %d message(s)?" = "%d bericht(en) doorsturen?"; + +/* No comment provided by engineer. */ +"Forward and save messages" = "Berichten doorsturen en opslaan"; + +/* alert action */ +"Forward messages" = "Berichten doorsturen"; + +/* alert message */ +"Forward messages without files?" = "Berichten doorsturen zonder bestanden?"; + +/* No comment provided by engineer. */ +"Forward up to 20 messages at once." = "Stuur maximaal 20 berichten tegelijk door."; + +/* No comment provided by engineer. */ +"forwarded" = "doorgestuurd"; + +/* No comment provided by engineer. */ +"Forwarded" = "Doorgestuurd"; + +/* No comment provided by engineer. */ +"Forwarded from" = "Doorgestuurd vanuit"; + +/* No comment provided by engineer. */ +"Forwarding %lld messages" = "%lld berichten doorsturen"; + +/* No comment provided by engineer. */ +"Forwarding server %@ failed to connect to destination server %@. Please try later." = "De doorstuurserver %@ kon geen verbinding maken met de bestemmingsserver %@. Probeer het later opnieuw."; + +/* No comment provided by engineer. */ +"Forwarding server address is incompatible with network settings: %@." = "Het adres van de doorstuurserver is niet compatibel met de netwerkinstellingen: %@."; + +/* No comment provided by engineer. */ +"Forwarding server version is incompatible with network settings: %@." = "De doorstuurserverversie is niet compatibel met de netwerkinstellingen: %@."; + +/* snd error text */ +"Forwarding server: %@\nDestination server error: %@" = "Doorstuurserver: %1$@\nBestemmingsserverfout: %2$@"; + +/* snd error text */ +"Forwarding server: %@\nError: %@" = "Doorstuurserver: %1$@\nFout: %2$@"; + /* No comment provided by engineer. */ "Found desktop" = "Desktop gevonden"; @@ -1791,9 +2556,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Volledige naam (optioneel)"; -/* No comment provided by engineer. */ -"Full name:" = "Volledige naam:"; - /* No comment provided by engineer. */ "Fully decentralized – visible only to members." = "Volledig gedecentraliseerd – alleen zichtbaar voor leden."; @@ -1803,9 +2565,18 @@ /* No comment provided by engineer. */ "Further reduced battery usage" = "Verder verminderd batterij verbruik"; +/* No comment provided by engineer. */ +"Get notified when mentioned." = "Ontvang een melding als u vermeld wordt."; + /* No comment provided by engineer. */ "GIFs and stickers" = "GIF's en stickers"; +/* message preview */ +"Good afternoon!" = "Goedemiddag!"; + +/* message preview */ +"Good morning!" = "Goedemorgen!"; + /* No comment provided by engineer. */ "Group" = "Groep"; @@ -1842,24 +2613,6 @@ /* No comment provided by engineer. */ "Group links" = "Groep links"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "Groepsleden kunnen berichtreacties toevoegen."; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "Groepsleden kunnen verzonden berichten onherroepelijk verwijderen. (24 uur)"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "Groepsleden kunnen directe berichten sturen."; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "Groepsleden kunnen verdwijnende berichten sturen."; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "Groepsleden kunnen bestanden en media verzenden."; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "Groepsleden kunnen spraak berichten verzenden."; - /* notification */ "Group message:" = "Groep bericht:"; @@ -1879,7 +2632,7 @@ "group profile updated" = "groep profiel bijgewerkt"; /* No comment provided by engineer. */ -"Group welcome message" = "Groep welkomst bericht"; +"Group welcome message" = "Groep welkom bericht"; /* No comment provided by engineer. */ "Group will be deleted for all members - this cannot be undone!" = "Groep wordt verwijderd voor alle leden, dit kan niet ongedaan worden gemaakt!"; @@ -1887,14 +2640,20 @@ /* No comment provided by engineer. */ "Group will be deleted for you - this cannot be undone!" = "De groep wordt voor u verwijderd, dit kan niet ongedaan worden gemaakt!"; +/* No comment provided by engineer. */ +"Groups" = "Groepen"; + /* No comment provided by engineer. */ "Help" = "Help"; +/* No comment provided by engineer. */ +"Help admins moderating their groups." = "Help beheerders bij het modereren van hun groepen."; + /* No comment provided by engineer. */ "Hidden" = "Verborgen"; /* No comment provided by engineer. */ -"Hidden chat profiles" = "Verborgen chat profielen"; +"Hidden chat profiles" = "Verborgen chatprofielen"; /* No comment provided by engineer. */ "Hidden profile password" = "Verborgen profiel wachtwoord"; @@ -1921,6 +2680,12 @@ "hours" = "uren"; /* No comment provided by engineer. */ +"How it affects privacy" = "Hoe het de privacy beïnvloedt"; + +/* No comment provided by engineer. */ +"How it helps privacy" = "Hoe het de privacy helpt"; + +/* alert button */ "How it works" = "Hoe het werkt"; /* No comment provided by engineer. */ @@ -1935,6 +2700,9 @@ /* No comment provided by engineer. */ "How to use your servers" = "Hoe u uw servers gebruikt"; +/* No comment provided by engineer. */ +"Hungarian interface" = "Hongaarse interface"; + /* No comment provided by engineer. */ "ICE servers (one per line)" = "ICE servers (één per lijn)"; @@ -1942,7 +2710,7 @@ "If you can't meet in person, show QR code in a video call, or share the link." = "Als je elkaar niet persoonlijk kunt ontmoeten, laat dan de QR-code zien in een videogesprek of deel de link."; /* No comment provided by engineer. */ -"If you enter this passcode when opening the app, all app data will be irreversibly removed!" = "Als u deze toegangscode invoert bij het openen van de app, worden alle app-gegevens onomkeerbaar verwijderd!"; +"If you enter this passcode when opening the app, all app data will be irreversibly removed!" = "Als u deze toegangscode invoert bij het openen van de app, worden alle app-gegevens definitief verwijderd!"; /* No comment provided by engineer. */ "If you enter your self-destruct passcode while opening the app:" = "Als u uw zelfvernietigings wachtwoord invoert tijdens het openen van de app:"; @@ -1963,7 +2731,7 @@ "Immediately" = "Onmiddellijk"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "Immuun voor spam en misbruik"; +"Immune to spam" = "Immuun voor spam en misbruik"; /* No comment provided by engineer. */ "Import" = "Importeren"; @@ -1974,6 +2742,18 @@ /* No comment provided by engineer. */ "Import database" = "Database importeren"; +/* No comment provided by engineer. */ +"Import failed" = "Importeren is mislukt"; + +/* No comment provided by engineer. */ +"Import theme" = "Thema importeren"; + +/* No comment provided by engineer. */ +"Importing archive" = "Archief importeren"; + +/* No comment provided by engineer. */ +"Improved delivery, reduced traffic usage.\nMore improvements are coming soon!" = "Verbeterde levering, minder data gebruik.\nBinnenkort meer verbeteringen!"; + /* No comment provided by engineer. */ "Improved message delivery" = "Verbeterde berichtbezorging"; @@ -1983,9 +2763,24 @@ /* No comment provided by engineer. */ "Improved server configuration" = "Verbeterde serverconfiguratie"; +/* No comment provided by engineer. */ +"In order to continue, chat should be stopped." = "Om verder te kunnen gaan, moet de chat worden gestopt."; + /* No comment provided by engineer. */ "In reply to" = "In antwoord op"; +/* No comment provided by engineer. */ +"In-call sounds" = "Geluiden tijdens het bellen"; + +/* No comment provided by engineer. */ +"inactive" = "inactief"; + +/* report reason */ +"Inappropriate content" = "Ongepaste inhoud"; + +/* report reason */ +"Inappropriate profile" = "Ongepast profiel"; + /* No comment provided by engineer. */ "Incognito" = "Incognito"; @@ -2040,14 +2835,32 @@ /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Installeer [SimpleX Chat voor terminal](https://github.com/simplex-chat/simplex-chat)"; +/* No comment provided by engineer. */ +"Instant" = "Direct"; + /* No comment provided by engineer. */ "Instant push notifications will be hidden!\n" = "Directe push meldingen worden verborgen!\n"; /* No comment provided by engineer. */ -"Instantly" = "Direct"; +"Interface" = "Interface"; /* No comment provided by engineer. */ -"Interface" = "Interface"; +"Interface colors" = "Interface kleuren"; + +/* token status text */ +"Invalid" = "Ongeldig"; + +/* token status text */ +"Invalid (bad token)" = "Ongeldig (ongeldig token)"; + +/* token status text */ +"Invalid (expired)" = "Ongeldig (verlopen)"; + +/* token status text */ +"Invalid (unregistered)" = "Ongeldig (niet geregistreerd)"; + +/* token status text */ +"Invalid (wrong topic)" = "Ongeldig (verkeerd onderwerp)"; /* invalid chat data */ "invalid chat" = "ongeldige gesprek"; @@ -2067,6 +2880,9 @@ /* No comment provided by engineer. */ "Invalid link" = "Ongeldige link"; +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Ongeldige migratie bevestiging"; + /* No comment provided by engineer. */ "Invalid name!" = "Ongeldige naam!"; @@ -2076,7 +2892,7 @@ /* No comment provided by engineer. */ "Invalid response" = "Ongeldig antwoord"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "Ongeldig server adres!"; /* item status text */ @@ -2088,12 +2904,18 @@ /* group name */ "invitation to group %@" = "uitnodiging voor groep %@"; +/* No comment provided by engineer. */ +"invite" = "uitnodiging"; + /* No comment provided by engineer. */ "Invite friends" = "Nodig vrienden uit"; /* No comment provided by engineer. */ "Invite members" = "Nodig leden uit"; +/* No comment provided by engineer. */ +"Invite to chat" = "Uitnodigen voor een chat"; + /* No comment provided by engineer. */ "Invite to group" = "Uitnodigen voor groep"; @@ -2115,17 +2937,20 @@ /* No comment provided by engineer. */ "iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "iOS-keychain wordt gebruikt om het wachtwoord veilig op te slaan nadat u de app opnieuw hebt opgestart of het wachtwoord hebt gewijzigd, hiermee kunt u push meldingen ontvangen."; +/* No comment provided by engineer. */ +"IP address" = "IP-adres"; + /* No comment provided by engineer. */ "Irreversible message deletion" = "Onomkeerbare berichtverwijdering"; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this chat." = "Het onomkeerbaar verwijderen van berichten is verboden in dit gesprek."; +"Irreversible message deletion is prohibited in this chat." = "Het definitief verwijderen van berichten is niet toegestaan in dit gesprek."; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "Het onomkeerbaar verwijderen van berichten is verboden in deze groep."; +"Irreversible message deletion is prohibited." = "Het definitief verwijderen van berichten is verbHet definitief verwijderen van berichten is niet toegestaan.."; /* No comment provided by engineer. */ -"It allows having many anonymous connections without any shared data between them in a single chat profile." = "Het maakt het mogelijk om veel anonieme verbindingen te hebben zonder enige gedeelde gegevens tussen hen in een enkel chat profiel."; +"It allows having many anonymous connections without any shared data between them in a single chat profile." = "Het maakt het mogelijk om veel anonieme verbindingen te hebben zonder enige gedeelde gegevens tussen hen in een enkel chatprofiel."; /* No comment provided by engineer. */ "It can happen when you or your connection used the old database backup." = "Het kan gebeuren wanneer u of de ander een oude database back-up gebruikt."; @@ -2133,6 +2958,9 @@ /* No comment provided by engineer. */ "It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Het kan gebeuren wanneer:\n1. De berichten zijn na 2 dagen verlopen bij de verzendende client of na 30 dagen op de server.\n2. Decodering van het bericht is mislukt, omdat u of uw contact een oude database back-up heeft gebruikt.\n3. De verbinding is verbroken."; +/* No comment provided by engineer. */ +"It protects your IP address and connections." = "Het beschermt uw IP-adres en verbindingen."; + /* No comment provided by engineer. */ "It seems like you are already connected via this link. If it is not the case, there was an error (%@)." = "Het lijkt erop dat u al bent verbonden via deze link. Als dit niet het geval is, is er een fout opgetreden (%@)."; @@ -2145,8 +2973,8 @@ /* No comment provided by engineer. */ "Japanese interface" = "Japanse interface"; -/* No comment provided by engineer. */ -"Join" = "Word lid van"; +/* swipe action */ +"Join" = "Word lid"; /* No comment provided by engineer. */ "join as %@" = "deelnemen als %@"; @@ -2172,13 +3000,16 @@ /* No comment provided by engineer. */ "Joining group" = "Deel nemen aan groep"; -/* No comment provided by engineer. */ +/* alert action */ "Keep" = "Bewaar"; +/* No comment provided by engineer. */ +"Keep conversation" = "Behoud het gesprek"; + /* No comment provided by engineer. */ "Keep the app open to use it from desktop" = "Houd de app geopend om deze vanaf de desktop te gebruiken"; -/* No comment provided by engineer. */ +/* alert title */ "Keep unused invitation?" = "Ongebruikte uitnodiging bewaren?"; /* No comment provided by engineer. */ @@ -2196,9 +3027,15 @@ /* No comment provided by engineer. */ "Learn more" = "Kom meer te weten"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "Verlaten"; +/* No comment provided by engineer. */ +"Leave chat" = "Chat verlaten"; + +/* No comment provided by engineer. */ +"Leave chat?" = "Chat verlaten?"; + /* No comment provided by engineer. */ "Leave group" = "Groep verlaten"; @@ -2226,6 +3063,15 @@ /* No comment provided by engineer. */ "Linked desktops" = "Gelinkte desktops"; +/* swipe action */ +"List" = "Lijst"; + +/* No comment provided by engineer. */ +"List name and emoji should be different for all lists." = "De naam en emoji van de lijst moeten voor alle lijsten verschillend zijn."; + +/* No comment provided by engineer. */ +"List name..." = "Naam van lijst..."; + /* No comment provided by engineer. */ "LIVE" = "LIVE"; @@ -2235,9 +3081,6 @@ /* No comment provided by engineer. */ "Live messages" = "Live berichten"; -/* No comment provided by engineer. */ -"Local" = "Lokaal"; - /* No comment provided by engineer. */ "Local name" = "Lokale naam"; @@ -2250,24 +3093,15 @@ /* No comment provided by engineer. */ "Lock mode" = "Vergrendeling modus"; -/* No comment provided by engineer. */ -"Make a private connection" = "Maak een privéverbinding"; - /* No comment provided by engineer. */ "Make one message disappear" = "Eén bericht laten verdwijnen"; /* No comment provided by engineer. */ "Make profile private!" = "Profiel privé maken!"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Zorg ervoor dat %@ server adressen de juiste indeling hebben, regel gescheiden zijn en niet gedupliceerd zijn (%@)."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Zorg ervoor dat WebRTC ICE server adressen de juiste indeling hebben, regel gescheiden zijn en niet gedupliceerd zijn."; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Veel mensen vroegen: *als SimpleX geen gebruikers-ID's heeft, hoe kan het dan berichten bezorgen?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "Markeer verwijderd voor iedereen"; @@ -2286,6 +3120,12 @@ /* No comment provided by engineer. */ "Max 30 seconds, received instantly." = "Max 30 seconden, direct ontvangen."; +/* No comment provided by engineer. */ +"Media & file servers" = "Media- en bestandsservers"; + +/* blur media */ +"Medium" = "Medium"; + /* member role */ "member" = "lid"; @@ -2298,39 +3138,117 @@ /* rcv group event chat item */ "member connected" = "is toegetreden"; +/* item status text */ +"Member inactive" = "Lid inactief"; + +/* chat feature */ +"Member reports" = "Ledenrapporten"; + +/* No comment provided by engineer. */ +"Member role will be changed to \"%@\". All chat members will be notified." = "De rol van het lid wordt gewijzigd naar \"%@\". Alle chatleden worden op de hoogte gebracht."; + /* No comment provided by engineer. */ "Member role will be changed to \"%@\". All group members will be notified." = "De rol van lid wordt gewijzigd in \"%@\". Alle groepsleden worden op de hoogte gebracht."; /* No comment provided by engineer. */ "Member role will be changed to \"%@\". The member will receive a new invitation." = "De rol van lid wordt gewijzigd in \"%@\". Het lid ontvangt een nieuwe uitnodiging."; +/* No comment provided by engineer. */ +"Member will be removed from chat - this cannot be undone!" = "Lid wordt verwijderd uit de chat - dit kan niet ongedaan worden gemaakt!"; + /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "Lid wordt uit de groep verwijderd, dit kan niet ongedaan worden gemaakt!"; +/* No comment provided by engineer. */ +"Members can add message reactions." = "Groepsleden kunnen bericht reacties toevoegen."; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "Groepsleden kunnen verzonden berichten onherroepelijk verwijderen. (24 uur)"; + +/* No comment provided by engineer. */ +"Members can report messsages to moderators." = "Leden kunnen berichten melden bij moderators."; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "Groepsleden kunnen directe berichten sturen."; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "Groepsleden kunnen verdwijnende berichten sturen."; + +/* No comment provided by engineer. */ +"Members can send files and media." = "Groepsleden kunnen bestanden en media verzenden."; + +/* No comment provided by engineer. */ +"Members can send SimpleX links." = "Groepsleden kunnen SimpleX-links verzenden."; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "Groepsleden kunnen spraak berichten verzenden."; + +/* No comment provided by engineer. */ +"Mention members 👋" = "Vermeld leden 👋"; + +/* No comment provided by engineer. */ +"Menus" = "Menu's"; + +/* No comment provided by engineer. */ +"message" = "bericht"; + /* item status text */ "Message delivery error" = "Fout bij bezorging van bericht"; /* No comment provided by engineer. */ "Message delivery receipts!" = "Ontvangst bevestiging voor berichten!"; +/* item status text */ +"Message delivery warning" = "Waarschuwing voor berichtbezorging"; + /* No comment provided by engineer. */ "Message draft" = "Concept bericht"; +/* item status text */ +"Message forwarded" = "Bericht doorgestuurd"; + +/* item status description */ +"Message may be delivered later if member becomes active." = "Het bericht kan later worden bezorgd als het lid actief wordt."; + +/* No comment provided by engineer. */ +"Message queue info" = "Informatie over berichtenwachtrij"; + /* chat feature */ "Message reactions" = "Reacties op berichten"; /* No comment provided by engineer. */ -"Message reactions are prohibited in this chat." = "Reacties op berichten zijn verboden in deze chat."; +"Message reactions are prohibited in this chat." = "Reacties op berichten zijn niet toegestaan in deze chat."; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "Reacties op berichten zijn verboden in deze groep."; +"Message reactions are prohibited." = "Reacties op berichten zijn niet toegestaan."; /* notification */ "message received" = "bericht ontvangen"; +/* No comment provided by engineer. */ +"Message reception" = "Bericht ontvangst"; + +/* No comment provided by engineer. */ +"Message servers" = "Berichtservers"; + +/* No comment provided by engineer. */ +"Message shape" = "Berichtvorm"; + +/* No comment provided by engineer. */ +"Message source remains private." = "Berichtbron blijft privé."; + +/* No comment provided by engineer. */ +"Message status" = "Berichtstatus"; + +/* copied message info */ +"Message status: %@" = "Berichtstatus: %@"; + /* No comment provided by engineer. */ "Message text" = "Bericht tekst"; +/* No comment provided by engineer. */ +"Message too large" = "Bericht te groot"; + /* No comment provided by engineer. */ "Messages" = "Berichten"; @@ -2340,20 +3258,59 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "Berichten van %@ worden getoond!"; +/* alert message */ +"Messages in this chat will never be deleted." = "Berichten in deze chat zullen nooit worden verwijderd."; + +/* No comment provided by engineer. */ +"Messages received" = "Berichten ontvangen"; + +/* No comment provided by engineer. */ +"Messages sent" = "Berichten verzonden"; + +/* alert message */ +"Messages were deleted after you selected them." = "Berichten zijn verwijderd nadat u ze had geselecteerd."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Berichten, bestanden en oproepen worden beschermd door **end-to-end codering** met perfecte voorwaartse geheimhouding, afwijzing en inbraakherstel."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Berichten, bestanden en oproepen worden beschermd door **kwantumbestendige e2e encryptie** met perfecte voorwaartse geheimhouding, afwijzing en inbraakherstel."; + +/* No comment provided by engineer. */ +"Migrate device" = "Apparaat migreren"; + +/* No comment provided by engineer. */ +"Migrate from another device" = "Migreer vanaf een ander apparaat"; + +/* No comment provided by engineer. */ +"Migrate here" = "Migreer hierheen"; + +/* No comment provided by engineer. */ +"Migrate to another device" = "Migreer naar een ander apparaat"; + +/* No comment provided by engineer. */ +"Migrate to another device via QR code." = "Migreer naar een ander apparaat via QR-code."; + +/* No comment provided by engineer. */ +"Migrating" = "Migreren"; + /* No comment provided by engineer. */ "Migrating database archive…" = "Database archief migreren…"; +/* No comment provided by engineer. */ +"Migration complete" = "Migratie voltooid"; + /* No comment provided by engineer. */ "Migration error:" = "Migratiefout:"; /* No comment provided by engineer. */ -"Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat)." = "Migratie mislukt. Tik hieronder op **Overslaan** om de huidige database te blijven gebruiken. Meld het probleem aan de app-ontwikkelaars via chat of e-mail [chat@simplex.chat](mailto:chat@simplex.chat)."; +"Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat)." = "Migratie mislukt. Tik hier onder op **Overslaan** om de huidige database te blijven gebruiken. Meld het probleem aan de app-ontwikkelaars via chat of e-mail [chat@simplex.chat](mailto:chat@simplex.chat)."; /* No comment provided by engineer. */ "Migration is completed" = "Migratie is voltooid"; /* No comment provided by engineer. */ -"Migrations: %@" = "Migraties: %@"; +"Migrations:" = "Migraties:"; /* time unit */ "minutes" = "minuten"; @@ -2376,63 +3333,99 @@ /* marked deleted chat item preview text */ "moderated by %@" = "gemodereerd door %@"; +/* member role */ +"moderator" = "moderator"; + /* time unit */ "months" = "maanden"; +/* swipe action */ +"More" = "Meer"; + /* No comment provided by engineer. */ "More improvements are coming soon!" = "Meer verbeteringen volgen snel!"; +/* No comment provided by engineer. */ +"More reliable network connection." = "Betrouwbaardere netwerkverbinding."; + +/* No comment provided by engineer. */ +"More reliable notifications" = "Betrouwbaardere meldingen"; + /* item status description */ "Most likely this connection is deleted." = "Hoogstwaarschijnlijk is deze verbinding verwijderd."; /* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "Hoogstwaarschijnlijk heeft dit contact de verbinding met jou verwijderd."; +"Multiple chat profiles" = "Meerdere chatprofielen"; -/* No comment provided by engineer. */ -"Multiple chat profiles" = "Meerdere chat profielen"; - -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "Dempen"; +/* notification label action */ +"Mute all" = "Alles dempen"; + /* No comment provided by engineer. */ "Muted when inactive!" = "Gedempt wanneer inactief!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "Naam"; /* No comment provided by engineer. */ "Network & servers" = "Netwerk & servers"; +/* No comment provided by engineer. */ +"Network connection" = "Netwerkverbinding"; + +/* No comment provided by engineer. */ +"Network decentralization" = "Netwerk decentralisatie"; + +/* snd error text */ +"Network issues - message expired after many attempts to send it." = "Netwerkproblemen - bericht is verlopen na vele pogingen om het te verzenden."; + +/* No comment provided by engineer. */ +"Network management" = "Netwerkbeheer"; + +/* No comment provided by engineer. */ +"Network operator" = "Netwerkbeheerder"; + /* No comment provided by engineer. */ "Network settings" = "Netwerk instellingen"; /* No comment provided by engineer. */ "Network status" = "Netwerk status"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "nooit"; +/* token status text */ +"New" = "Nieuw"; + /* No comment provided by engineer. */ "New chat" = "Nieuw gesprek"; +/* No comment provided by engineer. */ +"New chat experience 🎉" = "Nieuwe chatervaring 🎉"; + /* notification */ "New contact request" = "Nieuw contactverzoek"; /* notification */ "New contact:" = "Nieuw contact:"; -/* No comment provided by engineer. */ -"New database archive" = "Nieuw database archief"; - /* No comment provided by engineer. */ "New desktop app!" = "Nieuwe desktop app!"; /* No comment provided by engineer. */ "New display name" = "Nieuwe weergavenaam"; +/* notification */ +"New events" = "Nieuwe gebeurtenissen"; + /* No comment provided by engineer. */ "New in %@" = "Nieuw in %@"; +/* No comment provided by engineer. */ +"New media options" = "Nieuwe media-opties"; + /* No comment provided by engineer. */ "New member role" = "Nieuwe leden rol"; @@ -2448,6 +3441,15 @@ /* No comment provided by engineer. */ "New passphrase…" = "Nieuw wachtwoord…"; +/* No comment provided by engineer. */ +"New server" = "Nieuwe server"; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used every time you start the app." = "Elke keer dat u de app start, worden er nieuwe SOCKS-inloggegevens gebruikt."; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used for each server." = "Voor elke server worden nieuwe SOCKS-inloggegevens gebruikt."; + /* pref value */ "no" = "Nee"; @@ -2457,6 +3459,15 @@ /* Authentication unavailable */ "No app password" = "Geen app wachtwoord"; +/* No comment provided by engineer. */ +"No chats" = "Geen chats"; + +/* No comment provided by engineer. */ +"No chats found" = "Geen chats gevonden"; + +/* No comment provided by engineer. */ +"No chats in list %@" = "Geen chats in lijst %@"; + /* No comment provided by engineer. */ "No contacts selected" = "Geen contacten geselecteerd"; @@ -2469,11 +3480,14 @@ /* No comment provided by engineer. */ "No device token!" = "Geen apparaattoken!"; +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Nog geen directe verbinding, bericht wordt doorgestuurd door beheerder."; + /* No comment provided by engineer. */ "no e2e encryption" = "geen e2e versleuteling"; /* No comment provided by engineer. */ -"No filtered chats" = "Geen gefilterde gesprekken"; +"No filtered chats" = "Geen gefilterde chats"; /* No comment provided by engineer. */ "No group!" = "Groep niet gevonden!"; @@ -2481,24 +3495,87 @@ /* No comment provided by engineer. */ "No history" = "Geen geschiedenis"; +/* No comment provided by engineer. */ +"No info, try to reload" = "Geen info, probeer opnieuw te laden"; + +/* servers error */ +"No media & file servers." = "Geen media- en bestandsservers."; + +/* No comment provided by engineer. */ +"No message" = "Geen bericht"; + +/* servers error */ +"No message servers." = "Geen berichtenservers."; + +/* No comment provided by engineer. */ +"No network connection" = "Geen netwerkverbinding"; + +/* No comment provided by engineer. */ +"No permission to record speech" = "Geen toestemming om spraak op te nemen"; + +/* No comment provided by engineer. */ +"No permission to record video" = "Geen toestemming om video op te nemen"; + /* No comment provided by engineer. */ "No permission to record voice message" = "Geen toestemming om spraakbericht op te nemen"; +/* No comment provided by engineer. */ +"No push server" = "Lokaal"; + /* No comment provided by engineer. */ "No received or sent files" = "Geen ontvangen of verzonden bestanden"; +/* servers error */ +"No servers for private message routing." = "Geen servers voor het routeren van privéberichten."; + +/* servers error */ +"No servers to receive files." = "Geen servers om bestanden te ontvangen."; + +/* servers error */ +"No servers to receive messages." = "Geen servers om berichten te ontvangen."; + +/* servers error */ +"No servers to send files." = "Geen servers om bestanden te verzenden."; + /* copied message info in history */ "no text" = "geen tekst"; +/* alert title */ +"No token!" = "Geen token!"; + +/* No comment provided by engineer. */ +"No unread chats" = "Geen ongelezen chats"; + +/* No comment provided by engineer. */ +"No user identifiers." = "Geen gebruikers-ID's."; + /* No comment provided by engineer. */ "Not compatible!" = "Niet compatibel!"; +/* No comment provided by engineer. */ +"Notes" = "Notities"; + +/* No comment provided by engineer. */ +"Nothing selected" = "Niets geselecteerd"; + +/* alert title */ +"Nothing to forward!" = "Niets om door te sturen!"; + /* No comment provided by engineer. */ "Notifications" = "Meldingen"; /* No comment provided by engineer. */ "Notifications are disabled!" = "Meldingen zijn uitgeschakeld!"; +/* alert title */ +"Notifications error" = "Meldingsfout"; + +/* No comment provided by engineer. */ +"Notifications privacy" = "Privacy van meldingen"; + +/* alert title */ +"Notifications status" = "Meldingsstatus"; + /* No comment provided by engineer. */ "Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Nu kunnen beheerders: \n- berichten van leden verwijderen.\n- schakel leden uit (\"waarnemer\" rol)"; @@ -2506,11 +3583,11 @@ "observer" = "Waarnemer"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "uit"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Uit"; /* feature offered item */ @@ -2519,7 +3596,7 @@ /* feature offered item */ "offered %@: %@" = "voorgesteld %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "OK"; /* No comment provided by engineer. */ @@ -2528,9 +3605,6 @@ /* No comment provided by engineer. */ "Old database" = "Oude database"; -/* No comment provided by engineer. */ -"Old database archive" = "Oud database archief"; - /* group pref value */ "on" = "aan"; @@ -2538,16 +3612,22 @@ "One-time invitation link" = "Eenmalige uitnodiging link"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "Onion hosts zullen nodig zijn voor verbinding. Vereist het inschakelen van VPN."; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "Onion hosts zullen nodig zijn voor verbinding.\nVereist het inschakelen van VPN."; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "Onion hosts worden gebruikt indien beschikbaar. Vereist het inschakelen van VPN."; +"Onion hosts will be used when available.\nRequires compatible VPN." = "Onion hosts worden gebruikt indien beschikbaar.\nVereist het inschakelen van VPN."; /* No comment provided by engineer. */ "Onion hosts will not be used." = "Onion hosts worden niet gebruikt."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Alleen client apparaten slaan gebruikers profielen, contacten, groepen en berichten op die zijn verzonden met **2-laags end-to-end-codering**."; +"Only chat owners can change preferences." = "Alleen chateigenaren kunnen voorkeuren wijzigen."; + +/* No comment provided by engineer. */ +"Only client devices store user profiles, contacts, groups, and messages." = "Alleen client apparaten slaan gebruikers profielen, contacten, groepen en berichten op die zijn verzonden met **2-laags end-to-end-codering**."; + +/* No comment provided by engineer. */ +"Only delete conversation" = "Alleen conversatie verwijderen"; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Alleen groep eigenaren kunnen groep voorkeuren wijzigen."; @@ -2559,10 +3639,16 @@ "Only group owners can enable voice messages." = "Alleen groep eigenaren kunnen spraak berichten inschakelen."; /* No comment provided by engineer. */ -"Only you can add message reactions." = "Alleen jij kunt berichtreacties toevoegen."; +"Only sender and moderators see it" = "Alleen de verzender en moderators zien het"; /* No comment provided by engineer. */ -"Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours)" = "Alleen jij kunt berichten onomkeerbaar verwijderen (je contact kan ze markeren voor verwijdering). (24 uur)"; +"Only you and moderators see it" = "Alleen jij en moderators zien het"; + +/* No comment provided by engineer. */ +"Only you can add message reactions." = "Alleen jij kunt bericht reacties toevoegen."; + +/* No comment provided by engineer. */ +"Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours)" = "Alleen jij kunt berichten definitief verwijderen (je contact kan ze markeren voor verwijdering). (24 uur)"; /* No comment provided by engineer. */ "Only you can make calls." = "Alleen jij kunt bellen."; @@ -2574,7 +3660,7 @@ "Only you can send voice messages." = "Alleen jij kunt spraak berichten verzenden."; /* No comment provided by engineer. */ -"Only your contact can add message reactions." = "Alleen uw contact kan berichtreacties toevoegen."; +"Only your contact can add message reactions." = "Alleen uw contact kan bericht reacties toevoegen."; /* No comment provided by engineer. */ "Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours)" = "Alleen uw contact kan berichten onherroepelijk verwijderen (u kunt ze markeren voor verwijdering). (24 uur)"; @@ -2588,39 +3674,78 @@ /* No comment provided by engineer. */ "Only your contact can send voice messages." = "Alleen uw contact kan spraak berichten verzenden."; -/* No comment provided by engineer. */ +/* alert action */ "Open" = "Open"; /* No comment provided by engineer. */ -"Open chat" = "Gesprekken openen"; +"Open changes" = "Wijzigingen openen"; + +/* No comment provided by engineer. */ +"Open chat" = "Chat openen"; /* authentication reason */ "Open chat console" = "Chat console openen"; +/* No comment provided by engineer. */ +"Open conditions" = "Open voorwaarden"; + /* No comment provided by engineer. */ "Open group" = "Open groep"; +/* authentication reason */ +"Open migration to another device" = "Open de migratie naar een ander apparaat"; + /* No comment provided by engineer. */ "Open Settings" = "Open instellingen"; -/* authentication reason */ -"Open user profiles" = "Gebruikers profielen openen"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "Open-source protocol en code. Iedereen kan de servers draaien."; - /* No comment provided by engineer. */ "Opening app…" = "App openen…"; +/* No comment provided by engineer. */ +"Operator" = "Operator"; + +/* alert title */ +"Operator server" = "Operatorserver"; + +/* No comment provided by engineer. */ +"Or import archive file" = "Of importeer archiefbestand"; + +/* No comment provided by engineer. */ +"Or paste archive link" = "Of plak de archief link"; + /* No comment provided by engineer. */ "Or scan QR code" = "Of scan de QR-code"; +/* No comment provided by engineer. */ +"Or securely share this file link" = "Of deel deze bestands link veilig"; + /* No comment provided by engineer. */ "Or show this code" = "Of laat deze code zien"; +/* No comment provided by engineer. */ +"Or to share privately" = "Of om privé te delen"; + +/* No comment provided by engineer. */ +"Organize chats into lists" = "Organiseer chats in lijsten"; + +/* No comment provided by engineer. */ +"other" = "overig"; + +/* No comment provided by engineer. */ +"Other" = "Ander"; + +/* No comment provided by engineer. */ +"other errors" = "overige fouten"; + +/* alert message */ +"Other file errors:\n%@" = "Andere bestandsfouten:\n%@"; + /* member role */ "owner" = "Eigenaar"; +/* feature role */ +"owners" = "eigenaren"; + /* No comment provided by engineer. */ "Passcode" = "Toegangscode"; @@ -2636,6 +3761,9 @@ /* No comment provided by engineer. */ "Passcode set!" = "Toegangscode ingesteld!"; +/* No comment provided by engineer. */ +"Password" = "Wachtwoord"; + /* No comment provided by engineer. */ "Password to show" = "Wachtwoord om weer te geven"; @@ -2658,23 +3786,41 @@ "peer-to-peer" = "peer-to-peer"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "Mensen kunnen alleen verbinding met u maken via de links die u deelt."; +"pending" = "In behandeling"; /* No comment provided by engineer. */ -"Periodically" = "Periodiek"; +"Pending" = "in behandeling"; + +/* No comment provided by engineer. */ +"pending approval" = "in afwachting van goedkeuring"; + +/* No comment provided by engineer. */ +"Periodic" = "Periodiek"; /* message decrypt error item */ "Permanent decryption error" = "Decodering fout"; +/* No comment provided by engineer. */ +"Picture-in-picture calls" = "Beeld-in-beeld oproepen"; + /* No comment provided by engineer. */ "PING count" = "PING count"; /* No comment provided by engineer. */ "PING interval" = "PING interval"; +/* No comment provided by engineer. */ +"Play from the chat list." = "Afspelen via de chat lijst."; + +/* No comment provided by engineer. */ +"Please ask your contact to enable calls." = "Vraag uw contactpersoon om oproepen in te schakelen."; + /* No comment provided by engineer. */ "Please ask your contact to enable sending voice messages." = "Vraag uw contact om het verzenden van spraak berichten in te schakelen."; +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Controleer of mobiel en desktop met hetzelfde lokale netwerk zijn verbonden en of de desktopfirewall de verbinding toestaat.\nDeel eventuele andere problemen met de ontwikkelaars."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Controleer of u de juiste link heeft gebruikt of vraag uw contact om u een andere te sturen."; @@ -2684,6 +3830,9 @@ /* No comment provided by engineer. */ "Please check yours and your contact preferences." = "Controleer de uwe en uw contact voorkeuren."; +/* No comment provided by engineer. */ +"Please confirm that network settings are correct for this device." = "Controleer of de netwerk instellingen correct zijn voor dit apparaat."; + /* No comment provided by engineer. */ "Please contact developers.\nError: %@" = "Neem contact op met ontwikkelaars.\nFout: %@"; @@ -2706,14 +3855,26 @@ "Please restart the app and migrate the database to enable push notifications." = "Start de app opnieuw en migreer de database om push meldingen in te schakelen."; /* No comment provided by engineer. */ -"Please store passphrase securely, you will NOT be able to access chat if you lose it." = "Sla het wachtwoord veilig op. Als u deze kwijtraakt, heeft u GEEN toegang tot de gesprekken."; +"Please store passphrase securely, you will NOT be able to access chat if you lose it." = "Sla het wachtwoord veilig op. Als u deze kwijtraakt, heeft u GEEN toegang tot de chats."; /* No comment provided by engineer. */ "Please store passphrase securely, you will NOT be able to change it if you lose it." = "Bewaar het wachtwoord veilig, u kunt deze NIET wijzigen als u het kwijtraakt."; +/* token info */ +"Please try to disable and re-enable notfications." = "Probeer meldingen uit en weer in te schakelen."; + +/* token info */ +"Please wait for token activation to complete." = "Wacht tot de tokenactivering voltooid is."; + +/* token info */ +"Please wait for token to be registered." = "Wacht tot het token is geregistreerd."; + /* No comment provided by engineer. */ "Polish interface" = "Poolse interface"; +/* No comment provided by engineer. */ +"Port" = "Poort"; + /* server test error */ "Possibly, certificate fingerprint in server address is incorrect" = "Mogelijk is de certificaat vingerafdruk in het server adres onjuist"; @@ -2721,26 +3882,53 @@ "Preserve the last message draft, with attachments." = "Bewaar het laatste berichtconcept, met bijlagen."; /* No comment provided by engineer. */ -"Preset server" = "Vooraf ingestelde server"; +"Preset server address" = "Vooraf ingesteld server adres"; /* No comment provided by engineer. */ -"Preset server address" = "Vooraf ingesteld server adres"; +"Preset servers" = "Vooraf ingestelde servers"; /* No comment provided by engineer. */ "Preview" = "Voorbeeld"; +/* No comment provided by engineer. */ +"Previously connected servers" = "Eerder verbonden servers"; + /* No comment provided by engineer. */ "Privacy & security" = "Privacy en beveiliging"; +/* No comment provided by engineer. */ +"Privacy for your customers." = "Privacy voor uw klanten."; + +/* No comment provided by engineer. */ +"Privacy policy and conditions of use." = "Privacybeleid en gebruiksvoorwaarden."; + /* No comment provided by engineer. */ "Privacy redefined" = "Privacy opnieuw gedefinieerd"; +/* No comment provided by engineer. */ +"Private chats, groups and your contacts are not accessible to server operators." = "Privéchats, groepen en uw contacten zijn niet toegankelijk voor serverbeheerders."; + /* No comment provided by engineer. */ "Private filenames" = "Privé bestandsnamen"; +/* No comment provided by engineer. */ +"Private media file names." = "Namen van persoonlijke mediabestanden."; + +/* No comment provided by engineer. */ +"Private message routing" = "Routering van privéberichten"; + +/* No comment provided by engineer. */ +"Private message routing 🚀" = "Routing van privéberichten🚀"; + /* name of notes to self */ "Private notes" = "Privé notities"; +/* No comment provided by engineer. */ +"Private routing" = "Privéroutering"; + +/* No comment provided by engineer. */ +"Private routing error" = "Fout in privéroutering"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profiel- en serververbindingen"; @@ -2748,29 +3936,32 @@ "Profile image" = "profielfoto"; /* No comment provided by engineer. */ -"Profile name" = "Profielnaam"; - -/* No comment provided by engineer. */ -"Profile name:" = "Profielnaam:"; +"Profile images" = "Profiel afbeeldingen"; /* No comment provided by engineer. */ "Profile password" = "Profiel wachtwoord"; /* No comment provided by engineer. */ +"Profile theme" = "Profiel thema"; + +/* alert message */ "Profile update will be sent to your contacts." = "Profiel update wordt naar uw contacten verzonden."; /* No comment provided by engineer. */ "Prohibit audio/video calls." = "Audio/video gesprekken verbieden."; /* No comment provided by engineer. */ -"Prohibit irreversible message deletion." = "Verbied het onomkeerbaar verwijderen van berichten."; +"Prohibit irreversible message deletion." = "Verbied het definitief verwijderen van berichten."; /* No comment provided by engineer. */ -"Prohibit message reactions." = "Berichtreacties verbieden."; +"Prohibit message reactions." = "Bericht reacties verbieden."; /* No comment provided by engineer. */ "Prohibit messages reactions." = "Berichten reacties verbieden."; +/* No comment provided by engineer. */ +"Prohibit reporting messages to moderators." = "Het melden van berichten aan moderators is niet toegestaan."; + /* No comment provided by engineer. */ "Prohibit sending direct messages to members." = "Verbied het sturen van directe berichten naar leden."; @@ -2780,6 +3971,9 @@ /* No comment provided by engineer. */ "Prohibit sending files and media." = "Verbied het verzenden van bestanden en media."; +/* No comment provided by engineer. */ +"Prohibit sending SimpleX links." = "Verbied het verzenden van SimpleX-links"; + /* No comment provided by engineer. */ "Prohibit sending voice messages." = "Verbieden het verzenden van spraak berichten."; @@ -2787,7 +3981,13 @@ "Protect app screen" = "App scherm verbergen"; /* No comment provided by engineer. */ -"Protect your chat profiles with a password!" = "Bescherm je chat profielen met een wachtwoord!"; +"Protect IP address" = "Bescherm het IP-adres"; + +/* No comment provided by engineer. */ +"Protect your chat profiles with a password!" = "Bescherm je chatprofielen met een wachtwoord!"; + +/* No comment provided by engineer. */ +"Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." = "Bescherm uw IP-adres tegen de berichtenrelais die door uw contacten zijn gekozen.\nSchakel dit in in *Netwerk en servers*-instellingen."; /* No comment provided by engineer. */ "Protocol timeout" = "Protocol timeout"; @@ -2795,26 +3995,47 @@ /* No comment provided by engineer. */ "Protocol timeout per KB" = "Protocol timeout per KB"; +/* No comment provided by engineer. */ +"Proxied" = "Proxied"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Proxied servers"; + +/* No comment provided by engineer. */ +"Proxy requires password" = "Proxy vereist wachtwoord"; + /* No comment provided by engineer. */ "Push notifications" = "Push meldingen"; +/* No comment provided by engineer. */ +"Push server" = "Push server"; + +/* chat item text */ +"quantum resistant e2e encryption" = "quantum bestendige e2e-codering"; + +/* No comment provided by engineer. */ +"Quantum resistant encryption" = "quantum bestendige encryptie"; + /* No comment provided by engineer. */ "Rate the app" = "Beoordeel de app"; +/* No comment provided by engineer. */ +"Reachable chat toolbar" = "Toegankelijke chatwerkbalk"; + /* chat item menu */ "React…" = "Reageer…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "Lees"; /* No comment provided by engineer. */ "Read more" = "Lees meer"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Lees meer in de [Gebruikershandleiding](https://simplex.chat/docs/guide/app-settings.html#uw-simplex-contactadres)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Lees meer in de [Gebruikershandleiding](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Lees meer in de [Gebruikershandleiding](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "Lees meer in de [Gebruikershandleiding](https://simplex.chat/docs/guide/app-settings.html#uw-simplex-contactadres)."; /* No comment provided by engineer. */ "Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Lees meer in de [Gebruikershandleiding](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; @@ -2823,10 +4044,10 @@ "Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Lees meer in onze [GitHub-repository](https://github.com/simplex-chat/simplex-chat#readme)."; /* No comment provided by engineer. */ -"Read more in our GitHub repository." = "Lees meer in onze GitHub repository."; +"Receipts are disabled" = "Bevestigingen zijn uitgeschakeld"; /* No comment provided by engineer. */ -"Receipts are disabled" = "Bevestigingen zijn uitgeschakeld"; +"Receive errors" = "Fouten ontvangen"; /* No comment provided by engineer. */ "received answer…" = "antwoord gekregen…"; @@ -2846,6 +4067,15 @@ /* message info title */ "Received message" = "Ontvangen bericht"; +/* No comment provided by engineer. */ +"Received messages" = "Ontvangen berichten"; + +/* No comment provided by engineer. */ +"Received reply" = "Antwoord ontvangen"; + +/* No comment provided by engineer. */ +"Received total" = "Totaal ontvangen"; + /* No comment provided by engineer. */ "Receiving address will be changed to a different server. Address change will complete after sender comes online." = "Het ontvangstadres wordt gewijzigd naar een andere server. Adres wijziging wordt voltooid nadat de afzender online is."; @@ -2858,12 +4088,30 @@ /* No comment provided by engineer. */ "Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)." = "Recente geschiedenis en verbeterde [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)."; +/* No comment provided by engineer. */ +"Recipient(s) can't see who this message is from." = "Ontvanger(s) kunnen niet zien van wie dit bericht afkomstig is."; + /* No comment provided by engineer. */ "Recipients see updates as you type them." = "Ontvangers zien updates terwijl u ze typt."; +/* No comment provided by engineer. */ +"Reconnect" = "opnieuw verbinden"; + /* No comment provided by engineer. */ "Reconnect all connected servers to force message delivery. It uses additional traffic." = "Verbind alle verbonden servers opnieuw om de bezorging van berichten af te dwingen. Het maakt gebruik van extra data."; +/* No comment provided by engineer. */ +"Reconnect all servers" = "Maak opnieuw verbinding met alle servers"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "Alle servers opnieuw verbinden?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "Maak opnieuw verbinding met de server om de bezorging van berichten te forceren. Er wordt gebruik gemaakt van extra verkeer.Maak opnieuw verbinding met de server om de bezorging van berichten te forceren. Er wordt gebruik gemaakt van extra data."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "Server opnieuw verbinden?"; + /* No comment provided by engineer. */ "Reconnect servers?" = "Servers opnieuw verbinden?"; @@ -2876,7 +4124,17 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "Verminderd batterijgebruik"; -/* reject incoming call via notification */ +/* No comment provided by engineer. */ +"Register" = "Register"; + +/* token info */ +"Register notification token?" = "Meldingstoken registreren?"; + +/* token status text */ +"Registered" = "Geregistreerd"; + +/* reject incoming call via notification +swipe action */ "Reject" = "Afwijzen"; /* No comment provided by engineer. */ @@ -2885,6 +4143,9 @@ /* No comment provided by engineer. */ "Reject contact request" = "Contactverzoek afwijzen"; +/* No comment provided by engineer. */ +"rejected" = "afgewezen"; + /* call status */ "rejected call" = "geweigerde oproep"; @@ -2897,6 +4158,12 @@ /* No comment provided by engineer. */ "Remove" = "Verwijderen"; +/* No comment provided by engineer. */ +"Remove archive?" = "Archief verwijderen?"; + +/* No comment provided by engineer. */ +"Remove image" = "Verwijder afbeelding"; + /* No comment provided by engineer. */ "Remove member" = "Lid verwijderen"; @@ -2933,26 +4200,83 @@ /* No comment provided by engineer. */ "Repeat connection request?" = "Verbindingsverzoek herhalen?"; +/* No comment provided by engineer. */ +"Repeat download" = "Herhaal het downloaden"; + +/* No comment provided by engineer. */ +"Repeat import" = "Herhaal import"; + /* No comment provided by engineer. */ "Repeat join request?" = "Deelnameverzoek herhalen?"; +/* No comment provided by engineer. */ +"Repeat upload" = "Herhaal het uploaden"; + /* chat item action */ "Reply" = "Antwoord"; +/* chat item action */ +"Report" = "rapporteren"; + +/* report reason */ +"Report content: only group moderators will see it." = "Inhoud melden: alleen groepsmoderators kunnen dit zien."; + +/* report reason */ +"Report member profile: only group moderators will see it." = "Rapporteer ledenprofiel: alleen groepsmoderators kunnen dit zien."; + +/* report reason */ +"Report other: only group moderators will see it." = "Anders melden: alleen groepsmoderators kunnen het zien."; + +/* No comment provided by engineer. */ +"Report reason?" = "Reden melding?"; + +/* report reason */ +"Report spam: only group moderators will see it." = "Spam melden: alleen groepsmoderators kunnen het zien."; + +/* report reason */ +"Report violation: only group moderators will see it." = "Rapporteer overtreding: alleen groepsmoderators kunnen dit zien."; + +/* report in notification */ +"Report: %@" = "rapporteer: %@"; + +/* No comment provided by engineer. */ +"Reporting messages to moderators is prohibited." = "Het is niet toegestaan om berichten aan moderators te melden."; + +/* No comment provided by engineer. */ +"Reports" = "Rapporten"; + +/* chat list item title */ +"requested to connect" = "verzocht om verbinding te maken"; + /* No comment provided by engineer. */ "Required" = "Vereist"; /* No comment provided by engineer. */ "Reset" = "Resetten"; +/* No comment provided by engineer. */ +"Reset all hints" = "Alle hints resetten"; + +/* No comment provided by engineer. */ +"Reset all statistics" = "Reset alle statistieken"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "Alle statistieken resetten?"; + /* No comment provided by engineer. */ "Reset colors" = "Kleuren resetten"; +/* No comment provided by engineer. */ +"Reset to app theme" = "Terugzetten naar app thema"; + /* No comment provided by engineer. */ "Reset to defaults" = "Resetten naar standaardwaarden"; /* No comment provided by engineer. */ -"Restart the app to create a new chat profile" = "Start de app opnieuw om een nieuw chat profiel aan te maken"; +"Reset to user theme" = "Terugzetten naar gebruikersthema"; + +/* No comment provided by engineer. */ +"Restart the app to create a new chat profile" = "Start de app opnieuw om een nieuw chatprofiel aan te maken"; /* No comment provided by engineer. */ "Restart the app to use imported chat database" = "Start de app opnieuw om de geïmporteerde chat database te gebruiken"; @@ -2976,7 +4300,7 @@ "Reveal" = "Onthullen"; /* No comment provided by engineer. */ -"Revert" = "Terugdraaien"; +"Review conditions" = "Voorwaarden bekijken"; /* No comment provided by engineer. */ "Revoke" = "Intrekken"; @@ -2993,53 +4317,72 @@ /* No comment provided by engineer. */ "Run chat" = "Chat uitvoeren"; -/* chat item action */ +/* No comment provided by engineer. */ +"Safely receive files" = "Veilig bestanden ontvangen"; + +/* No comment provided by engineer. */ +"Safer groups" = "Veiligere groepen"; + +/* alert button +chat item action */ "Save" = "Opslaan"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Bewaar (en informeer contacten)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "Opslaan en Contact melden"; /* No comment provided by engineer. */ "Save and notify group members" = "Opslaan en groep leden melden"; +/* No comment provided by engineer. */ +"Save and reconnect" = "Opslaan en opnieuw verbinden"; + /* No comment provided by engineer. */ "Save and update group profile" = "Groep profiel opslaan en bijwerken"; -/* No comment provided by engineer. */ -"Save archive" = "Bewaar archief"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Sla instellingen voor automatisch accepteren op"; - /* No comment provided by engineer. */ "Save group profile" = "Groep profiel opslaan"; /* No comment provided by engineer. */ -"Save passphrase and open chat" = "Bewaar het wachtwoord en open je gesprekken"; +"Save list" = "Lijst opslaan"; + +/* No comment provided by engineer. */ +"Save passphrase and open chat" = "Wachtwoord opslaan en open je chats"; /* No comment provided by engineer. */ "Save passphrase in Keychain" = "Sla het wachtwoord op in de Keychain"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "Voorkeuren opslaan?"; /* No comment provided by engineer. */ "Save profile password" = "Bewaar profiel wachtwoord"; /* No comment provided by engineer. */ -"Save servers" = "Bewaar servers"; +"Save servers" = "Servers opslaan"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "Servers opslaan?"; /* No comment provided by engineer. */ -"Save settings?" = "Instellingen opslaan?"; +"Save welcome message?" = "Welkom bericht opslaan?"; + +/* alert title */ +"Save your profile?" = "Uw profiel opslaan?"; /* No comment provided by engineer. */ -"Save welcome message?" = "Welkomst bericht opslaan?"; +"saved" = "opgeslagen"; + +/* No comment provided by engineer. */ +"Saved" = "Opgeslagen"; + +/* No comment provided by engineer. */ +"Saved from" = "Opgeslagen van"; + +/* No comment provided by engineer. */ +"saved from %@" = "opgeslagen van %@"; /* message info title */ "Saved message" = "Opgeslagen bericht"; @@ -3047,6 +4390,15 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Opgeslagen WebRTC ICE servers worden verwijderd"; +/* No comment provided by engineer. */ +"Saving %lld messages" = "%lld berichten opslaan"; + +/* No comment provided by engineer. */ +"Scale" = "Schaal"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Link scannen/plakken"; + /* No comment provided by engineer. */ "Scan code" = "Code scannen"; @@ -3062,6 +4414,9 @@ /* No comment provided by engineer. */ "Scan server QR code" = "Scan server QR-code"; +/* No comment provided by engineer. */ +"search" = "zoekopdracht"; + /* No comment provided by engineer. */ "Search" = "Zoeken"; @@ -3074,6 +4429,9 @@ /* network option */ "sec" = "sec"; +/* No comment provided by engineer. */ +"Secondary" = "Secundair"; + /* time unit */ "seconds" = "seconden"; @@ -3083,6 +4441,9 @@ /* server test step */ "Secure queue" = "Veilige wachtrij"; +/* No comment provided by engineer. */ +"Secured" = "Beveiligd"; + /* No comment provided by engineer. */ "Security assessment" = "Beveiligingsbeoordeling"; @@ -3092,9 +4453,18 @@ /* chat item text */ "security code changed" = "beveiligingscode gewijzigd"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "Selecteer"; +/* No comment provided by engineer. */ +"Select chat profile" = "Selecteer chatprofiel"; + +/* No comment provided by engineer. */ +"Selected %lld" = "%lld geselecteerd"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Geselecteerde chat voorkeuren verbieden dit bericht."; + /* No comment provided by engineer. */ "Self-destruct" = "Zelfvernietiging"; @@ -3119,26 +4489,35 @@ /* No comment provided by engineer. */ "send direct message" = "stuur een direct bericht"; -/* No comment provided by engineer. */ -"Send direct message" = "Direct bericht sturen"; - /* No comment provided by engineer. */ "Send direct message to connect" = "Stuur een direct bericht om verbinding te maken"; /* No comment provided by engineer. */ "Send disappearing message" = "Stuur een verdwijnend bericht"; +/* No comment provided by engineer. */ +"Send errors" = "Verzend fouten"; + /* No comment provided by engineer. */ "Send link previews" = "Link voorbeelden verzenden"; /* No comment provided by engineer. */ "Send live message" = "Stuur een livebericht"; +/* No comment provided by engineer. */ +"Send message to enable calls." = "Stuur een bericht om oproepen mogelijk te maken."; + +/* No comment provided by engineer. */ +"Send messages directly when IP address is protected and your or destination server does not support private routing." = "Stuur berichten rechtstreeks als het IP-adres beschermd is en uw of bestemmingsserver geen privéroutering ondersteunt."; + +/* No comment provided by engineer. */ +"Send messages directly when your or destination server does not support private routing." = "Stuur berichten rechtstreeks wanneer uw of de doelserver geen privéroutering ondersteunt."; + /* No comment provided by engineer. */ "Send notifications" = "Meldingen verzenden"; /* No comment provided by engineer. */ -"Send notifications:" = "Meldingen verzenden:"; +"Send private reports" = "Rapporteer privé"; /* No comment provided by engineer. */ "Send questions and ideas" = "Stuur vragen en ideeën"; @@ -3152,7 +4531,7 @@ /* No comment provided by engineer. */ "Send up to 100 last messages to new members." = "Stuur tot 100 laatste berichten naar nieuwe leden."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Afzender heeft bestandsoverdracht geannuleerd."; /* No comment provided by engineer. */ @@ -3188,15 +4567,57 @@ /* copied message info */ "Sent at: %@" = "Verzonden op: %@"; +/* No comment provided by engineer. */ +"Sent directly" = "Direct verzonden"; + /* notification */ "Sent file event" = "Verzonden bestandsgebeurtenis"; /* message info title */ "Sent message" = "Verzonden bericht"; +/* No comment provided by engineer. */ +"Sent messages" = "Verzonden berichten"; + /* No comment provided by engineer. */ "Sent messages will be deleted after set time." = "Verzonden berichten worden na ingestelde tijd verwijderd."; +/* No comment provided by engineer. */ +"Sent reply" = "Antwoord verzonden"; + +/* No comment provided by engineer. */ +"Sent total" = "Totaal verzonden"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Verzonden via proxy"; + +/* No comment provided by engineer. */ +"Server" = "Server"; + +/* alert message */ +"Server added to operator %@." = "Server toegevoegd aan operator %@."; + +/* No comment provided by engineer. */ +"Server address" = "Server adres"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "Serveradres is incompatibel met netwerkinstellingen: %@."; + +/* srv error text. */ +"Server address is incompatible with network settings." = "Serveradres is niet compatibel met netwerkinstellingen."; + +/* alert title */ +"Server operator changed." = "Serveroperator gewijzigd."; + +/* No comment provided by engineer. */ +"Server operators" = "Serverbeheerders"; + +/* alert title */ +"Server protocol changed." = "Serverprotocol gewijzigd."; + +/* queue info */ +"server queue info: %@\n\nlast received msg: %@" = "informatie over serverwachtrij: %1$@\n\nlaatst ontvangen bericht: %2$@"; + /* server test error */ "Server requires authorization to create queues, check password" = "Server vereist autorisatie om wachtrijen te maken, controleer wachtwoord"; @@ -3206,33 +4627,60 @@ /* No comment provided by engineer. */ "Server test failed!" = "Servertest mislukt!"; +/* No comment provided by engineer. */ +"Server type" = "Server type"; + +/* srv error text */ +"Server version is incompatible with network settings." = "Serverversie is incompatibel met netwerkinstellingen."; + +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "Serverversie is incompatibel met uw app: %@."; + /* No comment provided by engineer. */ "Servers" = "Servers"; +/* No comment provided by engineer. */ +"Servers info" = "Server informatie"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "Serverstatistieken worden gereset - dit kan niet ongedaan worden gemaakt!"; + /* No comment provided by engineer. */ "Session code" = "Sessie code"; /* No comment provided by engineer. */ "Set 1 day" = "Stel 1 dag in"; +/* No comment provided by engineer. */ +"Set chat name…" = "Stel chatnaam in…"; + /* No comment provided by engineer. */ "Set contact name…" = "Contactnaam instellen…"; +/* No comment provided by engineer. */ +"Set default theme" = "Stel het standaard thema in"; + /* No comment provided by engineer. */ "Set group preferences" = "Groep voorkeuren instellen"; /* No comment provided by engineer. */ "Set it instead of system authentication." = "Stel het in in plaats van systeemverificatie."; +/* No comment provided by engineer. */ +"Set message expiration in chats." = "Stel de berichtvervaldatum in chats in."; + /* profile update event chat item */ "set new contact address" = "nieuw contactadres instellen"; /* profile update event chat item */ -"set new profile picture" = "nieuwe profielfoto instellen"; +"set new profile picture" = "nieuwe profielfoto"; /* No comment provided by engineer. */ "Set passcode" = "Toegangscode instellen"; +/* No comment provided by engineer. */ +"Set passphrase" = "Wachtwoord instellen"; + /* No comment provided by engineer. */ "Set passphrase to export" = "Wachtwoord instellen om te exporteren"; @@ -3245,27 +4693,55 @@ /* No comment provided by engineer. */ "Settings" = "Instellingen"; -/* chat item action */ +/* alert message */ +"Settings were changed." = "Instellingen zijn gewijzigd."; + +/* No comment provided by engineer. */ +"Shape profile images" = "Vorm profiel afbeeldingen"; + +/* alert action +chat item action */ "Share" = "Deel"; /* No comment provided by engineer. */ "Share 1-time link" = "Eenmalige link delen"; +/* No comment provided by engineer. */ +"Share 1-time link with a friend" = "Deel eenmalig een link met een vriend"; + /* No comment provided by engineer. */ "Share address" = "Adres delen"; /* No comment provided by engineer. */ +"Share address publicly" = "Adres openbaar delen"; + +/* alert title */ "Share address with contacts?" = "Adres delen met contacten?"; +/* No comment provided by engineer. */ +"Share from other apps." = "Delen vanuit andere apps."; + /* No comment provided by engineer. */ "Share link" = "Deel link"; +/* No comment provided by engineer. */ +"Share profile" = "Profiel delen"; + +/* No comment provided by engineer. */ +"Share SimpleX address on social media." = "Deel het SimpleX-adres op sociale media."; + /* No comment provided by engineer. */ "Share this 1-time invite link" = "Deel deze eenmalige uitnodigingslink"; +/* No comment provided by engineer. */ +"Share to SimpleX" = "Delen op SimpleX"; + /* No comment provided by engineer. */ "Share with contacts" = "Delen met contacten"; +/* No comment provided by engineer. */ +"Show → on messages sent via private routing." = "Toon → bij berichten verzonden via privéroutering."; + /* No comment provided by engineer. */ "Show calls in phone history" = "Toon oproepen in de telefoongeschiedenis"; @@ -3275,18 +4751,39 @@ /* No comment provided by engineer. */ "Show last messages" = "Laat laatste berichten zien"; +/* No comment provided by engineer. */ +"Show message status" = "Toon berichtstatus"; + +/* No comment provided by engineer. */ +"Show percentage" = "Percentage weergeven"; + /* No comment provided by engineer. */ "Show preview" = "Toon voorbeeld"; +/* No comment provided by engineer. */ +"Show QR code" = "Toon QR-code"; + /* No comment provided by engineer. */ "Show:" = "Toon:"; +/* No comment provided by engineer. */ +"SimpleX" = "SimpleX"; + /* No comment provided by engineer. */ "SimpleX address" = "SimpleX adres"; /* No comment provided by engineer. */ "SimpleX Address" = "SimpleX adres"; +/* No comment provided by engineer. */ +"SimpleX address and 1-time links are safe to share via any messenger." = "SimpleX-adressen en eenmalige links kunnen veilig worden gedeeld via elke messenger."; + +/* No comment provided by engineer. */ +"SimpleX address or 1-time link?" = "SimpleX adres of eenmalige link?"; + +/* No comment provided by engineer. */ +"SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app." = "Simplex-chat en flux hebben een overeenkomst gemaakt om door flux geëxploiteerde servers in de app op te nemen."; + /* No comment provided by engineer. */ "SimpleX Chat security was audited by Trail of Bits." = "De beveiliging van SimpleX Chat is gecontroleerd door Trail of Bits."; @@ -3299,9 +4796,15 @@ /* simplex link type */ "SimpleX group link" = "SimpleX groep link"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "SimpleX links"; +/* No comment provided by engineer. */ +"SimpleX links are prohibited." = "SimpleX-links zijn niet toegestaan."; + +/* No comment provided by engineer. */ +"SimpleX links not allowed" = "SimpleX-links zijn niet toegestaan"; + /* No comment provided by engineer. */ "SimpleX Lock" = "SimpleX Vergrendelen"; @@ -3317,9 +4820,15 @@ /* simplex link type */ "SimpleX one-time invitation" = "Eenmalige SimpleX uitnodiging"; +/* No comment provided by engineer. */ +"SimpleX protocols reviewed by Trail of Bits." = "SimpleX-protocollen beoordeeld door Trail of Bits."; + /* No comment provided by engineer. */ "Simplified incognito mode" = "Vereenvoudigde incognitomodus"; +/* No comment provided by engineer. */ +"Size" = "Maat"; + /* No comment provided by engineer. */ "Skip" = "Overslaan"; @@ -3330,14 +4839,42 @@ "Small groups (max 20)" = "Kleine groepen (max 20)"; /* No comment provided by engineer. */ -"SMP servers" = "SMP servers"; +"SMP server" = "SMP server"; + +/* No comment provided by engineer. */ +"SOCKS proxy" = "SOCKS proxy"; + +/* blur media */ +"Soft" = "Soft"; + +/* No comment provided by engineer. */ +"Some app settings were not migrated." = "Sommige app-instellingen zijn niet gemigreerd."; + +/* No comment provided by engineer. */ +"Some file(s) were not exported:" = "Sommige bestanden zijn niet geëxporteerd:"; /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "Er zijn enkele niet-fatale fouten opgetreden tijdens het importeren - u kunt de Chat console raadplegen voor meer details."; +/* No comment provided by engineer. */ +"Some non-fatal errors occurred during import:" = "Er zijn enkele niet-fatale fouten opgetreden tijdens het importeren:"; + +/* alert message */ +"Some servers failed the test:\n%@" = "Sommige servers zijn niet geslaagd voor de test:\n%@"; + /* notification title */ "Somebody" = "Iemand"; +/* blocking reason +report reason */ +"Spam" = "Spam"; + +/* No comment provided by engineer. */ +"Square, circle, or anything in between." = "Vierkant, cirkel of iets daartussenin."; + +/* chat item text */ +"standard end-to-end encryption" = "standaard end-to-end encryptie"; + /* No comment provided by engineer. */ "Start chat" = "Begin gesprek"; @@ -3347,14 +4884,20 @@ /* No comment provided by engineer. */ "Start migration" = "Start migratie"; +/* No comment provided by engineer. */ +"Starting from %@." = "Beginnend vanaf %@."; + /* No comment provided by engineer. */ "starting…" = "beginnen…"; +/* No comment provided by engineer. */ +"Statistics" = "Statistieken"; + /* No comment provided by engineer. */ "Stop" = "Stop"; /* No comment provided by engineer. */ -"Stop chat to enable database actions" = "Stop de chat om database acties mogelijk te maken"; +"Stop chat" = "Stop chat"; /* No comment provided by engineer. */ "Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Stop de chat om de chat database te exporteren, importeren of verwijderen. U kunt geen berichten ontvangen en verzenden terwijl de chat is gestopt."; @@ -3371,30 +4914,57 @@ /* No comment provided by engineer. */ "Stop sending file?" = "Bestand verzenden stoppen?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "Stop met delen"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "Stop met het delen van adres?"; /* authentication reason */ "Stop SimpleX" = "Stop SimpleX"; /* No comment provided by engineer. */ -"strike" = "staking"; +"Stopping chat" = "Chat stoppen"; /* No comment provided by engineer. */ -"Submit" = "Indienen"; +"Storage" = "Opslag"; + +/* No comment provided by engineer. */ +"strike" = "staking"; + +/* blur media */ +"Strong" = "Krachtig"; + +/* No comment provided by engineer. */ +"Submit" = "Bevestigen"; + +/* No comment provided by engineer. */ +"Subscribed" = "Subscribed"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Subscription fouten"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Subscriptions genegeerd"; /* No comment provided by engineer. */ "Support SimpleX Chat" = "Ondersteuning van SimpleX Chat"; +/* No comment provided by engineer. */ +"Switch audio and video during the call." = "Wisselen tussen audio en video tijdens het gesprek."; + +/* No comment provided by engineer. */ +"Switch chat profile for 1-time invitations." = "Wijzig chatprofiel voor eenmalige uitnodigingen."; + /* No comment provided by engineer. */ "System" = "Systeem"; /* No comment provided by engineer. */ "System authentication" = "Systeem authenticatie"; +/* No comment provided by engineer. */ +"Tail" = "Staart"; + /* No comment provided by engineer. */ "Take picture" = "Foto nemen"; @@ -3402,29 +4972,35 @@ "Tap button " = "Tik op de knop "; /* No comment provided by engineer. */ -"Tap to activate profile." = "Tik om profiel te activeren."; +"Tap Create SimpleX address in the menu to create it later." = "Tik op SimpleX-adres maken in het menu om het later te maken."; /* No comment provided by engineer. */ -"Tap to Connect" = "Tik om verbinding te maken"; +"Tap to activate profile." = "Tik hier om profiel te activeren."; /* No comment provided by engineer. */ -"Tap to join" = "Tik om lid te worden"; +"Tap to Connect" = "Tik hier om verbinding te maken"; /* No comment provided by engineer. */ -"Tap to join incognito" = "Tik om incognito lid te worden"; +"Tap to join" = "Tik hier om lid te worden"; /* No comment provided by engineer. */ -"Tap to paste link" = "Tik om de link te plakken"; +"Tap to join incognito" = "Tik hier om incognito lid te worden"; /* No comment provided by engineer. */ -"Tap to scan" = "Tik om te scannen"; +"Tap to paste link" = "Tik hier om de link te plakken"; /* No comment provided by engineer. */ -"Tap to start a new chat" = "Tik om een nieuw gesprek te starten"; +"Tap to scan" = "Tik hier om te scannen"; + +/* No comment provided by engineer. */ +"TCP connection" = "TCP verbinding"; /* No comment provided by engineer. */ "TCP connection timeout" = "Timeout van TCP-verbinding"; +/* No comment provided by engineer. */ +"TCP port for messaging" = "TCP-poort voor berichtenuitwisseling"; + /* No comment provided by engineer. */ "TCP_KEEPCNT" = "TCP_KEEPCNT"; @@ -3434,16 +5010,22 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* file error alert title */ +"Temporary file error" = "Tijdelijke bestandsfout"; + /* server test failure */ "Test failed at step %@." = "Test mislukt bij stap %@."; +/* No comment provided by engineer. */ +"Test notifications" = "Testmeldingen"; + /* No comment provided by engineer. */ "Test server" = "Server test"; /* No comment provided by engineer. */ "Test servers" = "Servers testen"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "Testen mislukt!"; /* No comment provided by engineer. */ @@ -3456,10 +5038,13 @@ "Thanks to the users – contribute via Weblate!" = "Dank aan de gebruikers – draag bij via Weblate!"; /* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "Het eerste platform zonder gebruikers-ID's, privé door ontwerp."; +"The app can notify you when you receive messages or contact requests - please open settings to enable." = "De app kan u op de hoogte stellen wanneer u berichten of contact verzoeken ontvangt - open de instellingen om dit in te schakelen."; /* No comment provided by engineer. */ -"The app can notify you when you receive messages or contact requests - please open settings to enable." = "De app kan u op de hoogte stellen wanneer u berichten of contact verzoeken ontvangt - open de instellingen om dit in te schakelen."; +"The app protects your privacy by using different operators in each conversation." = "De app beschermt uw privacy door in elk gesprek andere operatoren te gebruiken."; + +/* No comment provided by engineer. */ +"The app will ask to confirm downloads from unknown file servers (except .onion)." = "De app vraagt om downloads van onbekende bestandsservers (behalve .onion) te bevestigen."; /* No comment provided by engineer. */ "The attempt to change database passphrase was not completed." = "De poging om het wachtwoord van de database te wijzigen is niet voltooid."; @@ -3467,6 +5052,9 @@ /* No comment provided by engineer. */ "The code you scanned is not a SimpleX link QR code." = "De code die u heeft gescand is geen SimpleX link QR-code."; +/* No comment provided by engineer. */ +"The connection reached the limit of undelivered messages, your contact may be offline." = "De verbinding heeft de limiet van niet-afgeleverde berichten bereikt. Uw contactpersoon is mogelijk offline."; + /* No comment provided by engineer. */ "The connection you accepted will be cancelled!" = "De door u geaccepteerde verbinding wordt geannuleerd!"; @@ -3479,6 +5067,9 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "De versleuteling werkt en de nieuwe versleutelingsovereenkomst is niet vereist. Dit kan leiden tot verbindingsfouten!"; +/* No comment provided by engineer. */ +"The future of messaging" = "De volgende generatie privéberichten"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "De hash van het vorige bericht is anders."; @@ -3492,13 +5083,22 @@ "The message will be marked as moderated for all members." = "Het bericht wordt gemarkeerd als gemodereerd voor alle leden."; /* No comment provided by engineer. */ -"The next generation of private messaging" = "De volgende generatie privéberichten"; +"The messages will be deleted for all members." = "De berichten worden voor alle leden verwijderd."; + +/* No comment provided by engineer. */ +"The messages will be marked as moderated for all members." = "De berichten worden voor alle leden als gemodereerd gemarkeerd."; /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "De oude database is niet verwijderd tijdens de migratie, deze kan worden verwijderd."; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "Het profiel wordt alleen gedeeld met uw contacten."; +"Your profile is stored on your device and only shared with your contacts." = "Het profiel wordt alleen gedeeld met uw contacten."; + +/* No comment provided by engineer. */ +"The same conditions will apply to operator **%@**." = "Dezelfde voorwaarden gelden voor operator **%@**."; + +/* No comment provided by engineer. */ +"The second preset operator in the app!" = "De tweede vooraf ingestelde operator in de app!"; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "De tweede vink die we gemist hebben! ✅"; @@ -3507,13 +5107,22 @@ "The sender will NOT be notified" = "De afzender wordt NIET op de hoogte gebracht"; /* No comment provided by engineer. */ -"The servers for new connections of your current chat profile **%@**." = "De servers voor nieuwe verbindingen van uw huidige chat profiel **%@**."; +"The servers for new connections of your current chat profile **%@**." = "De servers voor nieuwe verbindingen van uw huidige chatprofiel **%@**."; + +/* No comment provided by engineer. */ +"The servers for new files of your current chat profile **%@**." = "De servers voor nieuwe bestanden van uw huidige chatprofiel **%@**."; /* No comment provided by engineer. */ "The text you pasted is not a SimpleX link." = "De tekst die u hebt geplakt is geen SimpleX link."; /* No comment provided by engineer. */ -"Theme" = "Thema"; +"The uploaded database archive will be permanently removed from the servers." = "Het geüploade databasearchief wordt permanent van de servers verwijderd."; + +/* No comment provided by engineer. */ +"Themes" = "Thema's"; + +/* No comment provided by engineer. */ +"These conditions will also apply for: **%@**." = "Deze voorwaarden zijn ook van toepassing op: **%@**."; /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Deze instellingen zijn voor uw huidige profiel **%@**."; @@ -3527,14 +5136,23 @@ /* No comment provided by engineer. */ "This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Deze actie kan niet ongedaan worden gemaakt, de berichten die eerder zijn verzonden en ontvangen dan geselecteerd, worden verwijderd. Het kan enkele minuten duren."; +/* alert message */ +"This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted." = "Deze actie kan niet ongedaan worden gemaakt. De berichten die eerder in deze chat zijn verzonden en ontvangen dan geselecteerd, worden verwijderd."; + /* No comment provided by engineer. */ -"This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Deze actie kan niet ongedaan worden gemaakt. Uw profiel, contacten, berichten en bestanden gaan onomkeerbaar verloren."; +"This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Deze actie kan niet ongedaan worden gemaakt. Uw profiel, contacten, berichten en bestanden gaan definitief verloren."; + +/* E2EE info chat item */ +"This chat is protected by end-to-end encryption." = "Deze chat is beveiligd met end-to-end codering."; + +/* E2EE info chat item */ +"This chat is protected by quantum resistant end-to-end encryption." = "Deze chat wordt beschermd door quantum bestendige end-to-end codering."; /* notification title */ "this contact" = "dit contact"; /* No comment provided by engineer. */ -"This device name" = "Deze apparaatnaam"; +"This device name" = "Naam van dit apparaat"; /* No comment provided by engineer. */ "This display name is invalid. Please choose another name." = "Deze weergavenaam is ongeldig. Kies een andere naam."; @@ -3552,7 +5170,16 @@ "This is your own SimpleX address!" = "Dit is uw eigen SimpleX adres!"; /* No comment provided by engineer. */ -"This setting applies to messages in your current chat profile **%@**." = "Deze instelling is van toepassing op berichten in je huidige chat profiel **%@**."; +"This link was used with another mobile device, please create a new link on the desktop." = "Deze link is gebruikt met een ander mobiel apparaat. Maak een nieuwe link op de desktop."; + +/* No comment provided by engineer. */ +"This message was deleted or not received yet." = "Dit bericht is verwijderd of nog niet ontvangen."; + +/* No comment provided by engineer. */ +"This setting applies to messages in your current chat profile **%@**." = "Deze instelling is van toepassing op berichten in je huidige chatprofiel **%@**."; + +/* No comment provided by engineer. */ +"Title" = "Titel"; /* No comment provided by engineer. */ "To ask any questions and to receive updates:" = "Om vragen te stellen en updates te ontvangen:"; @@ -3567,7 +5194,7 @@ "To make a new connection" = "Om een nieuwe verbinding te maken"; /* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Om de privacy te beschermen, heeft SimpleX in plaats van gebruikers-ID's die door alle andere platforms worden gebruikt, ID's voor berichten wachtrijen, afzonderlijk voor elk van uw contacten."; +"To protect against your link being replaced, you can compare contact security codes." = "Om te voorkomen dat uw link wordt vervangen, kunt u contactbeveiligingscodes vergelijken."; /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "Om de tijdzone te beschermen, gebruiken afbeeldings-/spraakbestanden UTC."; @@ -3575,24 +5202,60 @@ /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Schakel SimpleX Vergrendelen om uw informatie te beschermen.\nU wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingeschakeld."; +/* No comment provided by engineer. */ +"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Om uw IP-adres te beschermen, gebruikt privéroutering uw SMP-servers om berichten te bezorgen."; + +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "Om de privacy te beschermen, heeft SimpleX in plaats van gebruikers-ID's die door alle andere platforms worden gebruikt, ID's voor berichten wachtrijen, afzonderlijk voor elk van uw contacten."; + +/* No comment provided by engineer. */ +"To receive" = "Om te ontvangen"; + +/* No comment provided by engineer. */ +"To record speech please grant permission to use Microphone." = "Geef toestemming om de microfoon te gebruiken om spraak op te nemen."; + +/* No comment provided by engineer. */ +"To record video please grant permission to use Camera." = "Om video op te nemen, dient u toestemming te geven om de camera te gebruiken."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Geef toestemming om de microfoon te gebruiken om een spraakbericht op te nemen."; /* No comment provided by engineer. */ -"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Om uw verborgen profiel te onthullen, voert u een volledig wachtwoord in een zoek veld in op de pagina **Uw chat profielen**."; +"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Om uw verborgen profiel te onthullen, voert u een volledig wachtwoord in een zoek veld in op de pagina **Uw chatprofielen**."; + +/* No comment provided by engineer. */ +"To send" = "Om te verzenden"; /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Om directe push meldingen te ondersteunen, moet de chat database worden gemigreerd."; +/* No comment provided by engineer. */ +"To use the servers of **%@**, accept conditions of use." = "Om de servers van **%@** te gebruiken, moet u de gebruiksvoorwaarden accepteren."; + /* No comment provided by engineer. */ "To verify end-to-end encryption with your contact compare (or scan) the code on your devices." = "Vergelijk (of scan) de code op uw apparaten om end-to-end-codering met uw contact te verifiëren."; +/* No comment provided by engineer. */ +"Toggle chat list:" = "Chatlijst wisselen:"; + /* No comment provided by engineer. */ "Toggle incognito when connecting." = "Schakel incognito in tijdens het verbinden."; +/* token status */ +"Token status: %@." = "Tokenstatus: %@."; + +/* No comment provided by engineer. */ +"Toolbar opacity" = "De transparantie van de werkbalk"; + +/* No comment provided by engineer. */ +"Total" = "Totaal"; + /* No comment provided by engineer. */ "Transport isolation" = "Transport isolation"; +/* No comment provided by engineer. */ +"Transport sessions" = "Transportsessies"; + /* No comment provided by engineer. */ "Trying to connect to the server used to receive messages from this contact (error: %@)." = "Proberen verbinding te maken met de server die wordt gebruikt om berichten van dit contact te ontvangen (fout: %@)."; @@ -3629,20 +5292,20 @@ /* rcv group event chat item */ "unblocked %@" = "gedeblokkeerd %@"; -/* item status description */ -"Unexpected error: %@" = "Onverwachte fout: %@"; +/* No comment provided by engineer. */ +"Undelivered messages" = "Niet afgeleverde berichten"; /* No comment provided by engineer. */ "Unexpected migration state" = "Onverwachte migratiestatus"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "Niet fav."; /* No comment provided by engineer. */ "Unhide" = "zichtbaar maken"; /* No comment provided by engineer. */ -"Unhide chat profile" = "Chat profiel zichtbaar maken"; +"Unhide chat profile" = "Chatprofiel zichtbaar maken"; /* No comment provided by engineer. */ "Unhide profile" = "Profiel zichtbaar maken"; @@ -3662,6 +5325,12 @@ /* No comment provided by engineer. */ "Unknown error" = "Onbekende fout"; +/* No comment provided by engineer. */ +"unknown servers" = "onbekende relays"; + +/* alert title */ +"Unknown servers!" = "Onbekende servers!"; + /* No comment provided by engineer. */ "unknown status" = "onbekende status"; @@ -3683,10 +5352,13 @@ /* authentication reason */ "Unlock app" = "Ontgrendel app"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "Dempen opheffen"; /* No comment provided by engineer. */ +"unprotected" = "onbeschermd"; + +/* swipe action */ "Unread" = "Ongelezen"; /* No comment provided by engineer. */ @@ -3695,9 +5367,6 @@ /* No comment provided by engineer. */ "Update" = "Update"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = ".onion hosts-instelling updaten?"; - /* No comment provided by engineer. */ "Update database passphrase" = "Database wachtwoord bijwerken"; @@ -3705,7 +5374,10 @@ "Update network settings?" = "Netwerk instellingen bijwerken?"; /* No comment provided by engineer. */ -"Update transport isolation mode?" = "Transportisolatiemodus updaten?"; +"Update settings?" = "Instellingen actualiseren?"; + +/* No comment provided by engineer. */ +"Updated conditions" = "Bijgewerkte voorwaarden"; /* rcv group event chat item */ "updated group profile" = "bijgewerkt groep profiel"; @@ -3717,23 +5389,44 @@ "Updating settings will re-connect the client to all servers." = "Door de instellingen bij te werken, wordt de client opnieuw verbonden met alle servers."; /* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "Als u deze instelling bijwerkt, wordt de client opnieuw verbonden met alle servers."; +"Upgrade and open chat" = "Upgrade en open chat"; /* No comment provided by engineer. */ -"Upgrade and open chat" = "Upgrade en open chat"; +"Upload errors" = "Upload fouten"; + +/* No comment provided by engineer. */ +"Upload failed" = "Upload mislukt"; /* server test step */ "Upload file" = "Upload bestand"; +/* No comment provided by engineer. */ +"Uploaded" = "Geüpload"; + +/* No comment provided by engineer. */ +"Uploaded files" = "Geüploade bestanden"; + +/* No comment provided by engineer. */ +"Uploading archive" = "Archief uploaden"; + /* No comment provided by engineer. */ "Use .onion hosts" = "Gebruik .onion-hosts"; +/* No comment provided by engineer. */ +"Use %@" = "Gebruik %@"; + /* No comment provided by engineer. */ "Use chat" = "Gebruik chat"; /* No comment provided by engineer. */ "Use current profile" = "Gebruik het huidige profiel"; +/* No comment provided by engineer. */ +"Use for files" = "Gebruik voor bestanden"; + +/* No comment provided by engineer. */ +"Use for messages" = "Gebruik voor berichten"; + /* No comment provided by engineer. */ "Use for new connections" = "Gebruik voor nieuwe verbindingen"; @@ -3749,20 +5442,44 @@ /* No comment provided by engineer. */ "Use only local notifications?" = "Alleen lokale meldingen gebruiken?"; +/* No comment provided by engineer. */ +"Use private routing with unknown servers when IP address is not protected." = "Gebruik privéroutering met onbekende servers wanneer het IP-adres niet beveiligd is."; + +/* No comment provided by engineer. */ +"Use private routing with unknown servers." = "Gebruik privéroutering met onbekende servers."; + /* No comment provided by engineer. */ "Use server" = "Gebruik server"; +/* No comment provided by engineer. */ +"Use servers" = "Gebruik servers"; + /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "SimpleX Chat servers gebruiken?"; /* No comment provided by engineer. */ -"User profile" = "Gebruikers profiel"; +"Use SOCKS proxy" = "Gebruik SOCKS proxy"; /* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = "Het gebruik van .onion-hosts vereist een compatibele VPN-provider."; +"Use TCP port %@ when no port is specified." = "Gebruik TCP-poort %@ als er geen poort is opgegeven."; /* No comment provided by engineer. */ -"Using SimpleX Chat servers." = "SimpleX Chat servers gebruiken."; +"Use the app while in the call." = "Gebruik de app tijdens het gesprek."; + +/* No comment provided by engineer. */ +"Use the app with one hand." = "Gebruik de app met één hand."; + +/* No comment provided by engineer. */ +"Use web port" = "Gebruik een webpoort"; + +/* No comment provided by engineer. */ +"User selection" = "Gebruikersselectie"; + +/* No comment provided by engineer. */ +"Username" = "Gebruikersnaam"; + +/* No comment provided by engineer. */ +"Using SimpleX Chat servers." = "Gebruik SimpleX Chat servers."; /* No comment provided by engineer. */ "v%@" = "v%@"; @@ -3782,6 +5499,12 @@ /* No comment provided by engineer. */ "Verify connections" = "Controleer verbindingen"; +/* No comment provided by engineer. */ +"Verify database passphrase" = "Controleer het wachtwoord van de database"; + +/* No comment provided by engineer. */ +"Verify passphrase" = "Controleer het wachtwoord"; + /* No comment provided by engineer. */ "Verify security code" = "Controleer de beveiligingscode"; @@ -3803,6 +5526,9 @@ /* No comment provided by engineer. */ "Via secure quantum resistant protocol." = "Via een beveiligd kwantumbestendig protocol."; +/* No comment provided by engineer. */ +"video" = "video"; + /* No comment provided by engineer. */ "Video call" = "video oproep"; @@ -3818,9 +5544,15 @@ /* No comment provided by engineer. */ "Videos and files up to 1gb" = "Video's en bestanden tot 1 GB"; +/* No comment provided by engineer. */ +"View conditions" = "Bekijk voorwaarden"; + /* No comment provided by engineer. */ "View security code" = "Beveiligingscode bekijken"; +/* No comment provided by engineer. */ +"View updated conditions" = "Bekijk de bijgewerkte voorwaarden"; + /* chat feature */ "Visible history" = "Zichtbare geschiedenis"; @@ -3831,13 +5563,16 @@ "Voice messages" = "Spraak berichten"; /* No comment provided by engineer. */ -"Voice messages are prohibited in this chat." = "Spraak berichten zijn verboden in deze chat."; +"Voice messages are prohibited in this chat." = "Spraak berichten zijn niet toegestaan in dit gesprek."; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "Spraak berichten zijn verboden in deze groep."; +"Voice messages are prohibited." = "Spraak berichten zijn niet toegestaan."; /* No comment provided by engineer. */ -"Voice messages prohibited!" = "Spraak berichten verboden!"; +"Voice messages not allowed" = "Spraakberichten niet toegestaan"; + +/* No comment provided by engineer. */ +"Voice messages prohibited!" = "Spraak berichten niet toegestaan!"; /* No comment provided by engineer. */ "waiting for answer…" = "wachten op antwoord…"; @@ -3857,9 +5592,18 @@ /* No comment provided by engineer. */ "Waiting for video" = "Wachten op video"; +/* No comment provided by engineer. */ +"Wallpaper accent" = "Achtergrond accent"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Wallpaper achtergrond"; + /* No comment provided by engineer. */ "wants to connect to you!" = "wil met je in contact komen!"; +/* No comment provided by engineer. */ +"Warning: starting chat on multiple devices is not supported and will cause message delivery failures" = "Waarschuwing: het starten van de chat op meerdere apparaten wordt niet ondersteund en zal leiden tot mislukte bezorging van berichten"; + /* No comment provided by engineer. */ "Warning: you may lose some data!" = "Waarschuwing: u kunt sommige gegevens verliezen!"; @@ -3873,7 +5617,10 @@ "Welcome %@!" = "Welkom %@!"; /* No comment provided by engineer. */ -"Welcome message" = "Welkomst bericht"; +"Welcome message" = "Welkom bericht"; + +/* No comment provided by engineer. */ +"Welcome message is too long" = "Welkom bericht is te lang"; /* No comment provided by engineer. */ "What's new" = "Wat is er nieuw"; @@ -3882,34 +5629,64 @@ "When available" = "Wanneer beschikbaar"; /* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "Wanneer mensen vragen om verbinding te maken, kunt u dit accepteren of weigeren."; +"When connecting audio and video calls." = "Bij het verbinden van audio- en video-oproepen."; + +/* No comment provided by engineer. */ +"when IP hidden" = "wanneer IP verborgen is"; + +/* No comment provided by engineer. */ +"When more than one operator is enabled, none of them has metadata to learn who communicates with whom." = "Wanneer er meer dan één operator is ingeschakeld, beschikt geen enkele operator over metagegevens om te achterhalen wie met wie communiceert."; /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Wanneer je een incognito profiel met iemand deelt, wordt dit profiel gebruikt voor de groepen waarvoor ze je uitnodigen."; +/* No comment provided by engineer. */ +"WiFi" = "Wifi"; + +/* No comment provided by engineer. */ +"Will be enabled in direct chats!" = "Wordt ingeschakeld in directe chats!"; + +/* No comment provided by engineer. */ +"Wired ethernet" = "Bekabeld Ethernet"; + /* No comment provided by engineer. */ "With encrypted files and media." = "‐Met versleutelde bestanden en media."; /* No comment provided by engineer. */ -"With optional welcome message." = "Met optioneel welkomst bericht."; +"With optional welcome message." = "Met optioneel welkom bericht."; /* No comment provided by engineer. */ "With reduced battery usage." = "Met verminderd batterijgebruik."; +/* No comment provided by engineer. */ +"Without Tor or VPN, your IP address will be visible to file servers." = "Zonder Tor of VPN is uw IP-adres zichtbaar voor bestandsservers."; + +/* alert message */ +"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Zonder Tor of VPN zal uw IP-adres zichtbaar zijn voor deze XFTP-relays: %@."; + /* No comment provided by engineer. */ "Wrong database passphrase" = "Verkeerd wachtwoord voor de database"; +/* snd error text */ +"Wrong key or unknown connection - most likely this connection is deleted." = "Verkeerde sleutel of onbekende verbinding - hoogstwaarschijnlijk is deze verbinding verwijderd."; + +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Verkeerde sleutel of onbekend bestanddeeladres - hoogstwaarschijnlijk is het bestand verwijderd."; + /* No comment provided by engineer. */ "Wrong passphrase!" = "Verkeerd wachtwoord!"; /* No comment provided by engineer. */ -"XFTP servers" = "XFTP servers"; +"XFTP server" = "XFTP server"; /* pref value */ "yes" = "Ja"; /* No comment provided by engineer. */ -"You" = "Jij"; +"you" = "jij"; + +/* No comment provided by engineer. */ +"You **must not** use the same database on two devices." = "U **mag** niet dezelfde database op twee apparaten gebruiken."; /* No comment provided by engineer. */ "You accepted connection" = "Je hebt de verbinding geaccepteerd"; @@ -3918,11 +5695,14 @@ "You allow" = "Jij staat toe"; /* No comment provided by engineer. */ -"You already have a chat profile with the same display name. Please choose another name." = "Je hebt al een chat profiel met dezelfde weergave naam. Kies een andere naam."; +"You already have a chat profile with the same display name. Please choose another name." = "Je hebt al een chatprofiel met dezelfde weergave naam. Kies een andere naam."; /* No comment provided by engineer. */ "You are already connected to %@." = "U bent al verbonden met %@."; +/* No comment provided by engineer. */ +"You are already connected with %@." = "U bent al verbonden met %@."; + /* No comment provided by engineer. */ "You are already connecting to %@." = "U maakt al verbinding met %@."; @@ -3954,7 +5734,10 @@ "You are invited to group" = "Je bent uitgenodigd voor de groep"; /* No comment provided by engineer. */ -"you are observer" = "jij bent waarnemer"; +"You are not connected to these servers. Private routing is used to deliver messages to them." = "U bent niet verbonden met deze servers. Privéroutering wordt gebruikt om berichten bij hen af te leveren."; + +/* No comment provided by engineer. */ +"you are observer" = "je bent waarnemer"; /* snd group event chat item */ "you blocked %@" = "je hebt %@ geblokkeerd"; @@ -3962,6 +5745,12 @@ /* No comment provided by engineer. */ "You can accept calls from lock screen, without device and app authentication." = "U kunt oproepen van het vergrendelingsscherm accepteren, zonder apparaat- en app-verificatie."; +/* No comment provided by engineer. */ +"You can change it in Appearance settings." = "U kunt dit wijzigen in de instellingen onder uiterlijk."; + +/* No comment provided by engineer. */ +"You can configure servers via settings." = "U kunt servers configureren via instellingen."; + /* No comment provided by engineer. */ "You can create it later" = "U kan het later maken"; @@ -3971,6 +5760,9 @@ /* No comment provided by engineer. */ "You can enable them later via app Privacy & Security settings." = "U kunt ze later inschakelen via de privacy- en beveiligingsinstellingen van de app."; +/* No comment provided by engineer. */ +"You can give another try." = "Je kunt het nog een keer proberen."; + /* No comment provided by engineer. */ "You can hide or mute a user profile - swipe it to the right." = "U kunt een gebruikers profiel verbergen of dempen - veeg het naar rechts."; @@ -3978,7 +5770,13 @@ "You can make it visible to your SimpleX contacts via Settings." = "Je kunt het via Instellingen zichtbaar maken voor je SimpleX contacten."; /* notification body */ -"You can now send messages to %@" = "Je kunt nu berichten sturen naar %@"; +"You can now chat with %@" = "Je kunt nu berichten sturen naar %@"; + +/* No comment provided by engineer. */ +"You can send messages to %@ from Archived contacts." = "U kunt berichten naar %@ sturen vanuit gearchiveerde contacten."; + +/* No comment provided by engineer. */ +"You can set connection name, to remember who the link was shared with." = "U kunt een verbindingsnaam instellen, zodat u kunt onthouden met wie de link is gedeeld."; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "U kunt een voorbeeld van een melding op het vergrendeld scherm instellen via instellingen."; @@ -3990,10 +5788,10 @@ "You can share this address with your contacts to let them connect with **%@**." = "U kunt dit adres delen met uw contacten om hen verbinding te laten maken met **%@**."; /* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "U kunt uw adres delen als een link of als een QR-code. Iedereen kan verbinding met u maken."; +"You can start chat via app Settings / Database or by restarting the app" = "U kunt de chat starten via app Instellingen / Database of door de app opnieuw op te starten"; /* No comment provided by engineer. */ -"You can start chat via app Settings / Database or by restarting the app" = "U kunt de chat starten via app Instellingen / Database of door de app opnieuw op te starten"; +"You can still view conversation with %@ in the list of chats." = "Je kunt het gesprek met %@ nog steeds bekijken in de lijst met chats."; /* No comment provided by engineer. */ "You can turn on SimpleX Lock via Settings." = "Je kunt SimpleX Vergrendeling aanzetten via Instellingen."; @@ -4001,7 +5799,7 @@ /* No comment provided by engineer. */ "You can use markdown to format messages:" = "U kunt markdown gebruiken voor opmaak in berichten:"; -/* No comment provided by engineer. */ +/* alert message */ "You can view invitation link again in connection details." = "U kunt de uitnodigingslink opnieuw bekijken in de verbindingsdetails."; /* No comment provided by engineer. */ @@ -4020,10 +5818,10 @@ "you changed role of %@ to %@" = "je veranderde de rol van %1$@ in %2$@"; /* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "U bepaalt via welke server(s) de berichten **ontvangen**, uw contacten de servers die u gebruikt om ze berichten te sturen."; +"You could not be verified; please try again." = "U kon niet worden geverifieerd; probeer het opnieuw."; /* No comment provided by engineer. */ -"You could not be verified; please try again." = "U kon niet worden geverifieerd; probeer het opnieuw."; +"You decide who can connect." = "Jij bepaalt wie er verbinding mag maken."; /* No comment provided by engineer. */ "You have already requested connection via this address!" = "U heeft al een verbinding aangevraagd via dit adres!"; @@ -4031,9 +5829,6 @@ /* No comment provided by engineer. */ "You have already requested connection!\nRepeat connection request?" = "Je hebt al verbinding aangevraagd!\nVerbindingsverzoek herhalen?"; -/* No comment provided by engineer. */ -"You have no chats" = "Je hebt geen gesprekken"; - /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "U moet elke keer dat de app start het wachtwoord invoeren, deze wordt niet op het apparaat opgeslagen."; @@ -4047,11 +5842,20 @@ "You joined this group. Connecting to inviting group member." = "Je bent lid geworden van deze groep. Verbinding maken met uitnodigend groepslid."; /* snd group event chat item */ -"you left" = "jij bent vertrokken"; +"you left" = "je bent vertrokken"; + +/* No comment provided by engineer. */ +"You may migrate the exported database." = "U kunt de geëxporteerde database migreren."; + +/* No comment provided by engineer. */ +"You may save the exported archive." = "U kunt het geëxporteerde archief opslaan."; /* No comment provided by engineer. */ "You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." = "U mag ALLEEN de meest recente versie van uw chat database op één apparaat gebruiken, anders ontvangt u mogelijk geen berichten meer van sommige contacten."; +/* No comment provided by engineer. */ +"You need to allow your contact to call to be able to call them." = "U moet uw contactpersoon toestemming geven om te bellen, zodat hij/zij je kan bellen."; + /* No comment provided by engineer. */ "You need to allow your contact to send voice messages to be able to send them." = "U moet uw contact toestemming geven om spraak berichten te verzenden om ze te kunnen verzenden."; @@ -4070,6 +5874,9 @@ /* chat list item description */ "you shared one-time link incognito" = "je hebt een eenmalige link incognito gedeeld"; +/* token info */ +"You should receive notifications." = "U zou meldingen moeten ontvangen."; + /* snd group event chat item */ "you unblocked %@" = "je hebt %@ gedeblokkeerd"; @@ -4094,6 +5901,9 @@ /* No comment provided by engineer. */ "You will still receive calls and notifications from muted profiles when they are active." = "U ontvangt nog steeds oproepen en meldingen van gedempte profielen wanneer deze actief zijn."; +/* No comment provided by engineer. */ +"You will stop receiving messages from this chat. Chat history will be preserved." = "U ontvangt geen berichten meer van deze chat. De chatgeschiedenis blijft bewaard."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Je ontvangt geen berichten meer van deze groep. Je gesprek geschiedenis blijft behouden."; @@ -4109,9 +5919,6 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Je gebruikt een incognito profiel voor deze groep. Om te voorkomen dat je je hoofdprofiel deelt, is het niet toegestaan om contacten uit te nodigen"; -/* No comment provided by engineer. */ -"Your %@ servers" = "Uw %@ servers"; - /* No comment provided by engineer. */ "Your calls" = "Uw oproepen"; @@ -4121,11 +5928,14 @@ /* No comment provided by engineer. */ "Your chat database is not encrypted - set passphrase to encrypt it." = "Uw chat database is niet versleuteld, stel een wachtwoord in om deze te versleutelen."; +/* alert title */ +"Your chat preferences" = "Uw chat voorkeuren"; + /* No comment provided by engineer. */ "Your chat profiles" = "Uw chat profielen"; /* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Uw contact moet online zijn om de verbinding te voltooien.\nU kunt deze verbinding verbreken en het contact verwijderen en later proberen met een nieuwe link."; +"Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." = "Uw verbinding is verplaatst naar %@, maar er is een onverwachte fout opgetreden tijdens het omleiden naar het profiel."; /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "Uw contact heeft een bestand verzonden dat groter is dan de momenteel ondersteunde maximale grootte (%@)."; @@ -4136,6 +5946,9 @@ /* No comment provided by engineer. */ "Your contacts will remain connected." = "Uw contacten blijven verbonden."; +/* No comment provided by engineer. */ +"Your credentials may be sent unencrypted." = "Uw inloggegevens worden mogelijk niet-versleuteld verzonden."; + /* No comment provided by engineer. */ "Your current chat database will be DELETED and REPLACED with the imported one." = "Uw huidige chat database wordt VERWIJDERD en VERVANGEN door de geïmporteerde."; @@ -4158,7 +5971,10 @@ "Your profile **%@** will be shared." = "Uw profiel **%@** wordt gedeeld."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Uw profiel wordt op uw apparaat opgeslagen en alleen gedeeld met uw contacten.\nSimpleX servers kunnen uw profiel niet zien."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Uw profiel wordt op uw apparaat opgeslagen en alleen gedeeld met uw contacten. SimpleX servers kunnen uw profiel niet zien."; + +/* alert message */ +"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Je profiel is gewijzigd. Als je het opslaat, wordt het bijgewerkte profiel naar al je contacten verzonden."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Uw profiel, contacten en afgeleverde berichten worden op uw apparaat opgeslagen."; @@ -4167,10 +5983,10 @@ "Your random profile" = "Je willekeurige profiel"; /* No comment provided by engineer. */ -"Your server" = "Uw server"; +"Your server address" = "Uw server adres"; /* No comment provided by engineer. */ -"Your server address" = "Uw server adres"; +"Your servers" = "Uw servers"; /* No comment provided by engineer. */ "Your settings" = "Uw instellingen"; @@ -4178,9 +5994,3 @@ /* No comment provided by engineer. */ "Your SimpleX address" = "Uw SimpleX adres"; -/* No comment provided by engineer. */ -"Your SMP servers" = "Uw SMP servers"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "Uw XFTP servers"; - diff --git a/apps/ios/pl.lproj/Localizable.strings b/apps/ios/pl.lproj/Localizable.strings index bbacb49fb3..31a9b87662 100644 --- a/apps/ios/pl.lproj/Localizable.strings +++ b/apps/ios/pl.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (można skopiować)"; @@ -32,19 +17,7 @@ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- wiadomości głosowe do 5 minut.\n- niestandardowy czas zniknięcia.\n- historia edycji."; /* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - -/* No comment provided by engineer. */ -"!1 colored!" = "!1 kolorowy!"; - -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; +"!1 colored!" = "!1 pokolorowany!"; /* No comment provided by engineer. */ "(new)" = "(nowy)"; @@ -52,9 +25,6 @@ /* No comment provided by engineer. */ "(this device v%@)" = "(to urządzenie v%@)"; -/* No comment provided by engineer. */ -")" = ")"; - /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Przyczyń się](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -65,10 +35,7 @@ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Daj gwiazdkę na GitHub](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add contact**: to create a new invitation link, or connect via a link you received." = "**Dodaj kontakt**: aby utworzyć nowy link z zaproszeniem lub połączyć się za pomocą otrzymanego linku."; - -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Dodaj nowy kontakt**: aby stworzyć swój jednorazowy kod QR lub link dla kontaktu."; +"**Create 1-time link**: to create and share a new invitation link." = "**Dodaj kontakt**: aby utworzyć nowy link z zaproszeniem lub połączyć się za pomocą otrzymanego linku."; /* No comment provided by engineer. */ "**Create group**: to create a new group." = "**Utwórz grupę**: aby utworzyć nową grupę."; @@ -80,19 +47,28 @@ "**e2e encrypted** video call" = "**szyfrowane e2e** połączenie wideo"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Bardziej prywatny**: sprawdzanie nowych wiadomości co 20 minut. Token urządzenia jest współdzielony z serwerem SimpleX Chat, ale nie informacje o liczbie kontaktów lub wiadomości."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Bardziej prywatny**: sprawdzanie nowych wiadomości odbywa się co 20 minut. Współdzielony z serwerem SimpleX Chat jest token urządzenia, lecz nie informacje o liczbie kontaktów lub wiadomości."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Najbardziej prywatny**: nie korzystaj z serwera powiadomień SimpleX Chat, sprawdzaj wiadomości okresowo w tle (zależy jak często korzystasz z aplikacji)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**Najbardziej prywatny**: nie korzystaj z serwera powiadomień SimpleX Chat, wiadomości sprawdzane są co jakiś czas w tle (zależne od tego jak często korzystasz z aplikacji)."; /* No comment provided by engineer. */ -"**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Uwaga**: NIE będziesz w stanie odzyskać lub zmienić hasła, jeśli je stracisz."; +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "*Uwaga*: w celach bezpieczeństwa użycie tej samej bazy danych na dwóch różnych urządzeniach spowoduje brak możliwości odszyfrowywania wiadomości z Twoich połączeń."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Zalecane**: token urządzenia i powiadomienia są wysyłane do serwera powiadomień SimpleX Chat, ale nie treść wiadomości, rozmiar lub od kogo jest."; +"**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Uwaga**: NIE będziesz w stanie odzyskać lub zmienić kodu dostępu, jeśli go stracisz."; /* No comment provided by engineer. */ -"**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Uwaga**: Natychmiastowe powiadomienia push wymagają hasła zapisanego w Keychain."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Zalecane**: do serwera powiadomień SimpleX Chat wysyłany jest token urządzenia i powiadomienia, lecz nie treść wiadomości, jej rozmiar lub od kogo ona jest."; + +/* No comment provided by engineer. */ +"**Scan / Paste link**: to connect via a link you received." = "**Zeskanuj / Wklej link**: aby połączyć się za pomocą otrzymanego linku."; + +/* No comment provided by engineer. */ +"**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Uwaga**: Natychmiastowe powiadomienia push wymagają zapisania kodu dostępu w Keychain."; + +/* No comment provided by engineer. */ +"**Warning**: the archive will be removed." = "**Ostrzeżenie**: archiwum zostanie usunięte."; /* No comment provided by engineer. */ "*bold*" = "\\*pogrubiony*"; @@ -136,6 +112,9 @@ /* No comment provided by engineer. */ "%@ connected" = "%@ połączony"; +/* No comment provided by engineer. */ +"%@ downloaded" = "%@ pobrane"; + /* notification title */ "%@ is connected!" = "%@ jest połączony!"; @@ -146,11 +125,20 @@ "%@ is verified" = "%@ jest zweryfikowany"; /* No comment provided by engineer. */ -"%@ servers" = "%@ serwery"; +"%@ server" = "%@ serwer"; + +/* No comment provided by engineer. */ +"%@ servers" = "%@ serwery/ów"; + +/* No comment provided by engineer. */ +"%@ uploaded" = "%@ wgrane"; /* notification title */ "%@ wants to connect!" = "%@ chce się połączyć!"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + /* No comment provided by engineer. */ "%@, %@ and %lld members" = "%@, %@ i %lld członków"; @@ -163,9 +151,24 @@ /* time interval */ "%d days" = "%d dni"; +/* forward confirmation reason */ +"%d file(s) are still being downloaded." = "%d plik(ów) jest dalej pobieranych."; + +/* forward confirmation reason */ +"%d file(s) failed to download." = "%d plik(ów) nie udało się pobrać."; + +/* forward confirmation reason */ +"%d file(s) were deleted." = "%d plik(ów) zostało usuniętych."; + +/* forward confirmation reason */ +"%d file(s) were not downloaded." = "%d plik(ów) nie zostało pobranych."; + /* time interval */ "%d hours" = "%d godzin"; +/* alert title */ +"%d messages not forwarded" = "%d wiadomości nie przekazanych"; + /* time interval */ "%d min" = "%d min"; @@ -175,6 +178,9 @@ /* time interval */ "%d sec" = "%d sek"; +/* delete after time */ +"%d seconds(s)" = "%d sekundach"; + /* integrity error chat item */ "%d skipped message(s)" = "%d pominięte wiadomość(i)"; @@ -217,9 +223,6 @@ /* No comment provided by engineer. */ "%lld new interface languages" = "%lld nowe języki interfejsu"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld sekund(y)"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld sekund"; @@ -265,7 +268,8 @@ /* No comment provided by engineer. */ "0s" = "0s"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "1 dzień"; /* time interval */ @@ -274,12 +278,23 @@ /* No comment provided by engineer. */ "1 minute" = "1 minuta"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "1 miesiąc"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "1 tydzień"; +/* delete after time */ +"1 year" = "1 roku"; + +/* No comment provided by engineer. */ +"1-time link" = "link jednorazowy"; + +/* No comment provided by engineer. */ +"1-time link can be used *with one contact only* - share in person or via any messenger." = "Link jednorazowy może być użyty *tylko z jednym kontaktem* - udostępnij go osobiście lub przez dowolny komunikator."; + /* No comment provided by engineer. */ "5 minutes" = "5 minut"; @@ -314,10 +329,7 @@ "Abort changing address?" = "Przerwać zmianę adresu?"; /* No comment provided by engineer. */ -"About SimpleX" = "O SimpleX"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "O adresie SimpleX"; +"About operators" = "O operatorach"; /* No comment provided by engineer. */ "About SimpleX Chat" = "O SimpleX Chat"; @@ -326,81 +338,155 @@ "above, then choose:" = "powyżej, a następnie wybierz:"; /* No comment provided by engineer. */ -"Accent color" = "Kolor akcentu"; +"Accent" = "Akcent"; /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "Akceptuj"; +/* No comment provided by engineer. */ +"Accept conditions" = "Zaakceptuj warunki"; + /* No comment provided by engineer. */ "Accept connection request?" = "Zaakceptować prośbę o połączenie?"; /* notification body */ "Accept contact request from %@?" = "Zaakceptuj prośbę o kontakt od %@?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "Akceptuj incognito"; /* call status */ "accepted call" = "zaakceptowane połączenie"; +/* No comment provided by engineer. */ +"Accepted conditions" = "Zaakceptowano warunki"; + +/* No comment provided by engineer. */ +"Acknowledged" = "Potwierdzono"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Błędy potwierdzenia"; + +/* token status text */ +"Active" = "Aktywne"; + +/* No comment provided by engineer. */ +"Active connections" = "Aktywne połączenia"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Dodaj adres do swojego profilu, aby Twoje kontakty mogły go udostępnić innym osobom. Aktualizacja profilu zostanie wysłana do Twoich kontaktów."; /* No comment provided by engineer. */ -"Add contact" = "Dodaj kontakt"; +"Add friends" = "Dodaj znajomych"; /* No comment provided by engineer. */ -"Add preset servers" = "Dodaj gotowe serwery"; +"Add list" = "Dodaj listę"; /* No comment provided by engineer. */ "Add profile" = "Dodaj profil"; /* No comment provided by engineer. */ -"Add server…" = "Dodaj serwer…"; +"Add server" = "Dodaj serwer"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "Dodaj serwery, skanując kody QR."; +/* No comment provided by engineer. */ +"Add team members" = "Dodaj członków zespołu"; + /* No comment provided by engineer. */ "Add to another device" = "Dodaj do innego urządzenia"; +/* No comment provided by engineer. */ +"Add to list" = "Dodaj do listy"; + /* No comment provided by engineer. */ "Add welcome message" = "Dodaj wiadomość powitalną"; +/* No comment provided by engineer. */ +"Add your team members to the conversations." = "Dodaj członków zespołu do konwersacji."; + +/* No comment provided by engineer. */ +"Added media & file servers" = "Dodano serwery multimediów i plików"; + +/* No comment provided by engineer. */ +"Added message servers" = "Dodano serwery wiadomości"; + +/* No comment provided by engineer. */ +"Additional accent" = "Dodatkowy akcent"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Dodatkowy akcent 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Dodatkowy drugorzędny"; + /* No comment provided by engineer. */ "Address" = "Adres"; /* No comment provided by engineer. */ "Address change will be aborted. Old receiving address will be used." = "Zmiana adresu zostanie przerwana. Użyty zostanie stary adres odbiorczy."; +/* No comment provided by engineer. */ +"Address or 1-time link?" = "Adres czy jednorazowy link?"; + +/* No comment provided by engineer. */ +"Address settings" = "Ustawienia adresu"; + /* member role */ "admin" = "administrator"; +/* feature role */ +"admins" = "administratorzy"; + +/* No comment provided by engineer. */ +"Admins can block a member for all." = "Administratorzy mogą blokować członka dla wszystkich."; + /* No comment provided by engineer. */ "Admins can create the links to join groups." = "Administratorzy mogą tworzyć linki do dołączania do grup."; /* No comment provided by engineer. */ "Advanced network settings" = "Zaawansowane ustawienia sieci"; +/* No comment provided by engineer. */ +"Advanced settings" = "Zaawansowane ustawienia"; + /* chat item text */ "agreeing encryption for %@…" = "uzgadnianie szyfrowania dla %@…"; /* chat item text */ "agreeing encryption…" = "uzgadnianie szyfrowania…"; +/* No comment provided by engineer. */ +"All" = "Wszystko"; + /* No comment provided by engineer. */ "All app data is deleted." = "Wszystkie dane aplikacji są usunięte."; /* No comment provided by engineer. */ "All chats and messages will be deleted - this cannot be undone!" = "Wszystkie czaty i wiadomości zostaną usunięte - nie można tego cofnąć!"; +/* alert message */ +"All chats will be removed from the list %@, and the list deleted." = "Wszystkie rozmowy zostaną usunięte z listy %@, a lista usunięta."; + /* No comment provided by engineer. */ "All data is erased when it is entered." = "Wszystkie dane są usuwane po jego wprowadzeniu."; +/* No comment provided by engineer. */ +"All data is kept private on your device." = "Wszystkie dane są prywatne na Twoim urządzeniu."; + /* No comment provided by engineer. */ "All group members will remain connected." = "Wszyscy członkowie grupy pozostaną połączeni."; +/* feature role */ +"all members" = "wszyscy członkowie"; + +/* No comment provided by engineer. */ +"All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages." = "Wszystkie wiadomości i pliki są wysyłane **z szyfrowaniem end-to-end**, z bezpieczeństwem postkwantowym w wiadomościach bezpośrednich."; + /* No comment provided by engineer. */ "All messages will be deleted - this cannot be undone!" = "Wszystkie wiadomości zostaną usunięte – nie można tego cofnąć!"; @@ -410,21 +496,36 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Wszystkie nowe wiadomości z %@ zostaną ukryte!"; +/* profile dropdown */ +"All profiles" = "Wszystkie profile"; + +/* No comment provided by engineer. */ +"All reports will be archived for you." = "Wszystkie raporty zostaną dla Ciebie zarchiwizowane."; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Wszystkie Twoje kontakty pozostaną połączone."; /* No comment provided by engineer. */ "All your contacts will remain connected. Profile update will be sent to your contacts." = "Wszystkie Twoje kontakty pozostaną połączone. Aktualizacja profilu zostanie wysłana do Twoich kontaktów."; +/* No comment provided by engineer. */ +"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Wszystkie twoje kontakty, konwersacje i pliki będą bezpiecznie szyfrowane i wgrywane w kawałkach do skonfigurowanych przekaźników XFTP."; + /* No comment provided by engineer. */ "Allow" = "Pozwól"; /* No comment provided by engineer. */ "Allow calls only if your contact allows them." = "Zezwalaj na połączenia tylko wtedy, gdy Twój kontakt na to pozwala."; +/* No comment provided by engineer. */ +"Allow calls?" = "Zezwolić na połączenia?"; + /* No comment provided by engineer. */ "Allow disappearing messages only if your contact allows it to you." = "Zezwól na znikające wiadomości tylko wtedy, gdy Twój kontakt Ci na to pozwoli."; +/* No comment provided by engineer. */ +"Allow downgrade" = "Zezwól na obniżenie wersji"; + /* No comment provided by engineer. */ "Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Zezwalaj na nieodwracalne usuwanie wiadomości tylko wtedy, gdy Twój kontakt Ci na to pozwoli. (24 godziny)"; @@ -440,12 +541,21 @@ /* No comment provided by engineer. */ "Allow sending disappearing messages." = "Zezwól na wysyłanie znikających wiadomości."; +/* No comment provided by engineer. */ +"Allow sharing" = "Zezwól na udostępnianie"; + /* No comment provided by engineer. */ "Allow to irreversibly delete sent messages. (24 hours)" = "Zezwól na nieodwracalne usunięcie wysłanych wiadomości. (24 godziny)"; +/* No comment provided by engineer. */ +"Allow to report messsages to moderators." = "Zezwól na zgłaszanie wiadomości moderatorom."; + /* No comment provided by engineer. */ "Allow to send files and media." = "Pozwól na wysyłanie plików i mediów."; +/* No comment provided by engineer. */ +"Allow to send SimpleX links." = "Zezwól na wysyłanie linków SimpleX."; + /* No comment provided by engineer. */ "Allow to send voice messages." = "Zezwól na wysyłanie wiadomości głosowych."; @@ -482,6 +592,9 @@ /* pref value */ "always" = "zawsze"; +/* No comment provided by engineer. */ +"Always use private routing." = "Zawsze używaj prywatnego trasowania."; + /* No comment provided by engineer. */ "Always use relay" = "Zawsze używaj przekaźnika"; @@ -491,15 +604,27 @@ /* No comment provided by engineer. */ "and %lld other events" = "i %lld innych wydarzeń"; +/* report reason */ +"Another reason" = "Inny powód"; + /* No comment provided by engineer. */ "Answer call" = "Odbierz połączenie"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "Każdy może hostować serwery."; + /* No comment provided by engineer. */ "App build: %@" = "Kompilacja aplikacji: %@"; +/* No comment provided by engineer. */ +"App data migration" = "Migracja danych aplikacji"; + /* No comment provided by engineer. */ "App encrypts new local files (except videos)." = "Aplikacja szyfruje nowe lokalne pliki (bez filmów)."; +/* No comment provided by engineer. */ +"App group:" = "Grupa aplikacji:"; + /* No comment provided by engineer. */ "App icon" = "Ikona aplikacji"; @@ -509,6 +634,9 @@ /* No comment provided by engineer. */ "App passcode is replaced with self-destruct passcode." = "Pin aplikacji został zastąpiony pinem samozniszczenia."; +/* No comment provided by engineer. */ +"App session" = "Sesja aplikacji"; + /* No comment provided by engineer. */ "App version" = "Wersja aplikacji"; @@ -518,9 +646,48 @@ /* No comment provided by engineer. */ "Appearance" = "Wygląd"; +/* No comment provided by engineer. */ +"Apply" = "Zastosuj"; + +/* No comment provided by engineer. */ +"Apply to" = "Zastosuj dla"; + +/* No comment provided by engineer. */ +"Archive" = "Archiwizuj"; + +/* No comment provided by engineer. */ +"Archive %lld reports?" = "Archiwizować %lld reports?"; + +/* No comment provided by engineer. */ +"Archive all reports?" = "Archiwizować wszystkie zgłoszenia?"; + +/* No comment provided by engineer. */ +"Archive and upload" = "Archiwizuj i prześlij"; + +/* No comment provided by engineer. */ +"Archive contacts to chat later." = "Archiwizuj kontakty aby porozmawiać później."; + +/* No comment provided by engineer. */ +"Archive report" = "Archiwizuj zgłoszenie"; + +/* No comment provided by engineer. */ +"Archive report?" = "Archiwizować zgłoszenie?"; + +/* swipe action */ +"Archive reports" = "Archiwizuj zgłoszenia"; + +/* No comment provided by engineer. */ +"Archived contacts" = "Zarchiwizowane kontakty"; + +/* No comment provided by engineer. */ +"Archiving database" = "Archiwizowanie bazy danych"; + /* No comment provided by engineer. */ "Attach" = "Dołącz"; +/* No comment provided by engineer. */ +"attempts" = "próby"; + /* No comment provided by engineer. */ "Audio & video calls" = "Połączenia audio i wideo"; @@ -560,9 +727,15 @@ /* No comment provided by engineer. */ "Auto-accept images" = "Automatyczne akceptowanie obrazów"; +/* alert title */ +"Auto-accept settings" = "Ustawienia automatycznej akceptacji"; + /* No comment provided by engineer. */ "Back" = "Wstecz"; +/* No comment provided by engineer. */ +"Background" = "Tło"; + /* No comment provided by engineer. */ "Bad desktop address" = "Zły adres komputera"; @@ -578,12 +751,33 @@ /* No comment provided by engineer. */ "Bad message ID" = "Zły identyfikator wiadomości"; +/* No comment provided by engineer. */ +"Better calls" = "Lepsze połączenia"; + /* No comment provided by engineer. */ "Better groups" = "Lepsze grupy"; +/* No comment provided by engineer. */ +"Better message dates." = "Lepsze daty wiadomości."; + /* No comment provided by engineer. */ "Better messages" = "Lepsze wiadomości"; +/* No comment provided by engineer. */ +"Better networking" = "Lepsze sieciowanie"; + +/* No comment provided by engineer. */ +"Better notifications" = "Lepsze powiadomienia"; + +/* No comment provided by engineer. */ +"Better security ✅" = "Lepsze zabezpieczenia ✅"; + +/* No comment provided by engineer. */ +"Better user experience" = "Lepszy interfejs użytkownika"; + +/* No comment provided by engineer. */ +"Black" = "Czarny"; + /* No comment provided by engineer. */ "Block" = "Zablokuj"; @@ -608,12 +802,19 @@ /* rcv group event chat item */ "blocked %@" = "zablokowany %@"; -/* marked deleted chat item preview text */ +/* blocked chat item +marked deleted chat item preview text */ "blocked by admin" = "zablokowany przez admina"; /* No comment provided by engineer. */ "Blocked by admin" = "Zablokowany przez admina"; +/* No comment provided by engineer. */ +"Blur for better privacy." = "Rozmycie dla lepszej prywatności."; + +/* No comment provided by engineer. */ +"Blur media" = "Rozmycie mediów"; + /* No comment provided by engineer. */ "bold" = "pogrubiona"; @@ -635,9 +836,21 @@ /* No comment provided by engineer. */ "Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Bułgarski, fiński, tajski i ukraiński – dzięki użytkownikom i [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"; +/* No comment provided by engineer. */ +"Business address" = "Adres firmowy"; + +/* No comment provided by engineer. */ +"Business chats" = "Czaty biznesowe"; + +/* No comment provided by engineer. */ +"Businesses" = "Firmy"; + /* No comment provided by engineer. */ "By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Według profilu czatu (domyślnie) lub [według połączenia](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)."; +/* No comment provided by engineer. */ +"call" = "zadzwoń"; + /* No comment provided by engineer. */ "Call already ended!" = "Połączenie już zakończone!"; @@ -653,9 +866,18 @@ /* No comment provided by engineer. */ "Calls" = "Połączenia"; +/* No comment provided by engineer. */ +"Calls prohibited!" = "Połączenia zakazane!"; + /* No comment provided by engineer. */ "Camera not available" = "Kamera nie dostępna"; +/* No comment provided by engineer. */ +"Can't call contact" = "Nie można zadzwonić do kontaktu"; + +/* No comment provided by engineer. */ +"Can't call member" = "Nie można zadzwonić do członka"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Nie można zaprosić kontaktu!"; @@ -663,8 +885,15 @@ "Can't invite contacts!" = "Nie można zaprosić kontaktów!"; /* No comment provided by engineer. */ +"Can't message member" = "Nie można wysłać wiadomości do członka"; + +/* alert action +alert button */ "Cancel" = "Anuluj"; +/* No comment provided by engineer. */ +"Cancel migration" = "Anuluj migrację"; + /* feature offered item */ "cancelled %@" = "anulowany %@"; @@ -672,11 +901,23 @@ "Cannot access keychain to save database password" = "Nie można uzyskać dostępu do pęku kluczy, aby zapisać hasło do bazy danych"; /* No comment provided by engineer. */ +"Cannot forward message" = "Nie można przekazać wiadomości"; + +/* alert title */ "Cannot receive file" = "Nie można odebrać pliku"; +/* snd error text */ +"Capacity exceeded - recipient did not receive previously sent messages." = "Przekroczono pojemność - odbiorca nie otrzymał wcześniej wysłanych wiadomości."; + +/* No comment provided by engineer. */ +"Cellular" = "Sieć komórkowa"; + /* No comment provided by engineer. */ "Change" = "Zmień"; +/* authentication reason */ +"Change chat profiles" = "Zmień profil czatu"; + /* No comment provided by engineer. */ "Change database passphrase?" = "Zmienić hasło bazy danych?"; @@ -702,7 +943,7 @@ "Change self-destruct mode" = "Zmień tryb samozniszczenia"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "Zmień pin samozniszczenia"; /* chat item text */ @@ -721,7 +962,16 @@ "changing address…" = "zmiana adresu…"; /* No comment provided by engineer. */ -"Chat archive" = "Archiwum czatu"; +"Chat" = "Czat"; + +/* No comment provided by engineer. */ +"Chat already exists" = "Czat już istnieje"; + +/* No comment provided by engineer. */ +"Chat already exists!" = "Czat już istnieje!"; + +/* No comment provided by engineer. */ +"Chat colors" = "Kolory czatu"; /* No comment provided by engineer. */ "Chat console" = "Konsola czatu"; @@ -733,7 +983,10 @@ "Chat database deleted" = "Baza danych czatu usunięta"; /* No comment provided by engineer. */ -"Chat database imported" = "Zaimportowano bazę danych czatu"; +"Chat database exported" = "Wyeksportowano bazę danych czatów"; + +/* No comment provided by engineer. */ +"Chat database imported" = "Zaimportowano bazę danych czatów"; /* No comment provided by engineer. */ "Chat is running" = "Czat jest uruchomiony"; @@ -744,18 +997,48 @@ /* No comment provided by engineer. */ "Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "Czat został zatrzymany. Jeśli korzystałeś już z tej bazy danych na innym urządzeniu, powinieneś przenieść ją z powrotem przed rozpoczęciem czatu."; +/* No comment provided by engineer. */ +"Chat list" = "Lista czatów"; + +/* No comment provided by engineer. */ +"Chat migrated!" = "Czat zmigrowany!"; + /* No comment provided by engineer. */ "Chat preferences" = "Preferencje czatu"; +/* alert message */ +"Chat preferences were changed." = "Preferencje czatu zostały zmienione."; + +/* No comment provided by engineer. */ +"Chat profile" = "Profil użytkownika"; + +/* No comment provided by engineer. */ +"Chat theme" = "Motyw czatu"; + +/* No comment provided by engineer. */ +"Chat will be deleted for all members - this cannot be undone!" = "Czat zostanie usunięty dla wszystkich członków – tej operacji nie można cofnąć!"; + +/* No comment provided by engineer. */ +"Chat will be deleted for you - this cannot be undone!" = "Czat zostanie usunięty dla Ciebie – tej operacji nie można cofnąć!"; + /* No comment provided by engineer. */ "Chats" = "Czaty"; /* No comment provided by engineer. */ +"Check messages every 20 min." = "Sprawdzaj wiadomości co 20 min."; + +/* No comment provided by engineer. */ +"Check messages when allowed." = "Sprawdź wiadomości, gdy będzie to dopuszczone."; + +/* alert title */ "Check server address and try again." = "Sprawdź adres serwera i spróbuj ponownie."; /* No comment provided by engineer. */ "Chinese and Spanish interface" = "Chiński i hiszpański interfejs"; +/* No comment provided by engineer. */ +"Choose _Migrate from another device_ on the new device and scan QR code." = "Wybierz _Zmigruj z innego urządzenia_ na nowym urządzeniu i zeskanuj kod QR."; + /* No comment provided by engineer. */ "Choose file" = "Wybierz plik"; @@ -763,6 +1046,15 @@ "Choose from library" = "Wybierz z biblioteki"; /* No comment provided by engineer. */ +"Chunks deleted" = "Fragmenty usunięte"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "Fragmenty pobrane"; + +/* No comment provided by engineer. */ +"Chunks uploaded" = "Fragmenty przesłane"; + +/* swipe action */ "Clear" = "Wyczyść"; /* No comment provided by engineer. */ @@ -778,10 +1070,13 @@ "Clear verification" = "Wyczyść weryfikację"; /* No comment provided by engineer. */ -"colored" = "kolorowy"; +"Color chats with the new themes." = "Koloruj czaty z nowymi motywami."; /* No comment provided by engineer. */ -"Colors" = "Kolory"; +"Color mode" = "Tryb koloru"; + +/* No comment provided by engineer. */ +"colored" = "kolorowy"; /* server test step */ "Compare file" = "Porównaj plik"; @@ -792,15 +1087,39 @@ /* No comment provided by engineer. */ "complete" = "kompletny"; +/* No comment provided by engineer. */ +"Completed" = "Zakończono"; + +/* No comment provided by engineer. */ +"Conditions accepted on: %@." = "Warunki zaakceptowane dnia: %@."; + +/* No comment provided by engineer. */ +"Conditions are accepted for the operator(s): **%@**." = "Warunki zostały zaakceptowane przez operatora(-ów): **%@**."; + +/* No comment provided by engineer. */ +"Conditions are already accepted for these operator(s): **%@**." = "Warunki zostały już zaakceptowane przez tego(-ych) operatora(-ów): **%@**."; + +/* No comment provided by engineer. */ +"Conditions of use" = "Warunki użytkowania"; + /* No comment provided by engineer. */ "Configure ICE servers" = "Skonfiguruj serwery ICE"; /* No comment provided by engineer. */ "Confirm" = "Potwierdź"; +/* No comment provided by engineer. */ +"Confirm contact deletion?" = "Potwierdzić usunięcie kontaktu?"; + /* No comment provided by engineer. */ "Confirm database upgrades" = "Potwierdź aktualizacje bazy danych"; +/* No comment provided by engineer. */ +"Confirm files from unknown servers." = "Potwierdzaj pliki z nieznanych serwerów."; + +/* No comment provided by engineer. */ +"Confirm network settings" = "Potwierdź ustawienia sieciowe"; + /* No comment provided by engineer. */ "Confirm new passphrase…" = "Potwierdź nowe hasło…"; @@ -810,6 +1129,12 @@ /* No comment provided by engineer. */ "Confirm password" = "Potwierdź hasło"; +/* No comment provided by engineer. */ +"Confirm that you remember database passphrase to migrate it." = "Potwierdź, że pamiętasz hasło do bazy danych, aby ją zmigrować."; + +/* No comment provided by engineer. */ +"Confirm upload" = "Potwierdź wgranie"; + /* server test step */ "Connect" = "Połącz"; @@ -825,6 +1150,9 @@ /* No comment provided by engineer. */ "connect to SimpleX Chat developers." = "połącz się z deweloperami SimpleX Chat."; +/* No comment provided by engineer. */ +"Connect to your friends faster." = "Szybciej łącz się ze znajomymi."; + /* No comment provided by engineer. */ "Connect to yourself?" = "Połączyć się ze sobą?"; @@ -849,18 +1177,27 @@ /* No comment provided by engineer. */ "connected" = "połączony"; +/* No comment provided by engineer. */ +"Connected" = "Połączony"; + /* No comment provided by engineer. */ "Connected desktop" = "Połączony komputer"; /* rcv group event chat item */ "connected directly" = "połącz bezpośrednio"; +/* No comment provided by engineer. */ +"Connected servers" = "Połączone serwery"; + /* No comment provided by engineer. */ "Connected to desktop" = "Połączony do komputera"; /* No comment provided by engineer. */ "connecting" = "łączenie"; +/* No comment provided by engineer. */ +"Connecting" = "Łączenie"; + /* No comment provided by engineer. */ "connecting (accepted)" = "łączenie (zaakceptowane)"; @@ -882,15 +1219,21 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "Łączenie z serwerem... (błąd: %@)"; +/* No comment provided by engineer. */ +"Connecting to contact, please wait or check later!" = "Łączenie z kontaktem, poczekaj lub sprawdź później!"; + /* No comment provided by engineer. */ "Connecting to desktop" = "Łączenie z komputerem"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "łączenie…"; /* No comment provided by engineer. */ "Connection" = "Połączenie"; +/* No comment provided by engineer. */ +"Connection and servers status." = "Stan połączenia i serwerów."; + /* No comment provided by engineer. */ "Connection error" = "Błąd połączenia"; @@ -900,6 +1243,9 @@ /* chat list item title (it should not be shown */ "connection established" = "połączenie ustanowione"; +/* No comment provided by engineer. */ +"Connection notifications" = "Powiadomienia o połączeniu"; + /* No comment provided by engineer. */ "Connection request sent!" = "Prośba o połączenie wysłana!"; @@ -909,9 +1255,15 @@ /* No comment provided by engineer. */ "Connection timeout" = "Czas połączenia minął"; +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "Połączenie z komputerem zakończone"; + /* connection information */ "connection:%@" = "połączenie: %@"; +/* No comment provided by engineer. */ +"Connections" = "Połączenia"; + /* profile update event chat item */ "contact %@ changed to %@" = "kontakt %1$@ zmieniony na %2$@"; @@ -921,6 +1273,9 @@ /* No comment provided by engineer. */ "Contact already exists" = "Kontakt już istnieje"; +/* No comment provided by engineer. */ +"Contact deleted!" = "Kontakt usunięty!"; + /* No comment provided by engineer. */ "contact has e2e encryption" = "kontakt posiada szyfrowanie e2e"; @@ -934,7 +1289,7 @@ "Contact is connected" = "Kontakt jest połączony"; /* No comment provided by engineer. */ -"Contact is not connected yet!" = "Kontakt nie jest jeszcze połączony!"; +"Contact is deleted." = "Kontakt jest usunięty."; /* No comment provided by engineer. */ "Contact name" = "Nazwa kontaktu"; @@ -942,6 +1297,9 @@ /* No comment provided by engineer. */ "Contact preferences" = "Preferencje kontaktu"; +/* No comment provided by engineer. */ +"Contact will be deleted - this cannot be undone!" = "Kontakt zostanie usunięty – nie można tego cofnąć!"; + /* No comment provided by engineer. */ "Contacts" = "Kontakty"; @@ -951,12 +1309,21 @@ /* No comment provided by engineer. */ "Continue" = "Kontynuuj"; -/* chat item action */ +/* No comment provided by engineer. */ +"Conversation deleted!" = "Rozmowa usunięta!"; + +/* No comment provided by engineer. */ "Copy" = "Kopiuj"; +/* No comment provided by engineer. */ +"Copy error" = "Kopiuj błąd"; + /* No comment provided by engineer. */ "Core version: v%@" = "Wersja rdzenia: v%@"; +/* No comment provided by engineer. */ +"Corner" = "Róg"; + /* No comment provided by engineer. */ "Correct name to %@?" = "Poprawić imię na %@?"; @@ -966,9 +1333,6 @@ /* No comment provided by engineer. */ "Create a group using a random profile." = "Utwórz grupę używając losowego profilu."; -/* No comment provided by engineer. */ -"Create an address to let people connect with you." = "Utwórz adres, aby ludzie mogli się z Tobą połączyć."; - /* server test step */ "Create file" = "Utwórz plik"; @@ -999,6 +1363,9 @@ /* No comment provided by engineer. */ "Create your profile" = "Utwórz swój profil"; +/* No comment provided by engineer. */ +"Created" = "Utworzono"; + /* No comment provided by engineer. */ "Created at" = "Utworzony o"; @@ -1006,7 +1373,7 @@ "Created at: %@" = "Utworzony o: %@"; /* No comment provided by engineer. */ -"Created on %@" = "Utworzony w dniu %@"; +"Creating archive link" = "Tworzenie linku archiwum"; /* No comment provided by engineer. */ "Creating link…" = "Tworzenie linku…"; @@ -1020,6 +1387,9 @@ /* No comment provided by engineer. */ "Current passphrase…" = "Obecne hasło…"; +/* No comment provided by engineer. */ +"Current profile" = "Bieżący profil"; + /* No comment provided by engineer. */ "Currently maximum supported file size is %@." = "Obecnie maksymalna obsługiwana wielkość pliku wynosi %@."; @@ -1029,9 +1399,15 @@ /* No comment provided by engineer. */ "Custom time" = "Niestandardowy czas"; +/* No comment provided by engineer. */ +"Customize theme" = "Dostosuj motyw"; + /* No comment provided by engineer. */ "Dark" = "Ciemny"; +/* No comment provided by engineer. */ +"Dark mode colors" = "Kolory ciemnego trybu"; + /* No comment provided by engineer. */ "Database downgrade" = "Obniż wersję bazy danych"; @@ -1092,13 +1468,20 @@ /* time unit */ "days" = "dni"; +/* No comment provided by engineer. */ +"Debug delivery" = "Dostarczenie debugowania"; + /* No comment provided by engineer. */ "Decentralized" = "Zdecentralizowane"; /* message decrypt error item */ "Decryption error" = "Błąd odszyfrowania"; -/* pref value */ +/* No comment provided by engineer. */ +"decryption errors" = "błąd odszyfrowywania"; + +/* delete after time +pref value */ "default (%@)" = "domyślne (%@)"; /* No comment provided by engineer. */ @@ -1107,9 +1490,13 @@ /* No comment provided by engineer. */ "default (yes)" = "domyślnie (tak)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "Usuń"; +/* No comment provided by engineer. */ +"Delete %lld messages of members?" = "Usunąć %lld wiadomości członków?"; + /* No comment provided by engineer. */ "Delete %lld messages?" = "Usunąć %lld wiadomości?"; @@ -1128,12 +1515,6 @@ /* No comment provided by engineer. */ "Delete and notify contact" = "Usuń i powiadom kontakt"; -/* No comment provided by engineer. */ -"Delete archive" = "Usuń archiwum"; - -/* No comment provided by engineer. */ -"Delete chat archive?" = "Usunąć archiwum czatu?"; - /* No comment provided by engineer. */ "Delete chat profile" = "Usuń profil czatu"; @@ -1147,14 +1528,14 @@ "Delete contact" = "Usuń kontakt"; /* No comment provided by engineer. */ -"Delete Contact" = "Usuń Kontakt"; - -/* No comment provided by engineer. */ -"Delete contact?\nThis cannot be undone!" = "Usunąć kontakt?\nTo nie może być cofnięte!"; +"Delete contact?" = "Usunąć kontakt?"; /* No comment provided by engineer. */ "Delete database" = "Usuń bazę danych"; +/* No comment provided by engineer. */ +"Delete database from this device" = "Usuń bazę danych z tego urządzenia"; + /* server test step */ "Delete file" = "Usuń plik"; @@ -1191,7 +1572,7 @@ /* No comment provided by engineer. */ "Delete message?" = "Usunąć wiadomość?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "Usuń wiadomości"; /* No comment provided by engineer. */ @@ -1203,9 +1584,6 @@ /* No comment provided by engineer. */ "Delete old database?" = "Usunąć starą bazę danych?"; -/* No comment provided by engineer. */ -"Delete pending connection" = "Usuń oczekujące połączenie"; - /* No comment provided by engineer. */ "Delete pending connection?" = "Usunąć oczekujące połączenie?"; @@ -1215,12 +1593,21 @@ /* server test step */ "Delete queue" = "Usuń kolejkę"; +/* No comment provided by engineer. */ +"Delete up to 20 messages at once." = "Usuń do 20 wiadomości na raz."; + /* No comment provided by engineer. */ "Delete user profile?" = "Usunąć profil użytkownika?"; +/* No comment provided by engineer. */ +"Delete without notification" = "Usuń bez powiadomienia"; + /* deleted chat item */ "deleted" = "usunięty"; +/* No comment provided by engineer. */ +"Deleted" = "Usunięto"; + /* No comment provided by engineer. */ "Deleted at" = "Usunięto o"; @@ -1233,6 +1620,9 @@ /* rcv group event chat item */ "deleted group" = "usunięta grupa"; +/* No comment provided by engineer. */ +"Deletion errors" = "Błędy usuwania"; + /* No comment provided by engineer. */ "Delivery" = "Dostarczenie"; @@ -1254,9 +1644,27 @@ /* No comment provided by engineer. */ "Desktop devices" = "Urządzenia komputerowe"; +/* No comment provided by engineer. */ +"Destination server address of %@ is incompatible with forwarding server %@ settings." = "Adres serwera docelowego %@ jest niekompatybilny z ustawieniami serwera przekazującego %@."; + +/* snd error text */ +"Destination server error: %@" = "Błąd docelowego serwera: %@"; + +/* No comment provided by engineer. */ +"Destination server version of %@ is incompatible with forwarding server %@." = "Wersja serwera docelowego %@ jest niekompatybilna z serwerem przekierowującym %@."; + +/* No comment provided by engineer. */ +"Detailed statistics" = "Szczegółowe statystyki"; + +/* No comment provided by engineer. */ +"Details" = "Szczegóły"; + /* No comment provided by engineer. */ "Develop" = "Deweloperskie"; +/* No comment provided by engineer. */ +"Developer options" = "Opcje deweloperskie"; + /* No comment provided by engineer. */ "Developer tools" = "Narzędzia deweloperskie"; @@ -1282,7 +1690,7 @@ "Direct messages" = "Bezpośrednie wiadomości"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "Bezpośrednie wiadomości między członkami są zabronione w tej grupie."; +"Direct messages between members are prohibited." = "Bezpośrednie wiadomości między członkami są zabronione w tej grupie."; /* No comment provided by engineer. */ "Disable (keep overrides)" = "Wyłącz (zachowaj nadpisania)"; @@ -1296,6 +1704,9 @@ /* No comment provided by engineer. */ "disabled" = "wyłączony"; +/* No comment provided by engineer. */ +"Disabled" = "Wyłączony"; + /* No comment provided by engineer. */ "Disappearing message" = "Znikająca wiadomość"; @@ -1306,7 +1717,7 @@ "Disappearing messages are prohibited in this chat." = "Znikające wiadomości są zabronione na tym czacie."; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "Znikające wiadomości są zabronione w tej grupie."; +"Disappearing messages are prohibited." = "Znikające wiadomości są zabronione w tej grupie."; /* No comment provided by engineer. */ "Disappears at" = "Znika o"; @@ -1332,6 +1743,15 @@ /* No comment provided by engineer. */ "Do not send history to new members." = "Nie wysyłaj historii do nowych członków."; +/* No comment provided by engineer. */ +"Do NOT send messages directly, even if your or destination server does not support private routing." = "NIE wysyłaj wiadomości bezpośrednio, nawet jeśli serwer docelowy nie obsługuje prywatnego trasowania."; + +/* No comment provided by engineer. */ +"Do not use credentials with proxy." = "Nie używaj danych logowania do proxy."; + +/* No comment provided by engineer. */ +"Do NOT use private routing." = "NIE używaj prywatnego trasowania."; + /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "NIE używaj SimpleX do połączeń alarmowych."; @@ -1347,15 +1767,43 @@ /* No comment provided by engineer. */ "Downgrade and open chat" = "Obniż wersję i otwórz czat"; +/* alert button +chat item action */ +"Download" = "Pobierz"; + +/* No comment provided by engineer. */ +"Download errors" = "Błędy pobierania"; + +/* No comment provided by engineer. */ +"Download failed" = "Pobieranie nie udane"; + /* server test step */ "Download file" = "Pobierz plik"; +/* alert action */ +"Download files" = "Pobierz pliki"; + +/* No comment provided by engineer. */ +"Downloaded" = "Pobrane"; + +/* No comment provided by engineer. */ +"Downloaded files" = "Pobrane pliki"; + +/* No comment provided by engineer. */ +"Downloading archive" = "Pobieranie archiwum"; + +/* No comment provided by engineer. */ +"Downloading link details" = "Pobieranie szczegółów linku"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Zduplikowana wyświetlana nazwa!"; /* integrity error chat item */ "duplicate message" = "zduplikowana wiadomość"; +/* No comment provided by engineer. */ +"duplicates" = "duplikaty"; + /* No comment provided by engineer. */ "Duration" = "Czas trwania"; @@ -1374,7 +1822,7 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "Włącz (zachowaj nadpisania)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "Czy włączyć automatyczne usuwanie wiadomości?"; /* No comment provided by engineer. */ @@ -1383,6 +1831,9 @@ /* No comment provided by engineer. */ "Enable for all" = "Włącz dla wszystkich"; +/* No comment provided by engineer. */ +"Enable in direct chats (BETA)!" = "Włącz w czatach bezpośrednich (BETA)!"; + /* No comment provided by engineer. */ "Enable instant notifications?" = "Włączyć natychmiastowe powiadomienia?"; @@ -1410,6 +1861,12 @@ /* enabled status */ "enabled" = "włączone"; +/* No comment provided by engineer. */ +"Enabled" = "Włączony"; + +/* No comment provided by engineer. */ +"Enabled for" = "Włączony dla"; + /* enabled status */ "enabled for contact" = "włączone dla kontaktu"; @@ -1497,6 +1954,9 @@ /* No comment provided by engineer. */ "Enter Passcode" = "Wprowadź Pin"; +/* No comment provided by engineer. */ +"Enter passphrase" = "Wprowadź hasło"; + /* No comment provided by engineer. */ "Enter passphrase…" = "Wprowadź hasło…"; @@ -1530,21 +1990,27 @@ /* No comment provided by engineer. */ "Error accepting contact request" = "Błąd przyjmowania prośby o kontakt"; -/* No comment provided by engineer. */ -"Error accessing database file" = "Błąd dostępu do pliku bazy danych"; - /* No comment provided by engineer. */ "Error adding member(s)" = "Błąd dodawania członka(ów)"; /* No comment provided by engineer. */ "Error changing address" = "Błąd zmiany adresu"; +/* No comment provided by engineer. */ +"Error changing connection profile" = "Błąd zmiany połączenia profilu"; + /* No comment provided by engineer. */ "Error changing role" = "Błąd zmiany roli"; /* No comment provided by engineer. */ "Error changing setting" = "Błąd zmiany ustawienia"; +/* No comment provided by engineer. */ +"Error changing to incognito!" = "Błąd zmiany na incognito!"; + +/* No comment provided by engineer. */ +"Error connecting to forwarding server %@. Please try later." = "Błąd połączenia z serwerem przekierowania %@. Spróbuj ponownie później."; + /* No comment provided by engineer. */ "Error creating address" = "Błąd tworzenia adresu"; @@ -1575,9 +2041,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "Błąd usuwania połączenia"; -/* No comment provided by engineer. */ -"Error deleting contact" = "Błąd usuwania kontaktu"; - /* No comment provided by engineer. */ "Error deleting database" = "Błąd usuwania bazy danych"; @@ -1590,6 +2053,9 @@ /* No comment provided by engineer. */ "Error deleting user profile" = "Błąd usuwania profilu użytkownika"; +/* No comment provided by engineer. */ +"Error downloading the archive" = "Błąd pobierania archiwum"; + /* No comment provided by engineer. */ "Error enabling delivery receipts!" = "Błąd włączania potwierdzeń dostawy!"; @@ -1602,6 +2068,9 @@ /* No comment provided by engineer. */ "Error exporting chat database" = "Błąd eksportu bazy danych czatu"; +/* No comment provided by engineer. */ +"Error exporting theme: %@" = "Błąd eksportowania motywu: %@"; + /* No comment provided by engineer. */ "Error importing chat database" = "Błąd importu bazy danych czatu"; @@ -1609,19 +2078,25 @@ "Error joining group" = "Błąd dołączenia do grupy"; /* No comment provided by engineer. */ -"Error loading %@ servers" = "Błąd ładowania %@ serwerów"; +"Error migrating settings" = "Błąd migracji ustawień"; /* No comment provided by engineer. */ "Error opening chat" = "Błąd otwierania czatu"; -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Błąd odbioru pliku"; +/* No comment provided by engineer. */ +"Error reconnecting server" = "Błąd ponownego łączenia z serwerem"; + +/* No comment provided by engineer. */ +"Error reconnecting servers" = "Błąd ponownego łączenia serwerów"; + /* No comment provided by engineer. */ "Error removing member" = "Błąd usuwania członka"; /* No comment provided by engineer. */ -"Error saving %@ servers" = "Błąd zapisu %@ serwerów"; +"Error resetting statistics" = "Błąd resetowania statystyk"; /* No comment provided by engineer. */ "Error saving group profile" = "Błąd zapisu profilu grupy"; @@ -1635,6 +2110,9 @@ /* No comment provided by engineer. */ "Error saving passphrase to keychain" = "Błąd zapisu hasła do pęku kluczy"; +/* when migrating */ +"Error saving settings" = "Błąd zapisywania ustawień"; + /* No comment provided by engineer. */ "Error saving user password" = "Błąd zapisu hasła użytkownika"; @@ -1660,6 +2138,9 @@ "Error stopping chat" = "Błąd zatrzymania czatu"; /* No comment provided by engineer. */ +"Error switching profile" = "Błąd zmiany profilu"; + +/* alertTitle */ "Error switching profile!" = "Błąd przełączania profilu!"; /* No comment provided by engineer. */ @@ -1678,9 +2159,17 @@ "Error updating user privacy" = "Błąd aktualizacji prywatności użytkownika"; /* No comment provided by engineer. */ -"Error: " = "Błąd: "; +"Error uploading the archive" = "Błąd wgrywania archiwum"; /* No comment provided by engineer. */ +"Error verifying passphrase:" = "Błąd weryfikowania hasła:"; + +/* No comment provided by engineer. */ +"Error: " = "Błąd: "; + +/* alert message +file error text +snd error text */ "Error: %@" = "Błąd: %@"; /* No comment provided by engineer. */ @@ -1690,10 +2179,10 @@ "Error: URL is invalid" = "Błąd: URL jest nieprawidłowy"; /* No comment provided by engineer. */ -"Even when disabled in the conversation." = "Nawet po wyłączeniu w rozmowie."; +"Errors" = "Błędy"; /* No comment provided by engineer. */ -"event happened" = "nowe wydarzenie"; +"Even when disabled in the conversation." = "Nawet po wyłączeniu w rozmowie."; /* No comment provided by engineer. */ "Exit without saving" = "Wyjdź bez zapisywania"; @@ -1701,15 +2190,24 @@ /* chat item action */ "Expand" = "Rozszerz"; +/* No comment provided by engineer. */ +"expired" = "wygasły"; + /* No comment provided by engineer. */ "Export database" = "Eksportuj bazę danych"; /* No comment provided by engineer. */ "Export error:" = "Błąd eksportu:"; +/* No comment provided by engineer. */ +"Export theme" = "Eksportuj motyw"; + /* No comment provided by engineer. */ "Exported database archive." = "Wyeksportowane archiwum bazy danych."; +/* No comment provided by engineer. */ +"Exported file doesn't exist" = "Wyeksportowany plik nie istnieje"; + /* No comment provided by engineer. */ "Exporting database archive…" = "Eksportowanie archiwum bazy danych…"; @@ -1722,9 +2220,27 @@ /* No comment provided by engineer. */ "Faster joining and more reliable messages." = "Szybsze dołączenie i bardziej niezawodne wiadomości."; -/* No comment provided by engineer. */ +/* swipe action */ "Favorite" = "Ulubione"; +/* file error alert title */ +"File error" = "Błąd pliku"; + +/* alert message */ +"File errors:\n%@" = "Błędy pliku:\n%@"; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "Nie odnaleziono pliku - najprawdopodobniej plik został usunięty lub anulowany."; + +/* file error text */ +"File server error: %@" = "Błąd serwera plików: %@"; + +/* No comment provided by engineer. */ +"File status" = "Status pliku"; + +/* copied message info */ +"File status: %@" = "Status pliku: %@"; + /* No comment provided by engineer. */ "File will be deleted from servers." = "Plik zostanie usunięty z serwerów."; @@ -1737,6 +2253,9 @@ /* No comment provided by engineer. */ "File: %@" = "Plik: %@"; +/* No comment provided by engineer. */ +"Files" = "Pliki"; + /* No comment provided by engineer. */ "Files & media" = "Pliki i media"; @@ -1744,7 +2263,10 @@ "Files and media" = "Pliki i media"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "Pliki i media są zabronione w tej grupie."; +"Files and media are prohibited." = "Pliki i media są zabronione w tej grupie."; + +/* No comment provided by engineer. */ +"Files and media not allowed" = "Pliki i multimedia nie są dozwolone"; /* No comment provided by engineer. */ "Files and media prohibited!" = "Pliki i media zabronione!"; @@ -1752,6 +2274,12 @@ /* No comment provided by engineer. */ "Filter unread and favorite chats." = "Filtruj nieprzeczytane i ulubione czaty."; +/* No comment provided by engineer. */ +"Finalize migration" = "Dokończ migrację"; + +/* No comment provided by engineer. */ +"Finalize migration on another device." = "Dokończ migrację na innym urządzeniu."; + /* No comment provided by engineer. */ "Finally, we have them! 🚀" = "W końcu je mamy! 🚀"; @@ -1779,6 +2307,48 @@ /* No comment provided by engineer. */ "For console" = "Dla konsoli"; +/* chat item action */ +"Forward" = "Przekaż dalej"; + +/* alert title */ +"Forward %d message(s)?" = "Przekazać %d wiadomość(i)?"; + +/* No comment provided by engineer. */ +"Forward and save messages" = "Przesyłaj dalej i zapisuj wiadomości"; + +/* alert action */ +"Forward messages" = "Przekaż wiadomości"; + +/* alert message */ +"Forward messages without files?" = "Przekazać wiadomości bez plików?"; + +/* No comment provided by engineer. */ +"forwarded" = "przekazane dalej"; + +/* No comment provided by engineer. */ +"Forwarded" = "Przekazane dalej"; + +/* No comment provided by engineer. */ +"Forwarded from" = "Przekazane dalej od"; + +/* No comment provided by engineer. */ +"Forwarding %lld messages" = "Przekazywanie %lld wiadomości"; + +/* No comment provided by engineer. */ +"Forwarding server %@ failed to connect to destination server %@. Please try later." = "Serwer przekazujący %@ nie mógł połączyć się z serwerem docelowym %@. Spróbuj ponownie później."; + +/* No comment provided by engineer. */ +"Forwarding server address is incompatible with network settings: %@." = "Adres serwera przekierowującego jest niekompatybilny z ustawieniami sieciowymi: %@."; + +/* No comment provided by engineer. */ +"Forwarding server version is incompatible with network settings: %@." = "Wersja serwera przekierowującego jest niekompatybilna z ustawieniami sieciowymi: %@."; + +/* snd error text */ +"Forwarding server: %@\nDestination server error: %@" = "Serwer przekazujący: %1$@\nBłąd serwera docelowego: %2$@"; + +/* snd error text */ +"Forwarding server: %@\nError: %@" = "Serwer przekazujący: %1$@\nBłąd: %2$@"; + /* No comment provided by engineer. */ "Found desktop" = "Znaleziono komputer"; @@ -1791,9 +2361,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Pełna nazwa (opcjonalna)"; -/* No comment provided by engineer. */ -"Full name:" = "Pełna nazwa:"; - /* No comment provided by engineer. */ "Fully decentralized – visible only to members." = "W pełni zdecentralizowana – widoczna tylko dla członków."; @@ -1806,6 +2373,12 @@ /* No comment provided by engineer. */ "GIFs and stickers" = "GIF-y i naklejki"; +/* message preview */ +"Good afternoon!" = "Dzień dobry!"; + +/* message preview */ +"Good morning!" = "Dzień dobry!"; + /* No comment provided by engineer. */ "Group" = "Grupa"; @@ -1842,24 +2415,6 @@ /* No comment provided by engineer. */ "Group links" = "Linki grupowe"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "Członkowie grupy mogą dodawać reakcje wiadomości."; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "Członkowie grupy mogą nieodwracalnie usuwać wysłane wiadomości. (24 godziny)"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "Członkowie grupy mogą wysyłać bezpośrednie wiadomości."; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "Członkowie grupy mogą wysyłać znikające wiadomości."; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "Członkowie grupy mogą wysyłać pliki i media."; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "Członkowie grupy mogą wysyłać wiadomości głosowe."; - /* notification */ "Group message:" = "Wiadomość grupowa:"; @@ -1920,9 +2475,6 @@ /* time unit */ "hours" = "godziny"; -/* No comment provided by engineer. */ -"How it works" = "Jak to działa"; - /* No comment provided by engineer. */ "How SimpleX works" = "Jak działa SimpleX"; @@ -1935,6 +2487,9 @@ /* No comment provided by engineer. */ "How to use your servers" = "Jak korzystać z Twoich serwerów"; +/* No comment provided by engineer. */ +"Hungarian interface" = "Węgierski interfejs"; + /* No comment provided by engineer. */ "ICE servers (one per line)" = "Serwery ICE (po jednym na linię)"; @@ -1963,7 +2518,7 @@ "Immediately" = "Natychmiast"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "Odporność na spam i nadużycia"; +"Immune to spam" = "Odporność na spam i nadużycia"; /* No comment provided by engineer. */ "Import" = "Importuj"; @@ -1974,6 +2529,15 @@ /* No comment provided by engineer. */ "Import database" = "Importuj bazę danych"; +/* No comment provided by engineer. */ +"Import failed" = "Import nie udał się"; + +/* No comment provided by engineer. */ +"Import theme" = "Importuj motyw"; + +/* No comment provided by engineer. */ +"Importing archive" = "Importowanie archiwum"; + /* No comment provided by engineer. */ "Improved message delivery" = "Ulepszona dostawa wiadomości"; @@ -1983,9 +2547,18 @@ /* No comment provided by engineer. */ "Improved server configuration" = "Ulepszona konfiguracja serwera"; +/* No comment provided by engineer. */ +"In order to continue, chat should be stopped." = "Aby konturować, czat musi zostać zatrzymany."; + /* No comment provided by engineer. */ "In reply to" = "W odpowiedzi na"; +/* No comment provided by engineer. */ +"In-call sounds" = "Dźwięki w rozmowie"; + +/* No comment provided by engineer. */ +"inactive" = "nieaktywny"; + /* No comment provided by engineer. */ "Incognito" = "Incognito"; @@ -2040,14 +2613,17 @@ /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Zainstaluj [SimpleX Chat na terminal](https://github.com/simplex-chat/simplex-chat)"; +/* No comment provided by engineer. */ +"Instant" = "Natychmiastowo"; + /* No comment provided by engineer. */ "Instant push notifications will be hidden!\n" = "Natychmiastowe powiadomienia push będą ukryte!\n"; /* No comment provided by engineer. */ -"Instantly" = "Natychmiastowo"; +"Interface" = "Interfejs"; /* No comment provided by engineer. */ -"Interface" = "Interfejs"; +"Interface colors" = "Kolory interfejsu"; /* invalid chat data */ "invalid chat" = "nieprawidłowy czat"; @@ -2067,6 +2643,9 @@ /* No comment provided by engineer. */ "Invalid link" = "Nieprawidłowy link"; +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Nieprawidłowe potwierdzenie migracji"; + /* No comment provided by engineer. */ "Invalid name!" = "Nieprawidłowa nazwa!"; @@ -2076,7 +2655,7 @@ /* No comment provided by engineer. */ "Invalid response" = "Nieprawidłowa odpowiedź"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "Nieprawidłowy adres serwera!"; /* item status text */ @@ -2088,6 +2667,9 @@ /* group name */ "invitation to group %@" = "zaproszenie do grupy %@"; +/* No comment provided by engineer. */ +"invite" = "zaproś"; + /* No comment provided by engineer. */ "Invite friends" = "Zaproś znajomych"; @@ -2115,6 +2697,9 @@ /* No comment provided by engineer. */ "iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "iOS Keychain będzie używany do bezpiecznego przechowywania hasła po ponownym uruchomieniu aplikacji lub zmianie hasła - pozwoli to na otrzymywanie powiadomień push."; +/* No comment provided by engineer. */ +"IP address" = "Adres IP"; + /* No comment provided by engineer. */ "Irreversible message deletion" = "Nieodwracalne usuwanie wiadomości"; @@ -2122,7 +2707,7 @@ "Irreversible message deletion is prohibited in this chat." = "Nieodwracalne usuwanie wiadomości jest na tym czacie zabronione."; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "Nieodwracalne usuwanie wiadomości jest w tej grupie zabronione."; +"Irreversible message deletion is prohibited." = "Nieodwracalne usuwanie wiadomości jest w tej grupie zabronione."; /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "To pozwala na posiadanie wielu anonimowych połączeń bez żadnych wspólnych danych między nimi w pojedynczym profilu czatu."; @@ -2133,6 +2718,9 @@ /* No comment provided by engineer. */ "It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Może to nastąpić, gdy:\n1. Wiadomości wygasły w wysyłającym kliencie po 2 dniach lub na serwerze po 30 dniach.\n2. Odszyfrowanie wiadomości nie powiodło się, ponieważ Ty lub Twój kontakt użyliście starej kopii zapasowej bazy danych.\n3. Połączenie zostało skompromitowane."; +/* No comment provided by engineer. */ +"It protects your IP address and connections." = "Chroni Twój adres IP i połączenia."; + /* No comment provided by engineer. */ "It seems like you are already connected via this link. If it is not the case, there was an error (%@)." = "Wygląda na to, że jesteś już połączony przez ten link. Jeśli tak nie jest, wystąpił błąd (%@)."; @@ -2145,7 +2733,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "Japoński interfejs"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "Dołącz"; /* No comment provided by engineer. */ @@ -2172,13 +2760,16 @@ /* No comment provided by engineer. */ "Joining group" = "Dołączanie do grupy"; -/* No comment provided by engineer. */ +/* alert action */ "Keep" = "Zachowaj"; +/* No comment provided by engineer. */ +"Keep conversation" = "Zachowaj rozmowę"; + /* No comment provided by engineer. */ "Keep the app open to use it from desktop" = "Zostaw aplikację otwartą i używaj ją z komputera"; -/* No comment provided by engineer. */ +/* alert title */ "Keep unused invitation?" = "Zachować nieużyte zaproszenie?"; /* No comment provided by engineer. */ @@ -2196,7 +2787,7 @@ /* No comment provided by engineer. */ "Learn more" = "Dowiedz się więcej"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "Opuść"; /* No comment provided by engineer. */ @@ -2235,9 +2826,6 @@ /* No comment provided by engineer. */ "Live messages" = "Wiadomości na żywo"; -/* No comment provided by engineer. */ -"Local" = "Lokalnie"; - /* No comment provided by engineer. */ "Local name" = "Nazwa lokalna"; @@ -2250,24 +2838,15 @@ /* No comment provided by engineer. */ "Lock mode" = "Tryb blokady"; -/* No comment provided by engineer. */ -"Make a private connection" = "Nawiąż prywatne połączenie"; - /* No comment provided by engineer. */ "Make one message disappear" = "Spraw, aby jedna wiadomość zniknęła"; /* No comment provided by engineer. */ "Make profile private!" = "Ustaw profil jako prywatny!"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Upewnij się, że adresy serwerów %@ są w poprawnym formacie, rozdzielone liniami i nie są zduplikowane (%@)."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Upewnij się, że adresy serwerów WebRTC ICE są w poprawnym formacie, rozdzielone liniami i nie są zduplikowane."; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Wiele osób pytało: *jeśli SimpleX nie ma identyfikatora użytkownika, jak może dostarczać wiadomości?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "Oznacz jako usunięty dla wszystkich"; @@ -2286,6 +2865,12 @@ /* No comment provided by engineer. */ "Max 30 seconds, received instantly." = "Maksymalnie 30 sekund, odbierane natychmiast."; +/* No comment provided by engineer. */ +"Media & file servers" = "Serwery mediów i plików"; + +/* blur media */ +"Medium" = "Średni"; + /* member role */ "member" = "członek"; @@ -2298,6 +2883,9 @@ /* rcv group event chat item */ "member connected" = "połączony"; +/* item status text */ +"Member inactive" = "Członek nieaktywny"; + /* No comment provided by engineer. */ "Member role will be changed to \"%@\". All group members will be notified." = "Rola członka grupy zostanie zmieniona na \"%@\". Wszyscy członkowie grupy zostaną powiadomieni."; @@ -2307,15 +2895,54 @@ /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "Członek zostanie usunięty z grupy - nie można tego cofnąć!"; +/* No comment provided by engineer. */ +"Members can add message reactions." = "Członkowie grupy mogą dodawać reakcje wiadomości."; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "Członkowie grupy mogą nieodwracalnie usuwać wysłane wiadomości. (24 godziny)"; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "Członkowie grupy mogą wysyłać bezpośrednie wiadomości."; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "Członkowie grupy mogą wysyłać znikające wiadomości."; + +/* No comment provided by engineer. */ +"Members can send files and media." = "Członkowie grupy mogą wysyłać pliki i media."; + +/* No comment provided by engineer. */ +"Members can send SimpleX links." = "Członkowie grupy mogą wysyłać linki SimpleX."; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "Członkowie grupy mogą wysyłać wiadomości głosowe."; + +/* No comment provided by engineer. */ +"Menus" = "Menu"; + +/* No comment provided by engineer. */ +"message" = "wiadomość"; + /* item status text */ "Message delivery error" = "Błąd dostarczenia wiadomości"; /* No comment provided by engineer. */ "Message delivery receipts!" = "Potwierdzenia dostarczenia wiadomości!"; +/* item status text */ +"Message delivery warning" = "Ostrzeżenie dostarczenia wiadomości"; + /* No comment provided by engineer. */ "Message draft" = "Wersja robocza wiadomości"; +/* item status text */ +"Message forwarded" = "Wiadomość przekazana"; + +/* item status description */ +"Message may be delivered later if member becomes active." = "Wiadomość może zostać dostarczona później jeśli członek stanie się aktywny."; + +/* No comment provided by engineer. */ +"Message queue info" = "Informacje kolejki wiadomości"; + /* chat feature */ "Message reactions" = "Reakcje wiadomości"; @@ -2323,14 +2950,35 @@ "Message reactions are prohibited in this chat." = "Reakcje wiadomości są zabronione na tym czacie."; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "Reakcje wiadomości są zabronione w tej grupie."; +"Message reactions are prohibited." = "Reakcje wiadomości są zabronione w tej grupie."; /* notification */ "message received" = "wiadomość otrzymana"; +/* No comment provided by engineer. */ +"Message reception" = "Odebranie wiadomości"; + +/* No comment provided by engineer. */ +"Message servers" = "Serwery wiadomości"; + +/* No comment provided by engineer. */ +"Message shape" = "Kształt wiadomości"; + +/* No comment provided by engineer. */ +"Message source remains private." = "Źródło wiadomości pozostaje prywatne."; + +/* No comment provided by engineer. */ +"Message status" = "Status wiadomości"; + +/* copied message info */ +"Message status: %@" = "Status wiadomości: %@"; + /* No comment provided by engineer. */ "Message text" = "Tekst wiadomości"; +/* No comment provided by engineer. */ +"Message too large" = "Wiadomość jest zbyt duża"; + /* No comment provided by engineer. */ "Messages" = "Wiadomości"; @@ -2340,9 +2988,45 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "Wiadomości od %@ zostaną pokazane!"; +/* No comment provided by engineer. */ +"Messages received" = "Otrzymane wiadomości"; + +/* No comment provided by engineer. */ +"Messages sent" = "Wysłane wiadomości"; + +/* alert message */ +"Messages were deleted after you selected them." = "Wiadomości zostały usunięte po wybraniu ich."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Wiadomości, pliki i połączenia są chronione przez **szyfrowanie end-to-end** z doskonałym utajnianiem z wyprzedzeniem i odzyskiem po złamaniu."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Wiadomości, pliki i połączenia są chronione przez **kwantowo odporne szyfrowanie end-to-end** z doskonałym utajnianiem z wyprzedzeniem i odzyskiem po złamaniu."; + +/* No comment provided by engineer. */ +"Migrate device" = "Zmigruj urządzenie"; + +/* No comment provided by engineer. */ +"Migrate from another device" = "Zmigruj z innego urządzenia"; + +/* No comment provided by engineer. */ +"Migrate here" = "Zmigruj tutaj"; + +/* No comment provided by engineer. */ +"Migrate to another device" = "Zmigruj do innego urządzenia"; + +/* No comment provided by engineer. */ +"Migrate to another device via QR code." = "Zmigruj do innego urządzenia przez kod QR."; + +/* No comment provided by engineer. */ +"Migrating" = "Migrowanie"; + /* No comment provided by engineer. */ "Migrating database archive…" = "Migrowanie archiwum bazy danych…"; +/* No comment provided by engineer. */ +"Migration complete" = "Migracja zakończona"; + /* No comment provided by engineer. */ "Migration error:" = "Błąd migracji:"; @@ -2353,7 +3037,7 @@ "Migration is completed" = "Migracja została zakończona"; /* No comment provided by engineer. */ -"Migrations: %@" = "Migracje: %@"; +"Migrations:" = "Migracje:"; /* time unit */ "minutes" = "minuty"; @@ -2382,48 +3066,57 @@ /* No comment provided by engineer. */ "More improvements are coming soon!" = "Więcej ulepszeń już wkrótce!"; +/* No comment provided by engineer. */ +"More reliable network connection." = "Bardziej niezawodne połączenia sieciowe."; + /* item status description */ "Most likely this connection is deleted." = "Najprawdopodobniej to połączenie jest usunięte."; -/* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "Najprawdopodobniej ten kontakt usunął połączenie z Tobą."; - /* No comment provided by engineer. */ "Multiple chat profiles" = "Wiele profili czatu"; -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "Wycisz"; /* No comment provided by engineer. */ "Muted when inactive!" = "Wyciszony, gdy jest nieaktywny!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "Nazwa"; /* No comment provided by engineer. */ "Network & servers" = "Sieć i serwery"; +/* No comment provided by engineer. */ +"Network connection" = "Połączenie z siecią"; + +/* snd error text */ +"Network issues - message expired after many attempts to send it." = "Błąd sieciowy - wiadomość wygasła po wielu próbach wysłania jej."; + +/* No comment provided by engineer. */ +"Network management" = "Zarządzenie sieciowe"; + /* No comment provided by engineer. */ "Network settings" = "Ustawienia sieci"; /* No comment provided by engineer. */ "Network status" = "Status sieci"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "nigdy"; /* No comment provided by engineer. */ "New chat" = "Nowy czat"; +/* No comment provided by engineer. */ +"New chat experience 🎉" = "Nowe możliwości czatu 🎉"; + /* notification */ "New contact request" = "Nowa prośba o kontakt"; /* notification */ "New contact:" = "Nowy kontakt:"; -/* No comment provided by engineer. */ -"New database archive" = "Nowe archiwum bazy danych"; - /* No comment provided by engineer. */ "New desktop app!" = "Nowa aplikacja desktopowa!"; @@ -2433,6 +3126,9 @@ /* No comment provided by engineer. */ "New in %@" = "Nowość w %@"; +/* No comment provided by engineer. */ +"New media options" = "Nowe opcje mediów"; + /* No comment provided by engineer. */ "New member role" = "Nowa rola członka"; @@ -2448,6 +3144,12 @@ /* No comment provided by engineer. */ "New passphrase…" = "Nowe hasło…"; +/* No comment provided by engineer. */ +"New SOCKS credentials will be used every time you start the app." = "Nowe poświadczenia SOCKS będą używane przy każdym uruchomieniu aplikacji."; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used for each server." = "Dla każdego serwera zostaną użyte nowe poświadczenia SOCKS."; + /* pref value */ "no" = "nie"; @@ -2469,6 +3171,9 @@ /* No comment provided by engineer. */ "No device token!" = "Brak tokenu urządzenia!"; +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Brak bezpośredniego połączenia, wiadomość została przekazana przez administratora."; + /* No comment provided by engineer. */ "no e2e encryption" = "brak szyfrowania e2e"; @@ -2481,18 +3186,42 @@ /* No comment provided by engineer. */ "No history" = "Brak historii"; +/* No comment provided by engineer. */ +"No info, try to reload" = "Brak informacji, spróbuj przeładować"; + +/* No comment provided by engineer. */ +"No network connection" = "Brak połączenia z siecią"; + +/* No comment provided by engineer. */ +"No permission to record speech" = "Brak zezwoleń do nagrania rozmowy"; + +/* No comment provided by engineer. */ +"No permission to record video" = "Brak zezwoleń do nagrania wideo"; + /* No comment provided by engineer. */ "No permission to record voice message" = "Brak uprawnień do nagrywania wiadomości głosowej"; +/* No comment provided by engineer. */ +"No push server" = "Lokalnie"; + /* No comment provided by engineer. */ "No received or sent files" = "Brak odebranych lub wysłanych plików"; /* copied message info in history */ "no text" = "brak tekstu"; +/* No comment provided by engineer. */ +"No user identifiers." = "Brak identyfikatorów użytkownika."; + /* No comment provided by engineer. */ "Not compatible!" = "Nie kompatybilny!"; +/* No comment provided by engineer. */ +"Nothing selected" = "Nic nie jest zaznaczone"; + +/* alert title */ +"Nothing to forward!" = "Nic do przekazania!"; + /* No comment provided by engineer. */ "Notifications" = "Powiadomienia"; @@ -2506,11 +3235,11 @@ "observer" = "obserwator"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "wyłączony"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Wyłączony"; /* feature offered item */ @@ -2519,7 +3248,7 @@ /* feature offered item */ "offered %@: %@" = "zaoferował %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ @@ -2528,9 +3257,6 @@ /* No comment provided by engineer. */ "Old database" = "Stara baza danych"; -/* No comment provided by engineer. */ -"Old database archive" = "Stare archiwum bazy danych"; - /* group pref value */ "on" = "włączone"; @@ -2538,16 +3264,19 @@ "One-time invitation link" = "Jednorazowy link zaproszenia"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "Hosty onion będą wymagane do połączenia. Wymaga włączenia VPN."; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "Hosty onion będą wymagane do połączenia.\nWymaga włączenia VPN."; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "Hosty onion będą używane, gdy będą dostępne. Wymaga włączenia VPN."; +"Onion hosts will be used when available.\nRequires compatible VPN." = "Hosty onion będą używane, gdy będą dostępne.\nWymaga włączenia VPN."; /* No comment provided by engineer. */ "Onion hosts will not be used." = "Hosty onion nie będą używane."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Tylko urządzenia klienckie przechowują profile użytkowników, kontakty, grupy i wiadomości wysyłane za pomocą **2-warstwowego szyfrowania end-to-end**."; +"Only client devices store user profiles, contacts, groups, and messages." = "Tylko urządzenia klienckie przechowują profile użytkowników, kontakty, grupy i wiadomości wysyłane za pomocą **2-warstwowego szyfrowania end-to-end**."; + +/* No comment provided by engineer. */ +"Only delete conversation" = "Usuń tylko rozmowę"; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Tylko właściciele grup mogą zmieniać preferencje grupy."; @@ -2588,7 +3317,7 @@ /* No comment provided by engineer. */ "Only your contact can send voice messages." = "Tylko Twój kontakt może wysyłać wiadomości głosowe."; -/* No comment provided by engineer. */ +/* alert action */ "Open" = "Otwórz"; /* No comment provided by engineer. */ @@ -2600,27 +3329,45 @@ /* No comment provided by engineer. */ "Open group" = "Grupa otwarta"; +/* authentication reason */ +"Open migration to another device" = "Otwórz migrację na innym urządzeniu"; + /* No comment provided by engineer. */ "Open Settings" = "Otwórz Ustawienia"; -/* authentication reason */ -"Open user profiles" = "Otwórz profile użytkownika"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "Otwarto źródłowy protokół i kod - każdy może uruchomić serwery."; - /* No comment provided by engineer. */ "Opening app…" = "Otwieranie aplikacji…"; +/* No comment provided by engineer. */ +"Or paste archive link" = "Lub wklej link archiwum"; + /* No comment provided by engineer. */ "Or scan QR code" = "Lub zeskanuj kod QR"; +/* No comment provided by engineer. */ +"Or securely share this file link" = "Lub bezpiecznie udostępnij ten link pliku"; + /* No comment provided by engineer. */ "Or show this code" = "Lub pokaż ten kod"; +/* No comment provided by engineer. */ +"other" = "inne"; + +/* No comment provided by engineer. */ +"Other" = "Inne"; + +/* No comment provided by engineer. */ +"other errors" = "inne błędy"; + +/* alert message */ +"Other file errors:\n%@" = "Inne błędy pliku:\n%@"; + /* member role */ "owner" = "właściciel"; +/* feature role */ +"owners" = "właściciele"; + /* No comment provided by engineer. */ "Passcode" = "Pin"; @@ -2636,6 +3383,9 @@ /* No comment provided by engineer. */ "Passcode set!" = "Pin ustawiony!"; +/* No comment provided by engineer. */ +"Password" = "Hasło"; + /* No comment provided by engineer. */ "Password to show" = "Hasło do wyświetlenia"; @@ -2658,23 +3408,35 @@ "peer-to-peer" = "peer-to-peer"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "Ludzie mogą się z Tobą połączyć tylko poprzez linki, które udostępniasz."; +"Pending" = "Oczekujące"; /* No comment provided by engineer. */ -"Periodically" = "Okresowo"; +"Periodic" = "Okresowo"; /* message decrypt error item */ "Permanent decryption error" = "Stały błąd odszyfrowania"; +/* No comment provided by engineer. */ +"Picture-in-picture calls" = "Połączenia obraz-w-obrazie"; + /* No comment provided by engineer. */ "PING count" = "Liczba PINGÓW"; /* No comment provided by engineer. */ "PING interval" = "Interwał PINGU"; +/* No comment provided by engineer. */ +"Play from the chat list." = "Odtwórz z listy czatów."; + +/* No comment provided by engineer. */ +"Please ask your contact to enable calls." = "Poproś kontakt o włącznie połączeń."; + /* No comment provided by engineer. */ "Please ask your contact to enable sending voice messages." = "Poproś Twój kontakt o włączenie wysyłania wiadomości głosowych."; +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Sprawdź, czy telefon i komputer są podłączone do tej samej sieci lokalnej i czy zapora sieciowa komputera umożliwia połączenie.\nProszę podzielić się innymi problemami z deweloperami."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Sprawdź, czy użyłeś prawidłowego linku lub poproś Twój kontakt o przesłanie innego."; @@ -2684,6 +3446,9 @@ /* No comment provided by engineer. */ "Please check yours and your contact preferences." = "Proszę sprawdzić preferencje Twoje i Twojego kontaktu."; +/* No comment provided by engineer. */ +"Please confirm that network settings are correct for this device." = "Proszę potwierdzić, że ustawienia sieciowe są prawidłowe dla tego urządzenia."; + /* No comment provided by engineer. */ "Please contact developers.\nError: %@" = "Proszę skontaktować się z deweloperami.\nBłąd: %@"; @@ -2691,10 +3456,10 @@ "Please contact group admin." = "Skontaktuj się z administratorem grupy."; /* No comment provided by engineer. */ -"Please enter correct current passphrase." = "Wprowadź poprawne aktualne hasło."; +"Please enter correct current passphrase." = "Wprowadź poprawny obecny kod dostępu."; /* No comment provided by engineer. */ -"Please enter the previous password after restoring database backup. This action can not be undone." = "Proszę podać poprzednie hasło po przywróceniu kopii zapasowej bazy danych. Tej czynności nie można cofnąć."; +"Please enter the previous password after restoring database backup. This action can not be undone." = "Proszę podać poprzedni kod dostępu po przywróceniu kopii zapasowej bazy danych. Tej czynności nie można cofnąć."; /* No comment provided by engineer. */ "Please remember or store it securely - there is no way to recover a lost passcode!" = "Prosimy o jego zapamiętanie lub bezpieczne przechowywanie - nie ma możliwości odzyskania utraconego pinu!"; @@ -2706,29 +3471,32 @@ "Please restart the app and migrate the database to enable push notifications." = "Uruchom ponownie aplikację i przeprowadź migrację bazy danych, aby włączyć powiadomienia push."; /* No comment provided by engineer. */ -"Please store passphrase securely, you will NOT be able to access chat if you lose it." = "Prosimy o bezpieczne przechowywanie hasła, w przypadku jego utraty NIE będzie można uzyskać dostępu do czatu."; +"Please store passphrase securely, you will NOT be able to access chat if you lose it." = "Przechowuj kod dostępu w bezpieczny sposób, w przypadku jego utraty NIE będzie można uzyskać dostępu do czatu."; /* No comment provided by engineer. */ -"Please store passphrase securely, you will NOT be able to change it if you lose it." = "Prosimy o bezpieczne przechowywanie hasła, w przypadku jego utraty NIE będzie można go zmienić."; +"Please store passphrase securely, you will NOT be able to change it if you lose it." = "Przechowuj kod dostępu w bezpieczny sposób, w przypadku jego utraty NIE będzie można go zmienić."; /* No comment provided by engineer. */ "Polish interface" = "Polski interfejs"; +/* No comment provided by engineer. */ +"Port" = "Port"; + /* server test error */ "Possibly, certificate fingerprint in server address is incorrect" = "Możliwe, że odcisk palca certyfikatu w adresie serwera jest nieprawidłowy"; /* No comment provided by engineer. */ "Preserve the last message draft, with attachments." = "Zachowaj ostatnią wersję roboczą wiadomości wraz z załącznikami."; -/* No comment provided by engineer. */ -"Preset server" = "Wstępnie ustawiony serwer"; - /* No comment provided by engineer. */ "Preset server address" = "Wstępnie ustawiony adres serwera"; /* No comment provided by engineer. */ "Preview" = "Podgląd"; +/* No comment provided by engineer. */ +"Previously connected servers" = "Wcześniej połączone serwery"; + /* No comment provided by engineer. */ "Privacy & security" = "Prywatność i bezpieczeństwo"; @@ -2738,9 +3506,21 @@ /* No comment provided by engineer. */ "Private filenames" = "Prywatne nazwy plików"; +/* No comment provided by engineer. */ +"Private message routing" = "Trasowanie prywatnych wiadomości"; + +/* No comment provided by engineer. */ +"Private message routing 🚀" = "Trasowanie prywatnych wiadomości🚀"; + /* name of notes to self */ "Private notes" = "Prywatne notatki"; +/* No comment provided by engineer. */ +"Private routing" = "Prywatne trasowanie"; + +/* No comment provided by engineer. */ +"Private routing error" = "Błąd prywatnego trasowania"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profil i połączenia z serwerem"; @@ -2748,15 +3528,15 @@ "Profile image" = "Zdjęcie profilowe"; /* No comment provided by engineer. */ -"Profile name" = "Nazwa profilu"; - -/* No comment provided by engineer. */ -"Profile name:" = "Nazwa profilu:"; +"Profile images" = "Zdjęcia profilowe"; /* No comment provided by engineer. */ "Profile password" = "Hasło profilu"; /* No comment provided by engineer. */ +"Profile theme" = "Motyw profilu"; + +/* alert message */ "Profile update will be sent to your contacts." = "Aktualizacja profilu zostanie wysłana do Twoich kontaktów."; /* No comment provided by engineer. */ @@ -2780,41 +3560,71 @@ /* No comment provided by engineer. */ "Prohibit sending files and media." = "Zakaz wysyłania plików i mediów."; +/* No comment provided by engineer. */ +"Prohibit sending SimpleX links." = "Zabroń wysyłania linków SimpleX."; + /* No comment provided by engineer. */ "Prohibit sending voice messages." = "Zabroń wysyłania wiadomości głosowych."; /* No comment provided by engineer. */ "Protect app screen" = "Chroń ekran aplikacji"; +/* No comment provided by engineer. */ +"Protect IP address" = "Chroń adres IP"; + /* No comment provided by engineer. */ "Protect your chat profiles with a password!" = "Chroń swoje profile czatu hasłem!"; +/* No comment provided by engineer. */ +"Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." = "Chroni Twój adres IP przed przekaźnikami wiadomości wybranych przez Twoje kontakty.\nWłącz w ustawianiach *Sieć i serwery* ."; + /* No comment provided by engineer. */ "Protocol timeout" = "Limit czasu protokołu"; /* No comment provided by engineer. */ "Protocol timeout per KB" = "Limit czasu protokołu na KB"; +/* No comment provided by engineer. */ +"Proxied" = "Trasowane przez proxy"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Serwery trasowane przez proxy"; + +/* No comment provided by engineer. */ +"Proxy requires password" = "Proxy wymaga hasła"; + /* No comment provided by engineer. */ "Push notifications" = "Powiadomienia push"; +/* No comment provided by engineer. */ +"Push server" = "Serwer Push"; + +/* chat item text */ +"quantum resistant e2e encryption" = "kwantowo odporne szyfrowanie e2e"; + +/* No comment provided by engineer. */ +"Quantum resistant encryption" = "Kwantowo odporne szyfrowanie"; + /* No comment provided by engineer. */ "Rate the app" = "Oceń aplikację"; +/* No comment provided by engineer. */ +"Reachable chat toolbar" = "Osiągalny pasek narzędzi czatu"; + /* chat item menu */ "React…" = "Reaguj…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "Czytaj"; /* No comment provided by engineer. */ "Read more" = "Przeczytaj więcej"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Przeczytaj więcej w [Podręczniku Użytkownika](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Przeczytaj więcej w [Poradniku Użytkownika](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Przeczytaj więcej w [Poradniku Użytkownika](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "Przeczytaj więcej w [Podręczniku Użytkownika](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)."; /* No comment provided by engineer. */ "Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Przeczytaj więcej w [Podręczniku Użytkownika](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; @@ -2823,10 +3633,10 @@ "Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Przeczytaj więcej na naszym [repozytorium GitHub](https://github.com/simplex-chat/simplex-chat#readme)."; /* No comment provided by engineer. */ -"Read more in our GitHub repository." = "Przeczytaj więcej na naszym repozytorium GitHub."; +"Receipts are disabled" = "Potwierdzenia są wyłączone"; /* No comment provided by engineer. */ -"Receipts are disabled" = "Potwierdzenia są wyłączone"; +"Receive errors" = "Błędy otrzymania"; /* No comment provided by engineer. */ "received answer…" = "otrzymano odpowiedź…"; @@ -2846,6 +3656,15 @@ /* message info title */ "Received message" = "Otrzymano wiadomość"; +/* No comment provided by engineer. */ +"Received messages" = "Otrzymane wiadomości"; + +/* No comment provided by engineer. */ +"Received reply" = "Otrzymano odpowiedź"; + +/* No comment provided by engineer. */ +"Received total" = "Otrzymano łącznie"; + /* No comment provided by engineer. */ "Receiving address will be changed to a different server. Address change will complete after sender comes online." = "Adres odbiorczy zostanie zmieniony na inny serwer. Zmiana adresu zostanie zakończona gdy nadawca będzie online."; @@ -2858,12 +3677,30 @@ /* No comment provided by engineer. */ "Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)." = "Ostania historia i ulepszony [bot adresowy](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)."; +/* No comment provided by engineer. */ +"Recipient(s) can't see who this message is from." = "Odbiorca/y nie mogą zobaczyć od kogo jest ta wiadomość."; + /* No comment provided by engineer. */ "Recipients see updates as you type them." = "Odbiorcy widzą aktualizacje podczas ich wpisywania."; +/* No comment provided by engineer. */ +"Reconnect" = "Połącz ponownie"; + /* No comment provided by engineer. */ "Reconnect all connected servers to force message delivery. It uses additional traffic." = "Połącz ponownie wszystkie połączone serwery, aby wymusić dostarczanie wiadomości. Wykorzystuje dodatkowy ruch."; +/* No comment provided by engineer. */ +"Reconnect all servers" = "Połącz ponownie wszystkie serwery"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "Połączyć ponownie wszystkie serwery?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "Ponownie połącz z serwerem w celu wymuszenia dostarczenia wiadomości. Wykorzystuje to dodatkowy ruch."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "Połączyć ponownie serwer?"; + /* No comment provided by engineer. */ "Reconnect servers?" = "Ponownie połączyć serwery?"; @@ -2876,7 +3713,8 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "Zmniejszone zużycie baterii"; -/* reject incoming call via notification */ +/* reject incoming call via notification +swipe action */ "Reject" = "Odrzuć"; /* No comment provided by engineer. */ @@ -2897,6 +3735,12 @@ /* No comment provided by engineer. */ "Remove" = "Usuń"; +/* No comment provided by engineer. */ +"Remove archive?" = "Usunąć archiwum?"; + +/* No comment provided by engineer. */ +"Remove image" = "Usuń obraz"; + /* No comment provided by engineer. */ "Remove member" = "Usuń członka"; @@ -2933,9 +3777,18 @@ /* No comment provided by engineer. */ "Repeat connection request?" = "Powtórzyć prośbę połączenia?"; +/* No comment provided by engineer. */ +"Repeat download" = "Powtórz pobieranie"; + +/* No comment provided by engineer. */ +"Repeat import" = "Powtórz importowanie"; + /* No comment provided by engineer. */ "Repeat join request?" = "Powtórzyć prośbę dołączenia?"; +/* No comment provided by engineer. */ +"Repeat upload" = "Powtórz wgrywanie"; + /* chat item action */ "Reply" = "Odpowiedz"; @@ -2945,12 +3798,27 @@ /* No comment provided by engineer. */ "Reset" = "Resetuj"; +/* No comment provided by engineer. */ +"Reset all hints" = "Zresetuj wszystkie wskazówki"; + +/* No comment provided by engineer. */ +"Reset all statistics" = "Resetuj wszystkie statystyki"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "Zresetować wszystkie statystyki?"; + /* No comment provided by engineer. */ "Reset colors" = "Resetuj kolory"; +/* No comment provided by engineer. */ +"Reset to app theme" = "Zresetuj do motywu aplikacji"; + /* No comment provided by engineer. */ "Reset to defaults" = "Przywróć wartości domyślne"; +/* No comment provided by engineer. */ +"Reset to user theme" = "Zresetuj do motywu użytkownika"; + /* No comment provided by engineer. */ "Restart the app to create a new chat profile" = "Uruchom ponownie aplikację, aby utworzyć nowy profil czatu"; @@ -2975,9 +3843,6 @@ /* chat item action */ "Reveal" = "Ujawnij"; -/* No comment provided by engineer. */ -"Revert" = "Przywrócić"; - /* No comment provided by engineer. */ "Revoke" = "Odwołaj"; @@ -2993,27 +3858,31 @@ /* No comment provided by engineer. */ "Run chat" = "Uruchom czat"; -/* chat item action */ +/* No comment provided by engineer. */ +"Safely receive files" = "Bezpiecznie otrzymuj pliki"; + +/* No comment provided by engineer. */ +"Safer groups" = "Bezpieczniejsze grupy"; + +/* alert button +chat item action */ "Save" = "Zapisz"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Zapisz (i powiadom kontakty)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "Zapisz i powiadom kontakt"; /* No comment provided by engineer. */ "Save and notify group members" = "Zapisz i powiadom członków grupy"; +/* No comment provided by engineer. */ +"Save and reconnect" = "Zapisz i połącz ponownie"; + /* No comment provided by engineer. */ "Save and update group profile" = "Zapisz i zaktualizuj profil grupowy"; -/* No comment provided by engineer. */ -"Save archive" = "Zapisz archiwum"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Zapisz ustawienia automatycznej akceptacji"; - /* No comment provided by engineer. */ "Save group profile" = "Zapisz profil grupy"; @@ -3023,7 +3892,7 @@ /* No comment provided by engineer. */ "Save passphrase in Keychain" = "Zapisz hasło w pęku kluczy"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "Zapisać preferencje?"; /* No comment provided by engineer. */ @@ -3032,14 +3901,26 @@ /* No comment provided by engineer. */ "Save servers" = "Zapisz serwery"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "Zapisać serwery?"; /* No comment provided by engineer. */ -"Save settings?" = "Zapisać ustawienia?"; +"Save welcome message?" = "Zapisać wiadomość powitalną?"; + +/* alert title */ +"Save your profile?" = "Zapisać Twój profil?"; /* No comment provided by engineer. */ -"Save welcome message?" = "Zapisać wiadomość powitalną?"; +"saved" = "zapisane"; + +/* No comment provided by engineer. */ +"Saved" = "Zapisane"; + +/* No comment provided by engineer. */ +"Saved from" = "Zapisane od"; + +/* No comment provided by engineer. */ +"saved from %@" = "zapisane od %@"; /* message info title */ "Saved message" = "Zachowano wiadomość"; @@ -3047,6 +3928,15 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Zapisane serwery WebRTC ICE zostaną usunięte"; +/* No comment provided by engineer. */ +"Saving %lld messages" = "Zapisywanie %lld wiadomości"; + +/* No comment provided by engineer. */ +"Scale" = "Skaluj"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Skanuj / Wklej link"; + /* No comment provided by engineer. */ "Scan code" = "Zeskanuj kod"; @@ -3062,6 +3952,9 @@ /* No comment provided by engineer. */ "Scan server QR code" = "Zeskanuj kod QR serwera"; +/* No comment provided by engineer. */ +"search" = "szukaj"; + /* No comment provided by engineer. */ "Search" = "Szukaj"; @@ -3074,6 +3967,9 @@ /* network option */ "sec" = "sek"; +/* No comment provided by engineer. */ +"Secondary" = "Drugorzędny"; + /* time unit */ "seconds" = "sekundy"; @@ -3083,6 +3979,9 @@ /* server test step */ "Secure queue" = "Bezpieczna kolejka"; +/* No comment provided by engineer. */ +"Secured" = "Zabezpieczone"; + /* No comment provided by engineer. */ "Security assessment" = "Ocena bezpieczeństwa"; @@ -3092,9 +3991,18 @@ /* chat item text */ "security code changed" = "kod bezpieczeństwa zmieniony"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "Wybierz"; +/* No comment provided by engineer. */ +"Select chat profile" = "Wybierz profil czatu"; + +/* No comment provided by engineer. */ +"Selected %lld" = "Zaznaczono %lld"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Wybrane preferencje czatu zabraniają tej wiadomości."; + /* No comment provided by engineer. */ "Self-destruct" = "Samozniszczenie"; @@ -3119,15 +4027,15 @@ /* No comment provided by engineer. */ "send direct message" = "wyślij wiadomość bezpośrednią"; -/* No comment provided by engineer. */ -"Send direct message" = "Wyślij wiadomość bezpośrednią"; - /* No comment provided by engineer. */ "Send direct message to connect" = "Wyślij wiadomość bezpośrednią aby połączyć"; /* No comment provided by engineer. */ "Send disappearing message" = "Wyślij znikającą wiadomość"; +/* No comment provided by engineer. */ +"Send errors" = "Wyślij błędy"; + /* No comment provided by engineer. */ "Send link previews" = "Wyślij podgląd linku"; @@ -3135,10 +4043,16 @@ "Send live message" = "Wyślij wiadomość na żywo"; /* No comment provided by engineer. */ -"Send notifications" = "Wyślij powiadomienia"; +"Send message to enable calls." = "Wyślij wiadomość aby włączyć połączenia."; /* No comment provided by engineer. */ -"Send notifications:" = "Wyślij powiadomienia:"; +"Send messages directly when IP address is protected and your or destination server does not support private routing." = "Wysyłaj wiadomości bezpośrednio, gdy adres IP jest chroniony i Twój lub docelowy serwer nie obsługuje prywatnego trasowania."; + +/* No comment provided by engineer. */ +"Send messages directly when your or destination server does not support private routing." = "Wysyłaj wiadomości bezpośrednio, gdy Twój lub docelowy serwer nie obsługuje prywatnego trasowania."; + +/* No comment provided by engineer. */ +"Send notifications" = "Wyślij powiadomienia"; /* No comment provided by engineer. */ "Send questions and ideas" = "Wyślij pytania i pomysły"; @@ -3152,7 +4066,7 @@ /* No comment provided by engineer. */ "Send up to 100 last messages to new members." = "Wysyłaj do 100 ostatnich wiadomości do nowych członków."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Nadawca anulował transfer pliku."; /* No comment provided by engineer. */ @@ -3188,15 +4102,45 @@ /* copied message info */ "Sent at: %@" = "Wysłano o: %@"; +/* No comment provided by engineer. */ +"Sent directly" = "Wysłano bezpośrednio"; + /* notification */ "Sent file event" = "Wyślij zdarzenie pliku"; /* message info title */ "Sent message" = "Wyślij wiadomość"; +/* No comment provided by engineer. */ +"Sent messages" = "Wysłane wiadomości"; + /* No comment provided by engineer. */ "Sent messages will be deleted after set time." = "Wysłane wiadomości zostaną usunięte po ustawionym czasie."; +/* No comment provided by engineer. */ +"Sent reply" = "Wyślij odpowiedź"; + +/* No comment provided by engineer. */ +"Sent total" = "Wysłano łącznie"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Wysłano przez proxy"; + +/* No comment provided by engineer. */ +"Server" = "Serwer"; + +/* No comment provided by engineer. */ +"Server address" = "Adres serwera"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "Adres serwera jest niekompatybilny z ustawieniami sieci: %@."; + +/* srv error text. */ +"Server address is incompatible with network settings." = "Adres serwera jest niekompatybilny z ustawieniami sieciowymi."; + +/* queue info */ +"server queue info: %@\n\nlast received msg: %@" = "Informacje kolejki serwera: %1$@\n\nostatnia otrzymana wiadomość: %2$@"; + /* server test error */ "Server requires authorization to create queues, check password" = "Serwer wymaga autoryzacji do tworzenia kolejek, sprawdź hasło"; @@ -3206,9 +4150,24 @@ /* No comment provided by engineer. */ "Server test failed!" = "Test serwera nie powiódł się!"; +/* No comment provided by engineer. */ +"Server type" = "Typ serwera"; + +/* srv error text */ +"Server version is incompatible with network settings." = "Wersja serwera jest niekompatybilna z ustawieniami sieciowymi."; + +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "Wersja serwera jest niekompatybilna z aplikacją: %@."; + /* No comment provided by engineer. */ "Servers" = "Serwery"; +/* No comment provided by engineer. */ +"Servers info" = "Informacje o serwerach"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "Statystyki serwerów zostaną zresetowane - nie można tego cofnąć!"; + /* No comment provided by engineer. */ "Session code" = "Kod sesji"; @@ -3218,6 +4177,9 @@ /* No comment provided by engineer. */ "Set contact name…" = "Ustaw nazwę kontaktu…"; +/* No comment provided by engineer. */ +"Set default theme" = "Ustaw domyślny motyw"; + /* No comment provided by engineer. */ "Set group preferences" = "Ustaw preferencje grupy"; @@ -3233,6 +4195,9 @@ /* No comment provided by engineer. */ "Set passcode" = "Ustaw pin"; +/* No comment provided by engineer. */ +"Set passphrase" = "Ustaw hasło"; + /* No comment provided by engineer. */ "Set passphrase to export" = "Ustaw hasło do eksportu"; @@ -3245,7 +4210,14 @@ /* No comment provided by engineer. */ "Settings" = "Ustawienia"; -/* chat item action */ +/* alert message */ +"Settings were changed." = "Ustawienia zostały zmienione."; + +/* No comment provided by engineer. */ +"Shape profile images" = "Kształtuj obrazy profilowe"; + +/* alert action +chat item action */ "Share" = "Udostępnij"; /* No comment provided by engineer. */ @@ -3254,18 +4226,30 @@ /* No comment provided by engineer. */ "Share address" = "Udostępnij adres"; -/* No comment provided by engineer. */ +/* alert title */ "Share address with contacts?" = "Udostępnić adres kontaktom?"; +/* No comment provided by engineer. */ +"Share from other apps." = "Udostępnij z innych aplikacji."; + /* No comment provided by engineer. */ "Share link" = "Udostępnij link"; +/* No comment provided by engineer. */ +"Share profile" = "Udostępnij profil"; + /* No comment provided by engineer. */ "Share this 1-time invite link" = "Udostępnij ten jednorazowy link"; +/* No comment provided by engineer. */ +"Share to SimpleX" = "Udostępnij do SimpleX"; + /* No comment provided by engineer. */ "Share with contacts" = "Udostępnij kontaktom"; +/* No comment provided by engineer. */ +"Show → on messages sent via private routing." = "Pokaż → na wiadomościach wysłanych przez prywatne trasowanie."; + /* No comment provided by engineer. */ "Show calls in phone history" = "Pokaż połączenia w historii telefonu"; @@ -3275,12 +4259,24 @@ /* No comment provided by engineer. */ "Show last messages" = "Pokaż ostatnie wiadomości"; +/* No comment provided by engineer. */ +"Show message status" = "Pokaż status wiadomości"; + +/* No comment provided by engineer. */ +"Show percentage" = "Pokaż procent"; + /* No comment provided by engineer. */ "Show preview" = "Pokaż podgląd"; +/* No comment provided by engineer. */ +"Show QR code" = "Pokaż kod QR"; + /* No comment provided by engineer. */ "Show:" = "Pokaż:"; +/* No comment provided by engineer. */ +"SimpleX" = "SimpleX"; + /* No comment provided by engineer. */ "SimpleX address" = "Adres SimpleX"; @@ -3299,9 +4295,15 @@ /* simplex link type */ "SimpleX group link" = "Link grupy SimpleX"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "Linki SimpleX"; +/* No comment provided by engineer. */ +"SimpleX links are prohibited." = "Linki SimpleX są zablokowane na tej grupie."; + +/* No comment provided by engineer. */ +"SimpleX links not allowed" = "Linki SimpleX są niedozwolone"; + /* No comment provided by engineer. */ "SimpleX Lock" = "Blokada SimpleX"; @@ -3320,6 +4322,9 @@ /* No comment provided by engineer. */ "Simplified incognito mode" = "Uproszczony tryb incognito"; +/* No comment provided by engineer. */ +"Size" = "Rozmiar"; + /* No comment provided by engineer. */ "Skip" = "Pomiń"; @@ -3330,14 +4335,35 @@ "Small groups (max 20)" = "Małe grupy (maks. 20)"; /* No comment provided by engineer. */ -"SMP servers" = "Serwery SMP"; +"SMP server" = "Serwer SMP"; + +/* No comment provided by engineer. */ +"SOCKS proxy" = "Proxy SOCKS"; + +/* blur media */ +"Soft" = "Łagodny"; + +/* No comment provided by engineer. */ +"Some app settings were not migrated." = "Niektóre ustawienia aplikacji nie zostały zmigrowane."; + +/* No comment provided by engineer. */ +"Some file(s) were not exported:" = "Niektóre plik(i) nie zostały wyeksportowane:"; /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "Podczas importu wystąpiły niekrytyczne błędy - więcej szczegółów można znaleźć w konsoli czatu."; +/* No comment provided by engineer. */ +"Some non-fatal errors occurred during import:" = "Podczas importu wystąpiły niekrytyczne błędy:"; + /* notification title */ "Somebody" = "Ktoś"; +/* No comment provided by engineer. */ +"Square, circle, or anything in between." = "Kwadrat, okrąg lub cokolwiek pomiędzy."; + +/* chat item text */ +"standard end-to-end encryption" = "standardowe szyfrowanie end-to-end"; + /* No comment provided by engineer. */ "Start chat" = "Rozpocznij czat"; @@ -3347,14 +4373,20 @@ /* No comment provided by engineer. */ "Start migration" = "Rozpocznij migrację"; +/* No comment provided by engineer. */ +"Starting from %@." = "Zaczynanie od %@."; + /* No comment provided by engineer. */ "starting…" = "uruchamianie…"; +/* No comment provided by engineer. */ +"Statistics" = "Statystyki"; + /* No comment provided by engineer. */ "Stop" = "Zatrzymaj"; /* No comment provided by engineer. */ -"Stop chat to enable database actions" = "Zatrzymaj czat, aby umożliwić działania na bazie danych"; +"Stop chat" = "Zatrzymaj czat"; /* No comment provided by engineer. */ "Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Zatrzymaj czat, aby wyeksportować, zaimportować lub usunąć bazę danych czatu. Podczas zatrzymania chatu nie będzie można odbierać ani wysyłać wiadomości."; @@ -3371,21 +4403,36 @@ /* No comment provided by engineer. */ "Stop sending file?" = "Przestać wysyłać plik?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "Przestań udostępniać"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "Przestać udostępniać adres?"; /* authentication reason */ "Stop SimpleX" = "Zatrzymaj SimpleX"; +/* No comment provided by engineer. */ +"Stopping chat" = "Zatrzymywanie czatu"; + /* No comment provided by engineer. */ "strike" = "strajk"; +/* blur media */ +"Strong" = "Silne"; + /* No comment provided by engineer. */ "Submit" = "Zatwierdź"; +/* No comment provided by engineer. */ +"Subscribed" = "Zasubskrybowano"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Błędy subskrypcji"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Subskrypcje zignorowane"; + /* No comment provided by engineer. */ "Support SimpleX Chat" = "Wspieraj SimpleX Chat"; @@ -3395,6 +4442,9 @@ /* No comment provided by engineer. */ "System authentication" = "Uwierzytelnianie systemu"; +/* No comment provided by engineer. */ +"Tail" = "Ogon"; + /* No comment provided by engineer. */ "Take picture" = "Zrób zdjęcie"; @@ -3420,7 +4470,7 @@ "Tap to scan" = "Dotknij, aby zeskanować"; /* No comment provided by engineer. */ -"Tap to start a new chat" = "Dotknij, aby rozpocząć nowy czat"; +"TCP connection" = "Połączenie TCP"; /* No comment provided by engineer. */ "TCP connection timeout" = "Limit czasu połączenia TCP"; @@ -3434,6 +4484,9 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* file error alert title */ +"Temporary file error" = "Tymczasowy błąd pliku"; + /* server test failure */ "Test failed at step %@." = "Test nie powiódł się na etapie %@."; @@ -3443,7 +4496,7 @@ /* No comment provided by engineer. */ "Test servers" = "Przetestuj serwery"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "Testy nie powiodły się!"; /* No comment provided by engineer. */ @@ -3456,10 +4509,10 @@ "Thanks to the users – contribute via Weblate!" = "Podziękowania dla użytkowników - wkład za pośrednictwem Weblate!"; /* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "Pierwsza platforma bez żadnych identyfikatorów użytkowników – z założenia prywatna."; +"The app can notify you when you receive messages or contact requests - please open settings to enable." = "Aplikacja może powiadamiać Cię, gdy otrzymujesz wiadomości lub prośby o kontakt — otwórz ustawienia, aby włączyć."; /* No comment provided by engineer. */ -"The app can notify you when you receive messages or contact requests - please open settings to enable." = "Aplikacja może powiadamiać Cię, gdy otrzymujesz wiadomości lub prośby o kontakt — otwórz ustawienia, aby włączyć."; +"The app will ask to confirm downloads from unknown file servers (except .onion)." = "Aplikacja zapyta o potwierdzenie pobierania od nieznanych serwerów plików (poza .onion)."; /* No comment provided by engineer. */ "The attempt to change database passphrase was not completed." = "Próba zmiany hasła bazy danych nie została zakończona."; @@ -3479,6 +4532,9 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "Szyfrowanie działa, a nowe uzgodnienie szyfrowania nie jest wymagane. Może to spowodować błędy w połączeniu!"; +/* No comment provided by engineer. */ +"The future of messaging" = "Następna generacja prywatnych wiadomości"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "Hash poprzedniej wiadomości jest inny."; @@ -3492,13 +4548,16 @@ "The message will be marked as moderated for all members." = "Wiadomość zostanie oznaczona jako moderowana dla wszystkich członków."; /* No comment provided by engineer. */ -"The next generation of private messaging" = "Następna generacja prywatnych wiadomości"; +"The messages will be deleted for all members." = "Wiadomości zostaną usunięte dla wszystkich członków."; + +/* No comment provided by engineer. */ +"The messages will be marked as moderated for all members." = "Wiadomości zostaną oznaczone jako moderowane dla wszystkich członków."; /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "Stara baza danych nie została usunięta podczas migracji, można ją usunąć."; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "Profil jest udostępniany tylko Twoim kontaktom."; +"Your profile is stored on your device and only shared with your contacts." = "Profil jest udostępniany tylko Twoim kontaktom."; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "Drugi tik, który przegapiliśmy! ✅"; @@ -3513,7 +4572,10 @@ "The text you pasted is not a SimpleX link." = "Tekst, który wkleiłeś nie jest linkiem SimpleX."; /* No comment provided by engineer. */ -"Theme" = "Motyw"; +"The uploaded database archive will be permanently removed from the servers." = "Przesłane archiwum bazy danych zostanie trwale usunięte z serwerów."; + +/* No comment provided by engineer. */ +"Themes" = "Motywy"; /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Te ustawienia dotyczą Twojego bieżącego profilu **%@**."; @@ -3530,6 +4592,12 @@ /* No comment provided by engineer. */ "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Tego działania nie można cofnąć - Twój profil, kontakty, wiadomości i pliki zostaną nieodwracalnie utracone."; +/* E2EE info chat item */ +"This chat is protected by end-to-end encryption." = "Ten czat jest chroniony przez szyfrowanie end-to-end."; + +/* E2EE info chat item */ +"This chat is protected by quantum resistant end-to-end encryption." = "Ten czat jest chroniony przez kwantowo odporne szyfrowanie end-to-end."; + /* notification title */ "this contact" = "ten kontakt"; @@ -3551,9 +4619,15 @@ /* No comment provided by engineer. */ "This is your own SimpleX address!" = "To jest twój własny adres SimpleX!"; +/* No comment provided by engineer. */ +"This link was used with another mobile device, please create a new link on the desktop." = "Ten link dostał użyty z innym urządzeniem mobilnym, proszę stworzyć nowy link na komputerze."; + /* No comment provided by engineer. */ "This setting applies to messages in your current chat profile **%@**." = "To ustawienie dotyczy wiadomości Twojego bieżącego profilu czatu **%@**."; +/* No comment provided by engineer. */ +"Title" = "Tytuł"; + /* No comment provided by engineer. */ "To ask any questions and to receive updates:" = "Aby zadać wszelkie pytania i otrzymywać aktualizacje:"; @@ -3566,15 +4640,24 @@ /* No comment provided by engineer. */ "To make a new connection" = "Aby nawiązać nowe połączenie"; -/* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Aby chronić prywatność, zamiast identyfikatorów użytkowników używanych przez wszystkie inne platformy, SimpleX ma identyfikatory dla kolejek wiadomości, oddzielne dla każdego z Twoich kontaktów."; - /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "Aby chronić strefę czasową, pliki obrazów/głosów używają UTC."; /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Aby chronić swoje informacje, włącz funkcję blokady SimpleX.\nPrzed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania."; +/* No comment provided by engineer. */ +"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Aby chronić Twój adres IP, prywatne trasowanie używa Twoich serwerów SMP, aby dostarczyć wiadomości."; + +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "Aby chronić prywatność, zamiast identyfikatorów użytkowników używanych przez wszystkie inne platformy, SimpleX ma identyfikatory dla kolejek wiadomości, oddzielne dla każdego z Twoich kontaktów."; + +/* No comment provided by engineer. */ +"To record speech please grant permission to use Microphone." = "Aby nagrać rozmowę, proszę zezwolić na użycie Mikrofonu."; + +/* No comment provided by engineer. */ +"To record video please grant permission to use Camera." = "Aby nagrać wideo, proszę zezwolić na użycie Aparatu."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Aby nagrać wiadomość głosową należy udzielić zgody na użycie Mikrofonu."; @@ -3587,12 +4670,24 @@ /* No comment provided by engineer. */ "To verify end-to-end encryption with your contact compare (or scan) the code on your devices." = "Aby zweryfikować szyfrowanie end-to-end z Twoim kontaktem porównaj (lub zeskanuj) kod na waszych urządzeniach."; +/* No comment provided by engineer. */ +"Toggle chat list:" = "Przełącz listę czatów:"; + /* No comment provided by engineer. */ "Toggle incognito when connecting." = "Przełącz incognito przy połączeniu."; +/* No comment provided by engineer. */ +"Toolbar opacity" = "Nieprzezroczystość paska narzędzi"; + +/* No comment provided by engineer. */ +"Total" = "Łącznie"; + /* No comment provided by engineer. */ "Transport isolation" = "Izolacja transportu"; +/* No comment provided by engineer. */ +"Transport sessions" = "Sesje transportowe"; + /* No comment provided by engineer. */ "Trying to connect to the server used to receive messages from this contact (error: %@)." = "Próbowanie połączenia z serwerem używanym do odbierania wiadomości od tego kontaktu (błąd: %@)."; @@ -3629,13 +4724,10 @@ /* rcv group event chat item */ "unblocked %@" = "odblokowano %@"; -/* item status description */ -"Unexpected error: %@" = "Nieoczekiwany błąd: %@"; - /* No comment provided by engineer. */ "Unexpected migration state" = "Nieoczekiwany stan migracji"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "Nie ulub."; /* No comment provided by engineer. */ @@ -3662,6 +4754,12 @@ /* No comment provided by engineer. */ "Unknown error" = "Nieznany błąd"; +/* No comment provided by engineer. */ +"unknown servers" = "nieznane przekaźniki"; + +/* alert title */ +"Unknown servers!" = "Nieznane serwery!"; + /* No comment provided by engineer. */ "unknown status" = "nieznany status"; @@ -3683,10 +4781,13 @@ /* authentication reason */ "Unlock app" = "Odblokuj aplikację"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "Wyłącz wyciszenie"; /* No comment provided by engineer. */ +"unprotected" = "niezabezpieczony"; + +/* swipe action */ "Unread" = "Nieprzeczytane"; /* No comment provided by engineer. */ @@ -3695,9 +4796,6 @@ /* No comment provided by engineer. */ "Update" = "Aktualizuj"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = "Zaktualizować ustawienie hostów .onion?"; - /* No comment provided by engineer. */ "Update database passphrase" = "Aktualizuj hasło do bazy danych"; @@ -3705,7 +4803,7 @@ "Update network settings?" = "Zaktualizować ustawienia sieci?"; /* No comment provided by engineer. */ -"Update transport isolation mode?" = "Zaktualizować tryb izolacji transportu?"; +"Update settings?" = "Zaktualizować ustawienia?"; /* rcv group event chat item */ "updated group profile" = "zaktualizowano profil grupy"; @@ -3717,14 +4815,26 @@ "Updating settings will re-connect the client to all servers." = "Aktualizacja ustawień spowoduje ponowne połączenie klienta ze wszystkimi serwerami."; /* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "Aktualizacja tych ustawień spowoduje ponowne połączenie klienta ze wszystkimi serwerami."; +"Upgrade and open chat" = "Zaktualizuj i otwórz czat"; /* No comment provided by engineer. */ -"Upgrade and open chat" = "Zaktualizuj i otwórz czat"; +"Upload errors" = "Błędy przesłania"; + +/* No comment provided by engineer. */ +"Upload failed" = "Wgrywanie nie udane"; /* server test step */ "Upload file" = "Prześlij plik"; +/* No comment provided by engineer. */ +"Uploaded" = "Przesłane"; + +/* No comment provided by engineer. */ +"Uploaded files" = "Przesłane pliki"; + +/* No comment provided by engineer. */ +"Uploading archive" = "Wgrywanie archiwum"; + /* No comment provided by engineer. */ "Use .onion hosts" = "Użyj hostów .onion"; @@ -3749,6 +4859,12 @@ /* No comment provided by engineer. */ "Use only local notifications?" = "Używać tylko lokalnych powiadomień?"; +/* No comment provided by engineer. */ +"Use private routing with unknown servers when IP address is not protected." = "Używaj prywatnego trasowania z nieznanymi serwerami, gdy adres IP nie jest chroniony."; + +/* No comment provided by engineer. */ +"Use private routing with unknown servers." = "Używaj prywatnego trasowania z nieznanymi serwerami."; + /* No comment provided by engineer. */ "Use server" = "Użyj serwera"; @@ -3756,10 +4872,19 @@ "Use SimpleX Chat servers?" = "Użyć serwerów SimpleX Chat?"; /* No comment provided by engineer. */ -"User profile" = "Profil użytkownika"; +"Use SOCKS proxy" = "Użyj proxy SOCKS"; /* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = "Używanie hostów .onion wymaga kompatybilnego dostawcy VPN."; +"Use the app while in the call." = "Używaj aplikacji podczas połączenia."; + +/* No comment provided by engineer. */ +"Use the app with one hand." = "Korzystaj z aplikacji jedną ręką."; + +/* No comment provided by engineer. */ +"User selection" = "Wybór użytkownika"; + +/* No comment provided by engineer. */ +"Username" = "Nazwa użytkownika"; /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Używanie serwerów SimpleX Chat."; @@ -3782,6 +4907,12 @@ /* No comment provided by engineer. */ "Verify connections" = "Zweryfikuj połączenia"; +/* No comment provided by engineer. */ +"Verify database passphrase" = "Zweryfikuj hasło bazy danych"; + +/* No comment provided by engineer. */ +"Verify passphrase" = "Zweryfikuj hasło"; + /* No comment provided by engineer. */ "Verify security code" = "Weryfikuj kod bezpieczeństwa"; @@ -3803,6 +4934,9 @@ /* No comment provided by engineer. */ "Via secure quantum resistant protocol." = "Dzięki bezpiecznemu protokołowi odpornego kwantowo."; +/* No comment provided by engineer. */ +"video" = "wideo"; + /* No comment provided by engineer. */ "Video call" = "Połączenie wideo"; @@ -3834,7 +4968,10 @@ "Voice messages are prohibited in this chat." = "Wiadomości głosowe są zabronione na tym czacie."; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "Wiadomości głosowe są zabronione w tej grupie."; +"Voice messages are prohibited." = "Wiadomości głosowe są zabronione w tej grupie."; + +/* No comment provided by engineer. */ +"Voice messages not allowed" = "Wiadomości głosowe są niedozwolone"; /* No comment provided by engineer. */ "Voice messages prohibited!" = "Wiadomości głosowe zabronione!"; @@ -3857,9 +4994,18 @@ /* No comment provided by engineer. */ "Waiting for video" = "Oczekiwanie na film"; +/* No comment provided by engineer. */ +"Wallpaper accent" = "Akcent tapety"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Tło tapety"; + /* No comment provided by engineer. */ "wants to connect to you!" = "chce się z Tobą połączyć!"; +/* No comment provided by engineer. */ +"Warning: starting chat on multiple devices is not supported and will cause message delivery failures" = "Ostrzeżenie: rozpoczęcie czatu na wielu urządzeniach nie jest wspierane i spowoduje niepowodzenia dostarczania wiadomości"; + /* No comment provided by engineer. */ "Warning: you may lose some data!" = "Uwaga: możesz stracić niektóre dane!"; @@ -3875,6 +5021,9 @@ /* No comment provided by engineer. */ "Welcome message" = "Wiadomość powitalna"; +/* No comment provided by engineer. */ +"Welcome message is too long" = "Wiadomość powitalna jest zbyt długa"; + /* No comment provided by engineer. */ "What's new" = "Co nowego"; @@ -3882,11 +5031,23 @@ "When available" = "Gdy dostępny"; /* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "Kiedy ludzie proszą o połączenie, możesz je zaakceptować lub odrzucić."; +"When connecting audio and video calls." = "Podczas łączenia połączeń audio i wideo."; + +/* No comment provided by engineer. */ +"when IP hidden" = "gdy IP ukryty"; /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Gdy udostępnisz komuś profil incognito, będzie on używany w grupach, do których Cię zaprosi."; +/* No comment provided by engineer. */ +"WiFi" = "WiFi"; + +/* No comment provided by engineer. */ +"Will be enabled in direct chats!" = "Zostanie włączone w czatach bezpośrednich!"; + +/* No comment provided by engineer. */ +"Wired ethernet" = "Połączenie ethernet (po kablu)"; + /* No comment provided by engineer. */ "With encrypted files and media." = "Z zaszyfrowanymi plikami i multimediami."; @@ -3896,20 +5057,35 @@ /* No comment provided by engineer. */ "With reduced battery usage." = "Ze zmniejszonym zużyciem baterii."; +/* No comment provided by engineer. */ +"Without Tor or VPN, your IP address will be visible to file servers." = "Bez Tor lub VPN, Twój adres IP będzie widoczny do serwerów plików."; + +/* alert message */ +"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Bez Tor lub VPN, Twój adres IP będzie widoczny dla tych przekaźników XFTP: %@."; + /* No comment provided by engineer. */ "Wrong database passphrase" = "Nieprawidłowe hasło bazy danych"; +/* snd error text */ +"Wrong key or unknown connection - most likely this connection is deleted." = "Zły klucz lub nieznane połączenie - najprawdopodobniej to połączenie jest usunięte."; + +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Zły klucz lub nieznany adres fragmentu pliku - najprawdopodobniej plik został usunięty."; + /* No comment provided by engineer. */ "Wrong passphrase!" = "Nieprawidłowe hasło!"; /* No comment provided by engineer. */ -"XFTP servers" = "Serwery XFTP"; +"XFTP server" = "Serwer XFTP"; /* pref value */ "yes" = "tak"; /* No comment provided by engineer. */ -"You" = "Ty"; +"you" = "Ty"; + +/* No comment provided by engineer. */ +"You **must not** use the same database on two devices." = "**Nie możesz** używać tej samej bazy na dwóch urządzeniach."; /* No comment provided by engineer. */ "You accepted connection" = "Zaakceptowałeś połączenie"; @@ -3953,6 +5129,9 @@ /* No comment provided by engineer. */ "You are invited to group" = "Jesteś zaproszony do grupy"; +/* No comment provided by engineer. */ +"You are not connected to these servers. Private routing is used to deliver messages to them." = "Nie jesteś połączony z tymi serwerami. Prywatne trasowanie jest używane do dostarczania do nich wiadomości."; + /* No comment provided by engineer. */ "you are observer" = "jesteś obserwatorem"; @@ -3962,6 +5141,9 @@ /* No comment provided by engineer. */ "You can accept calls from lock screen, without device and app authentication." = "Możesz przyjmować połączenia z ekranu blokady, bez uwierzytelniania urządzenia i aplikacji."; +/* No comment provided by engineer. */ +"You can change it in Appearance settings." = "Możesz to zmienić w ustawieniach wyglądu."; + /* No comment provided by engineer. */ "You can create it later" = "Możesz go utworzyć później"; @@ -3971,6 +5153,9 @@ /* No comment provided by engineer. */ "You can enable them later via app Privacy & Security settings." = "Możesz je włączyć później w ustawieniach Prywatności i Bezpieczeństwa aplikacji."; +/* No comment provided by engineer. */ +"You can give another try." = "Możesz spróbować ponownie."; + /* No comment provided by engineer. */ "You can hide or mute a user profile - swipe it to the right." = "Możesz ukryć lub wyciszyć profil użytkownika - przesuń palcem w prawo."; @@ -3978,7 +5163,10 @@ "You can make it visible to your SimpleX contacts via Settings." = "Możesz ustawić go jako widoczny dla swoich kontaktów SimpleX w Ustawieniach."; /* notification body */ -"You can now send messages to %@" = "Możesz teraz wysyłać wiadomości do %@"; +"You can now chat with %@" = "Możesz teraz wysyłać wiadomości do %@"; + +/* No comment provided by engineer. */ +"You can send messages to %@ from Archived contacts." = "Możesz wysyłać wiadomości do %@ ze zarchiwizowanych kontaktów."; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "Podgląd powiadomień na ekranie blokady można ustawić w ustawieniach."; @@ -3990,10 +5178,10 @@ "You can share this address with your contacts to let them connect with **%@**." = "Możesz udostępnić ten adres Twoim kontaktom, aby umożliwić im połączenie z **%@**."; /* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "Możesz udostępnić swój adres jako link lub jako kod QR - każdy będzie mógł się z Tobą połączyć."; +"You can start chat via app Settings / Database or by restarting the app" = "Możesz rozpocząć czat poprzez Ustawienia aplikacji / Baza danych lub poprzez ponowne uruchomienie aplikacji"; /* No comment provided by engineer. */ -"You can start chat via app Settings / Database or by restarting the app" = "Możesz rozpocząć czat poprzez Ustawienia aplikacji / Baza danych lub poprzez ponowne uruchomienie aplikacji"; +"You can still view conversation with %@ in the list of chats." = "Nadal możesz przeglądać rozmowę z %@ na liście czatów."; /* No comment provided by engineer. */ "You can turn on SimpleX Lock via Settings." = "Możesz włączyć blokadę SimpleX poprzez Ustawienia."; @@ -4001,7 +5189,7 @@ /* No comment provided by engineer. */ "You can use markdown to format messages:" = "Możesz używać markdown do formatowania wiadomości:"; -/* No comment provided by engineer. */ +/* alert message */ "You can view invitation link again in connection details." = "Możesz zobaczyć link zaproszenia ponownie w szczegółach połączenia."; /* No comment provided by engineer. */ @@ -4020,10 +5208,10 @@ "you changed role of %@ to %@" = "zmieniłeś rolę %1$@ na %2$@"; /* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "Kontrolujesz przez który serwer(y) **odbierać** wiadomości, Twoje kontakty - serwery, których używasz do wysyłania im wiadomości."; +"You could not be verified; please try again." = "Nie można zweryfikować użytkownika; proszę spróbować ponownie."; /* No comment provided by engineer. */ -"You could not be verified; please try again." = "Nie można zweryfikować użytkownika; proszę spróbować ponownie."; +"You decide who can connect." = "Ty decydujesz, kto może się połączyć."; /* No comment provided by engineer. */ "You have already requested connection via this address!" = "Już prosiłeś o połączenie na ten adres!"; @@ -4031,9 +5219,6 @@ /* No comment provided by engineer. */ "You have already requested connection!\nRepeat connection request?" = "Już prosiłeś o połączenie!\nPowtórzyć prośbę połączenia?"; -/* No comment provided by engineer. */ -"You have no chats" = "Nie masz czatów"; - /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "Musisz wprowadzić hasło przy każdym uruchomieniu aplikacji - nie jest one przechowywane na urządzeniu."; @@ -4049,9 +5234,18 @@ /* snd group event chat item */ "you left" = "wyszedłeś"; +/* No comment provided by engineer. */ +"You may migrate the exported database." = "Możesz zmigrować wyeksportowaną bazy danych."; + +/* No comment provided by engineer. */ +"You may save the exported archive." = "Możesz zapisać wyeksportowane archiwum."; + /* No comment provided by engineer. */ "You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." = "Musisz używać najnowszej wersji bazy danych czatu TYLKO na jednym urządzeniu, w przeciwnym razie możesz przestać otrzymywać wiadomości od niektórych kontaktów."; +/* No comment provided by engineer. */ +"You need to allow your contact to call to be able to call them." = "Aby móc dzwonić, musisz zezwolić kontaktowi na połączenia."; + /* No comment provided by engineer. */ "You need to allow your contact to send voice messages to be able to send them." = "Musisz zezwolić Twojemu kontaktowi na wysyłanie wiadomości głosowych, aby móc je wysyłać."; @@ -4109,9 +5303,6 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Używasz profilu incognito dla tej grupy - aby zapobiec udostępnianiu głównego profilu zapraszanie kontaktów jest zabronione"; -/* No comment provided by engineer. */ -"Your %@ servers" = "Twoje serwery %@"; - /* No comment provided by engineer. */ "Your calls" = "Twoje połączenia"; @@ -4121,11 +5312,14 @@ /* No comment provided by engineer. */ "Your chat database is not encrypted - set passphrase to encrypt it." = "Baza danych czatu nie jest szyfrowana - ustaw hasło, aby ją zaszyfrować."; +/* alert title */ +"Your chat preferences" = "Twoje preferencje czatu"; + /* No comment provided by engineer. */ "Your chat profiles" = "Twoje profile czatu"; /* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Twój kontakt musi być online, aby połączenie zostało zakończone.\nMożesz anulować to połączenie i usunąć kontakt (i spróbować później z nowym linkiem)."; +"Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." = "Twoje połączenie zostało przeniesione do %@, ale podczas przekierowywania do profilu wystąpił nieoczekiwany błąd."; /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "Twój kontakt wysłał plik, który jest większy niż obecnie obsługiwany maksymalny rozmiar (%@)."; @@ -4136,6 +5330,9 @@ /* No comment provided by engineer. */ "Your contacts will remain connected." = "Twoje kontakty pozostaną połączone."; +/* No comment provided by engineer. */ +"Your credentials may be sent unencrypted." = "Twoje poświadczenia mogą zostać wysłane niezaszyfrowane."; + /* No comment provided by engineer. */ "Your current chat database will be DELETED and REPLACED with the imported one." = "Twoja obecna baza danych czatu zostanie usunięta i zastąpiona zaimportowaną."; @@ -4158,7 +5355,10 @@ "Your profile **%@** will be shared." = "Twój profil **%@** zostanie udostępniony."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Twój profil jest przechowywany na urządzeniu i udostępniany tylko Twoim kontaktom.\nSerwery SimpleX nie mogą zobaczyć Twojego profilu."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Twój profil jest przechowywany na urządzeniu i udostępniany tylko Twoim kontaktom. Serwery SimpleX nie mogą zobaczyć Twojego profilu."; + +/* alert message */ +"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Twój profil został zmieniony. Jeśli go zapiszesz, zaktualizowany profil zostanie wysłany do wszystkich kontaktów."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Twój profil, kontakty i dostarczone wiadomości są przechowywane na Twoim urządzeniu."; @@ -4167,10 +5367,10 @@ "Your random profile" = "Twój losowy profil"; /* No comment provided by engineer. */ -"Your server" = "Twój serwer"; +"Your server address" = "Twój adres serwera"; /* No comment provided by engineer. */ -"Your server address" = "Twój adres serwera"; +"Your servers" = "Twoje serwery"; /* No comment provided by engineer. */ "Your settings" = "Twoje ustawienia"; @@ -4178,9 +5378,3 @@ /* No comment provided by engineer. */ "Your SimpleX address" = "Twój adres SimpleX"; -/* No comment provided by engineer. */ -"Your SMP servers" = "Twoje serwery SMP"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "Twoje serwery XFTP"; - diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings index da23c04e10..cb837836ff 100644 --- a/apps/ios/ru.lproj/Localizable.strings +++ b/apps/ios/ru.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (можно скопировать)"; @@ -31,30 +16,15 @@ /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- голосовые сообщения до 5 минут.\n- настройка времени исчезающих сообщений.\n- история редактирования."; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 цвет!"; -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - /* No comment provided by engineer. */ "(new)" = "(новое)"; /* No comment provided by engineer. */ "(this device v%@)" = "(это устройство v%@)"; -/* No comment provided by engineer. */ -")" = ")"; - /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Внести свой вклад](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -65,10 +35,7 @@ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Поставить звездочку в GitHub](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add contact**: to create a new invitation link, or connect via a link you received." = "**Добавить контакт**: создать новую ссылку-приглашение или подключиться через полученную ссылку."; - -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Добавить новый контакт**: чтобы создать одноразовый QR код или ссылку для Вашего контакта."; +"**Create 1-time link**: to create and share a new invitation link." = "**Добавить контакт**: создать и поделиться новой ссылкой-приглашением."; /* No comment provided by engineer. */ "**Create group**: to create a new group." = "**Создать группу**: создать новую группу."; @@ -80,20 +47,29 @@ "**e2e encrypted** video call" = "**e2e зашифрованный** видеозвонок"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Более конфиденциально**: проверять новые сообщения каждые 20 минут. Токен устройства будет отправлен на сервер уведомлений SimpleX Chat, но у сервера не будет информации о количестве контактов и сообщений."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Более конфиденциально**: проверять новые сообщения каждые 20 минут. Только токен устройства будет отправлен на сервер уведомлений SimpleX Chat, но у сервера не будет информации о количестве контактов и какой либо информации о сообщениях."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Самый конфиденциальный**: не использовать сервер уведомлений SimpleX Chat, проверять сообщения периодически в фоновом режиме (зависит от того насколько часто Вы используете приложение)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**Самый конфиденциальный**: не использовать сервер уведомлений SimpleX Chat. Сообщения проверяются в фоновом режиме, когда система позволяет, в зависимости от того, как часто Вы используете приложение."; + +/* No comment provided by engineer. */ +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Обратите внимание**: использование одной и той же базы данных на двух устройствах нарушит расшифровку сообщений от ваших контактов, как свойство защиты соединений."; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Внимание**: Вы не сможете восстановить или поменять пароль, если Вы его потеряете."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Рекомендовано**: токен устройства и уведомления отправляются на сервер SimpleX Chat, но сервер не получает сами сообщения, их размер или от кого они."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Рекомендовано**: токен устройства и уведомления отправляются на сервер SimpleX Chat, но сервер не получает сами сообщения, их размер или от кого они."; + +/* No comment provided by engineer. */ +"**Scan / Paste link**: to connect via a link you received." = "**Сканировать / Вставить ссылку**: чтобы соединиться через полученную ссылку."; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Внимание**: для работы мгновенных уведомлений пароль должен быть сохранен в Keychain."; +/* No comment provided by engineer. */ +"**Warning**: the archive will be removed." = "**Внимание**: архив будет удален."; + /* No comment provided by engineer. */ "*bold*" = "\\*жирный*"; @@ -136,6 +112,9 @@ /* No comment provided by engineer. */ "%@ connected" = "%@ соединен(а)"; +/* No comment provided by engineer. */ +"%@ downloaded" = "%@ загружено"; + /* notification title */ "%@ is connected!" = "Установлено соединение с %@!"; @@ -145,17 +124,26 @@ /* No comment provided by engineer. */ "%@ is verified" = "%@ подтверждён"; +/* No comment provided by engineer. */ +"%@ server" = "%@ сервер"; + /* No comment provided by engineer. */ "%@ servers" = "%@ серверы"; +/* No comment provided by engineer. */ +"%@ uploaded" = "%@ загружено"; + /* notification title */ "%@ wants to connect!" = "%@ хочет соединиться!"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + /* No comment provided by engineer. */ "%@, %@ and %lld members" = "%@, %@ и %lld членов группы"; /* No comment provided by engineer. */ -"%@, %@ and %lld other members connected" = "%@, %@ и %lld других членов соединены"; +"%@, %@ and %lld other members connected" = "установлено соединение с %@, %@ и %lld другими членами группы"; /* copied message info */ "%@:" = "%@:"; @@ -163,9 +151,24 @@ /* time interval */ "%d days" = "%d дней"; +/* forward confirmation reason */ +"%d file(s) are still being downloaded." = "%d файл(ов) загружаются."; + +/* forward confirmation reason */ +"%d file(s) failed to download." = "%d файл(ов) не удалось загрузить."; + +/* forward confirmation reason */ +"%d file(s) were deleted." = "%d файлов было удалено."; + +/* forward confirmation reason */ +"%d file(s) were not downloaded." = "%d файлов не было загружено."; + /* time interval */ "%d hours" = "%d ч."; +/* alert title */ +"%d messages not forwarded" = "%d сообщений не переслано"; + /* time interval */ "%d min" = "%d мин"; @@ -175,6 +178,9 @@ /* time interval */ "%d sec" = "%d сек"; +/* delete after time */ +"%d seconds(s)" = "%d секунд"; + /* integrity error chat item */ "%d skipped message(s)" = "%d пропущенных сообщение(й)"; @@ -197,7 +203,7 @@ "%lld group events" = "%lld событий"; /* No comment provided by engineer. */ -"%lld members" = "Членов группы: %lld"; +"%lld members" = "%lld членов"; /* No comment provided by engineer. */ "%lld messages blocked" = "%lld сообщений заблокировано"; @@ -217,9 +223,6 @@ /* No comment provided by engineer. */ "%lld new interface languages" = "%lld новых языков интерфейса"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld секунд"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld секунд"; @@ -265,7 +268,8 @@ /* No comment provided by engineer. */ "0s" = "0с"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "1 день"; /* time interval */ @@ -274,12 +278,23 @@ /* No comment provided by engineer. */ "1 minute" = "1 минута"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "1 месяц"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "1 неделю"; +/* delete after time */ +"1 year" = "1 год"; + +/* No comment provided by engineer. */ +"1-time link" = "Одноразовая ссылка"; + +/* No comment provided by engineer. */ +"1-time link can be used *with one contact only* - share in person or via any messenger." = "Одноразовая ссылка может быть использована *только с одним контактом* - поделитесь при встрече или через любой мессенджер."; + /* No comment provided by engineer. */ "5 minutes" = "5 минут"; @@ -302,7 +317,7 @@ "A separate TCP connection will be used **for each chat profile you have in the app**." = "Отдельное TCP-соединение будет использоваться **для каждого профиля чата, который Вы имеете в приложении**."; /* No comment provided by engineer. */ -"A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "Отдельное TCP-соединение (и авторизация SOCKS) будет использоваться **для каждого контакта и члена группы**.\n**Обратите внимание**: если у Вас много контактов, потребление батареи и трафика может быть значительно выше, и некоторые соединения могут не работать."; +"A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "Будет использовано отдельное TCP соединение **для каждого контакта и члена группы**.\n**Примечание**: Чем больше подключений, тем быстрее разряжается батарея и расходуется трафик, а некоторые соединения могут отваливаться."; /* No comment provided by engineer. */ "Abort" = "Прекратить"; @@ -314,10 +329,7 @@ "Abort changing address?" = "Прекратить изменение адреса?"; /* No comment provided by engineer. */ -"About SimpleX" = "О SimpleX"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "Об адресе SimpleX"; +"About operators" = "Об операторах"; /* No comment provided by engineer. */ "About SimpleX Chat" = "Информация о SimpleX Chat"; @@ -326,80 +338,154 @@ "above, then choose:" = "наверху, затем выберите:"; /* No comment provided by engineer. */ -"Accent color" = "Основной цвет"; +"Accent" = "Акцент"; /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "Принять"; +/* No comment provided by engineer. */ +"Accept conditions" = "Принять условия"; + /* No comment provided by engineer. */ "Accept connection request?" = "Принять запрос?"; /* notification body */ "Accept contact request from %@?" = "Принять запрос на соединение от %@?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "Принять инкогнито"; /* call status */ "accepted call" = "принятый звонок"; +/* No comment provided by engineer. */ +"Accepted conditions" = "Принятые условия"; + +/* chat list item title */ +"accepted invitation" = "принятое приглашение"; + +/* No comment provided by engineer. */ +"Acknowledged" = "Подтверждено"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Ошибки подтверждения"; + +/* token status text */ +"Active" = "Активный"; + +/* No comment provided by engineer. */ +"Active connections" = "Активные соединения"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Добавьте адрес в свой профиль, чтобы Ваши контакты могли поделиться им. Профиль будет отправлен Вашим контактам."; /* No comment provided by engineer. */ -"Add contact" = "Добавить контакт"; +"Add friends" = "Добавить друзей"; /* No comment provided by engineer. */ -"Add preset servers" = "Добавить серверы по умолчанию"; +"Add list" = "Добавить список"; /* No comment provided by engineer. */ "Add profile" = "Добавить профиль"; /* No comment provided by engineer. */ -"Add server…" = "Добавить сервер…"; +"Add server" = "Добавить сервер"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "Добавить серверы через QR код."; +/* No comment provided by engineer. */ +"Add team members" = "Добавить сотрудников"; + /* No comment provided by engineer. */ "Add to another device" = "Добавить на другое устройство"; +/* No comment provided by engineer. */ +"Add to list" = "Добавить в список"; + /* No comment provided by engineer. */ "Add welcome message" = "Добавить приветственное сообщение"; +/* No comment provided by engineer. */ +"Add your team members to the conversations." = "Добавьте сотрудников в разговор."; + +/* No comment provided by engineer. */ +"Added media & file servers" = "Дополнительные серверы файлов и медиа"; + +/* No comment provided by engineer. */ +"Added message servers" = "Дополнительные серверы сообщений"; + +/* No comment provided by engineer. */ +"Additional accent" = "Дополнительный акцент"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Дополнительный акцент 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Вторичный 2"; + /* No comment provided by engineer. */ "Address" = "Адрес"; /* No comment provided by engineer. */ "Address change will be aborted. Old receiving address will be used." = "Изменение адреса будет прекращено. Будет использоваться старый адрес."; +/* No comment provided by engineer. */ +"Address or 1-time link?" = "Адрес или одноразовая ссылка?"; + +/* No comment provided by engineer. */ +"Address settings" = "Настройки адреса"; + /* member role */ "admin" = "админ"; +/* feature role */ +"admins" = "админы"; + +/* No comment provided by engineer. */ +"Admins can block a member for all." = "Админы могут заблокировать члена группы."; + /* No comment provided by engineer. */ "Admins can create the links to join groups." = "Админы могут создать ссылки для вступления в группу."; /* No comment provided by engineer. */ "Advanced network settings" = "Настройки сети"; +/* No comment provided by engineer. */ +"Advanced settings" = "Настройки сети"; + /* chat item text */ "agreeing encryption for %@…" = "шифрование согласовывается для %@…"; /* chat item text */ "agreeing encryption…" = "шифрование согласовывается…"; +/* No comment provided by engineer. */ +"All" = "Все"; + /* No comment provided by engineer. */ "All app data is deleted." = "Все данные приложения будут удалены."; /* No comment provided by engineer. */ "All chats and messages will be deleted - this cannot be undone!" = "Все чаты и сообщения будут удалены - это нельзя отменить!"; +/* alert message */ +"All chats will be removed from the list %@, and the list deleted." = "Все чаты будут удалены из списка %@, и список удален."; + /* No comment provided by engineer. */ "All data is erased when it is entered." = "Все данные удаляются при его вводе."; /* No comment provided by engineer. */ -"All group members will remain connected." = "Все члены группы, которые соединились через эту ссылку, останутся в группе."; +"All data is kept private on your device." = "Все данные хранятся только на вашем устройстве."; + +/* No comment provided by engineer. */ +"All group members will remain connected." = "Все члены группы останутся соединены."; + +/* No comment provided by engineer. */ +"All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages." = "Все сообщения и файлы отправляются с **end-to-end шифрованием**, с постквантовой безопасностью в прямых разговорах."; /* No comment provided by engineer. */ "All messages will be deleted - this cannot be undone!" = "Все сообщения будут удалены - это нельзя отменить!"; @@ -410,21 +496,36 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Все новые сообщения от %@ будут скрыты!"; +/* profile dropdown */ +"All profiles" = "Все профили"; + +/* No comment provided by engineer. */ +"All reports will be archived for you." = "Все сообщения о нарушениях будут заархивированы для вас."; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Все контакты, которые соединились через этот адрес, сохранятся."; /* No comment provided by engineer. */ "All your contacts will remain connected. Profile update will be sent to your contacts." = "Все Ваши контакты сохранятся. Обновленный профиль будет отправлен Вашим контактам."; +/* No comment provided by engineer. */ +"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Все ваши контакты, разговоры и файлы будут надежно зашифрованы и загружены на выбранные XFTP серверы."; + /* No comment provided by engineer. */ "Allow" = "Разрешить"; /* No comment provided by engineer. */ "Allow calls only if your contact allows them." = "Разрешить звонки, только если их разрешает Ваш контакт."; +/* No comment provided by engineer. */ +"Allow calls?" = "Разрешить звонки?"; + /* No comment provided by engineer. */ "Allow disappearing messages only if your contact allows it to you." = "Разрешить исчезающие сообщения, только если Ваш контакт разрешает их Вам."; +/* No comment provided by engineer. */ +"Allow downgrade" = "Разрешить прямую доставку"; + /* No comment provided by engineer. */ "Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Разрешить необратимое удаление сообщений, только если Ваш контакт разрешает это Вам. (24 часа)"; @@ -435,17 +536,26 @@ "Allow message reactions." = "Разрешить реакции на сообщения."; /* No comment provided by engineer. */ -"Allow sending direct messages to members." = "Разрешить посылать прямые сообщения членам группы."; +"Allow sending direct messages to members." = "Разрешить личные сообщения членам группы."; /* No comment provided by engineer. */ "Allow sending disappearing messages." = "Разрешить посылать исчезающие сообщения."; +/* No comment provided by engineer. */ +"Allow sharing" = "Разрешить поделиться"; + /* No comment provided by engineer. */ "Allow to irreversibly delete sent messages. (24 hours)" = "Разрешить необратимо удалять отправленные сообщения. (24 часа)"; +/* No comment provided by engineer. */ +"Allow to report messsages to moderators." = "Разрешить отправлять сообщения о нарушениях модераторам."; + /* No comment provided by engineer. */ "Allow to send files and media." = "Разрешить посылать файлы и медиа."; +/* No comment provided by engineer. */ +"Allow to send SimpleX links." = "Разрешить отправлять ссылки SimpleX."; + /* No comment provided by engineer. */ "Allow to send voice messages." = "Разрешить отправлять голосовые сообщения."; @@ -482,6 +592,9 @@ /* pref value */ "always" = "всегда"; +/* No comment provided by engineer. */ +"Always use private routing." = "Всегда использовать конфиденциальную доставку."; + /* No comment provided by engineer. */ "Always use relay" = "Всегда соединяться через relay"; @@ -491,15 +604,27 @@ /* No comment provided by engineer. */ "and %lld other events" = "и %lld других событий"; +/* report reason */ +"Another reason" = "Другая причина"; + /* No comment provided by engineer. */ "Answer call" = "Принять звонок"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "Кто угодно может запустить сервер."; + /* No comment provided by engineer. */ "App build: %@" = "Сборка приложения: %@"; +/* No comment provided by engineer. */ +"App data migration" = "Миграция данных"; + /* No comment provided by engineer. */ "App encrypts new local files (except videos)." = "Приложение шифрует новые локальные файлы (кроме видео)."; +/* No comment provided by engineer. */ +"App group:" = "Группа приложения:"; + /* No comment provided by engineer. */ "App icon" = "Иконка"; @@ -509,6 +634,9 @@ /* No comment provided by engineer. */ "App passcode is replaced with self-destruct passcode." = "Код доступа в приложение будет заменен кодом самоуничтожения."; +/* No comment provided by engineer. */ +"App session" = "Сессия приложения"; + /* No comment provided by engineer. */ "App version" = "Версия приложения"; @@ -518,9 +646,51 @@ /* No comment provided by engineer. */ "Appearance" = "Интерфейс"; +/* No comment provided by engineer. */ +"Apply" = "Применить"; + +/* No comment provided by engineer. */ +"Apply to" = "Применить к"; + +/* No comment provided by engineer. */ +"Archive" = "Архивировать"; + +/* No comment provided by engineer. */ +"Archive %lld reports?" = "Архивировать %lld сообщений о нарушениях?"; + +/* No comment provided by engineer. */ +"Archive all reports?" = "Архивировать все сообщения о нарушениях?"; + +/* No comment provided by engineer. */ +"Archive and upload" = "Архивировать и загрузить"; + +/* No comment provided by engineer. */ +"Archive contacts to chat later." = "Архивируйте контакты чтобы продолжить переписку."; + +/* No comment provided by engineer. */ +"Archive report" = "Архивировать сообщение о нарушении"; + +/* No comment provided by engineer. */ +"Archive report?" = "Архивировать сообщение о нарушении?"; + +/* swipe action */ +"Archive reports" = "Архивировать сообщения о нарушениях"; + +/* No comment provided by engineer. */ +"Archived contacts" = "Архивированные контакты"; + +/* No comment provided by engineer. */ +"archived report" = "заархивированное сообщение о нарушении"; + +/* No comment provided by engineer. */ +"Archiving database" = "Подготовка архива"; + /* No comment provided by engineer. */ "Attach" = "Прикрепить"; +/* No comment provided by engineer. */ +"attempts" = "попытки"; + /* No comment provided by engineer. */ "Audio & video calls" = "Аудио- и видеозвонки"; @@ -560,9 +730,15 @@ /* No comment provided by engineer. */ "Auto-accept images" = "Автоприем изображений"; +/* alert title */ +"Auto-accept settings" = "Настройки автоприема"; + /* No comment provided by engineer. */ "Back" = "Назад"; +/* No comment provided by engineer. */ +"Background" = "Фон"; + /* No comment provided by engineer. */ "Bad desktop address" = "Неверный адрес компьютера"; @@ -578,12 +754,39 @@ /* No comment provided by engineer. */ "Bad message ID" = "Ошибка ID сообщения"; +/* No comment provided by engineer. */ +"Better calls" = "Улучшенные звонки"; + /* No comment provided by engineer. */ "Better groups" = "Улучшенные группы"; +/* No comment provided by engineer. */ +"Better groups performance" = "Улучшенная производительность групп"; + +/* No comment provided by engineer. */ +"Better message dates." = "Улучшенные даты сообщений."; + /* No comment provided by engineer. */ "Better messages" = "Улучшенные сообщения"; +/* No comment provided by engineer. */ +"Better networking" = "Улучшенные сетевые функции"; + +/* No comment provided by engineer. */ +"Better notifications" = "Улучшенные уведомления"; + +/* No comment provided by engineer. */ +"Better privacy and security" = "Улучшенная конфиденциальность и безопасность"; + +/* No comment provided by engineer. */ +"Better security ✅" = "Улучшенная безопасность ✅"; + +/* No comment provided by engineer. */ +"Better user experience" = "Улучшенный интерфейс"; + +/* No comment provided by engineer. */ +"Black" = "Черная"; + /* No comment provided by engineer. */ "Block" = "Заблокировать"; @@ -591,13 +794,13 @@ "Block for all" = "Заблокировать для всех"; /* No comment provided by engineer. */ -"Block group members" = "Блокируйте членов группы"; +"Block group members" = "Заблокировать членов группы"; /* No comment provided by engineer. */ "Block member" = "Заблокировать члена группы"; /* No comment provided by engineer. */ -"Block member for all?" = "Заблокировать члена для всех?"; +"Block member for all?" = "Заблокировать для всех?"; /* No comment provided by engineer. */ "Block member?" = "Заблокировать члена группы?"; @@ -608,12 +811,19 @@ /* rcv group event chat item */ "blocked %@" = "%@ заблокирован"; -/* marked deleted chat item preview text */ +/* blocked chat item +marked deleted chat item preview text */ "blocked by admin" = "заблокировано администратором"; /* No comment provided by engineer. */ "Blocked by admin" = "Заблокирован администратором"; +/* No comment provided by engineer. */ +"Blur for better privacy." = "Размыть для конфиденциальности."; + +/* No comment provided by engineer. */ +"Blur media" = "Размытие изображений"; + /* No comment provided by engineer. */ "bold" = "жирный"; @@ -635,9 +845,24 @@ /* No comment provided by engineer. */ "Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" = "Болгарский, финский, тайский и украинский - благодаря пользователям и [Weblate] (https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"; +/* No comment provided by engineer. */ +"Business address" = "Бизнес адрес"; + +/* No comment provided by engineer. */ +"Business chats" = "Бизнес разговоры"; + +/* No comment provided by engineer. */ +"Businesses" = "Бизнесы"; + /* No comment provided by engineer. */ "By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "По профилю чата или [по соединению](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (БЕТА)."; +/* No comment provided by engineer. */ +"By using SimpleX Chat you agree to:\n- send only legal content in public groups.\n- respect other users – no spam." = "Используя SimpleX Chat, Вы согласны:\n- отправлять только законные сообщения в публичных группах.\n- уважать других пользователей – не отправлять спам."; + +/* No comment provided by engineer. */ +"call" = "звонок"; + /* No comment provided by engineer. */ "Call already ended!" = "Звонок уже завершен!"; @@ -653,9 +878,18 @@ /* No comment provided by engineer. */ "Calls" = "Звонки"; +/* No comment provided by engineer. */ +"Calls prohibited!" = "Звонки запрещены!"; + /* No comment provided by engineer. */ "Camera not available" = "Камера недоступна"; +/* No comment provided by engineer. */ +"Can't call contact" = "Не удается позвонить контакту"; + +/* No comment provided by engineer. */ +"Can't call member" = "Не удаётся позвонить члену группы"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Нельзя пригласить контакт!"; @@ -663,8 +897,15 @@ "Can't invite contacts!" = "Нельзя пригласить контакты!"; /* No comment provided by engineer. */ +"Can't message member" = "Не удаётся отправить сообщение члену группы"; + +/* alert action +alert button */ "Cancel" = "Отменить"; +/* No comment provided by engineer. */ +"Cancel migration" = "Отменить миграцию"; + /* feature offered item */ "cancelled %@" = "отменил(a) %@"; @@ -672,11 +913,26 @@ "Cannot access keychain to save database password" = "Ошибка доступа к Keychain при сохранении пароля"; /* No comment provided by engineer. */ +"Cannot forward message" = "Невозможно переслать сообщение"; + +/* alert title */ "Cannot receive file" = "Невозможно получить файл"; +/* snd error text */ +"Capacity exceeded - recipient did not receive previously sent messages." = "Превышено количество сообщений - предыдущие сообщения не доставлены."; + +/* No comment provided by engineer. */ +"Cellular" = "Мобильная сеть"; + /* No comment provided by engineer. */ "Change" = "Поменять"; +/* alert title */ +"Change automatic message deletion?" = "Измененить автоматическое удаление сообщений?"; + +/* authentication reason */ +"Change chat profiles" = "Поменять профили"; + /* No comment provided by engineer. */ "Change database passphrase?" = "Поменять пароль базы данных?"; @@ -702,7 +958,7 @@ "Change self-destruct mode" = "Изменить режим самоуничтожения"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "Изменить код самоуничтожения"; /* chat item text */ @@ -721,7 +977,16 @@ "changing address…" = "смена адреса…"; /* No comment provided by engineer. */ -"Chat archive" = "Архив чата"; +"Chat" = "Разговор"; + +/* No comment provided by engineer. */ +"Chat already exists" = "Разговор уже существует"; + +/* No comment provided by engineer. */ +"Chat already exists!" = "Разговор уже существует!"; + +/* No comment provided by engineer. */ +"Chat colors" = "Цвета чата"; /* No comment provided by engineer. */ "Chat console" = "Консоль"; @@ -732,6 +997,9 @@ /* No comment provided by engineer. */ "Chat database deleted" = "Данные чата удалены"; +/* No comment provided by engineer. */ +"Chat database exported" = "Данные чата экспортированы"; + /* No comment provided by engineer. */ "Chat database imported" = "Архив чата импортирован"; @@ -744,18 +1012,48 @@ /* No comment provided by engineer. */ "Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "Чат остановлен. Если вы уже использовали эту базу данных на другом устройстве, перенесите ее обратно до запуска чата."; +/* No comment provided by engineer. */ +"Chat list" = "Список чатов"; + +/* No comment provided by engineer. */ +"Chat migrated!" = "Чат мигрирован!"; + /* No comment provided by engineer. */ "Chat preferences" = "Предпочтения"; +/* alert message */ +"Chat preferences were changed." = "Настройки чата были изменены."; + +/* No comment provided by engineer. */ +"Chat profile" = "Профиль чата"; + +/* No comment provided by engineer. */ +"Chat theme" = "Тема чата"; + +/* No comment provided by engineer. */ +"Chat will be deleted for all members - this cannot be undone!" = "Разговор будет удален для всех участников - это действие нельзя отменить!"; + +/* No comment provided by engineer. */ +"Chat will be deleted for you - this cannot be undone!" = "Разговор будет удален для Вас - это действие нельзя отменить!"; + /* No comment provided by engineer. */ "Chats" = "Чаты"; /* No comment provided by engineer. */ +"Check messages every 20 min." = "Проверять сообщения каждые 20 минут."; + +/* No comment provided by engineer. */ +"Check messages when allowed." = "Проверять сообщения по возможности."; + +/* alert title */ "Check server address and try again." = "Проверьте адрес сервера и попробуйте снова."; /* No comment provided by engineer. */ "Chinese and Spanish interface" = "Китайский и Испанский интерфейс"; +/* No comment provided by engineer. */ +"Choose _Migrate from another device_ on the new device and scan QR code." = "Выберите _Мигрировать с другого устройства_ на новом устройстве и сосканируйте QR код."; + /* No comment provided by engineer. */ "Choose file" = "Выбрать файл"; @@ -763,6 +1061,15 @@ "Choose from library" = "Выбрать из библиотеки"; /* No comment provided by engineer. */ +"Chunks deleted" = "Блоков удалено"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "Блоков принято"; + +/* No comment provided by engineer. */ +"Chunks uploaded" = "Блоков загружено"; + +/* swipe action */ "Clear" = "Очистить"; /* No comment provided by engineer. */ @@ -771,6 +1078,12 @@ /* No comment provided by engineer. */ "Clear conversation?" = "Очистить разговор?"; +/* No comment provided by engineer. */ +"Clear group?" = "Очистить группу?"; + +/* No comment provided by engineer. */ +"Clear or delete group?" = "Очистить или удалить группу?"; + /* No comment provided by engineer. */ "Clear private notes?" = "Очистить личные заметки?"; @@ -778,10 +1091,16 @@ "Clear verification" = "Сбросить подтверждение"; /* No comment provided by engineer. */ -"colored" = "цвет"; +"Color chats with the new themes." = "Добавьте цвета к чатам в настройках."; /* No comment provided by engineer. */ -"Colors" = "Цвета"; +"Color mode" = "Режим цветов"; + +/* No comment provided by engineer. */ +"colored" = "цвет"; + +/* report reason */ +"Community guidelines violation" = "Нарушение правил группы"; /* server test step */ "Compare file" = "Сравнение файла"; @@ -792,15 +1111,51 @@ /* No comment provided by engineer. */ "complete" = "соединение завершено"; +/* No comment provided by engineer. */ +"Completed" = "Готово"; + +/* No comment provided by engineer. */ +"Conditions accepted on: %@." = "Условия приняты: %@."; + +/* No comment provided by engineer. */ +"Conditions are accepted for the operator(s): **%@**." = "Условия приняты для оператора(ов): **%@**."; + +/* No comment provided by engineer. */ +"Conditions are already accepted for these operator(s): **%@**." = "Условия уже приняты для следующих оператора(ов): **%@**."; + +/* No comment provided by engineer. */ +"Conditions of use" = "Условия использования"; + +/* No comment provided by engineer. */ +"Conditions will be accepted for the operator(s): **%@**." = "Условия будут приняты для оператора(ов): **%@**."; + +/* No comment provided by engineer. */ +"Conditions will be accepted on: %@." = "Условия будут приняты: %@."; + +/* No comment provided by engineer. */ +"Conditions will be automatically accepted for enabled operators on: %@." = "Условия будут автоматически приняты для включенных операторов: %@."; + /* No comment provided by engineer. */ "Configure ICE servers" = "Настройка ICE серверов"; +/* No comment provided by engineer. */ +"Configure server operators" = "Настроить операторов серверов"; + /* No comment provided by engineer. */ "Confirm" = "Подтвердить"; +/* No comment provided by engineer. */ +"Confirm contact deletion?" = "Потвердить удаление контакта?"; + /* No comment provided by engineer. */ "Confirm database upgrades" = "Подтвердить обновление базы данных"; +/* No comment provided by engineer. */ +"Confirm files from unknown servers." = "Подтверждать файлы с неизвестных серверов."; + +/* No comment provided by engineer. */ +"Confirm network settings" = "Подтвердите настройки сети"; + /* No comment provided by engineer. */ "Confirm new passphrase…" = "Подтвердите новый пароль…"; @@ -810,6 +1165,15 @@ /* No comment provided by engineer. */ "Confirm password" = "Подтвердить пароль"; +/* No comment provided by engineer. */ +"Confirm that you remember database passphrase to migrate it." = "Подтвердите, что Вы помните пароль базы данных для ее миграции."; + +/* No comment provided by engineer. */ +"Confirm upload" = "Подтвердить загрузку"; + +/* token status text */ +"Confirmed" = "Подтвержденный"; + /* server test step */ "Connect" = "Соединиться"; @@ -825,6 +1189,9 @@ /* No comment provided by engineer. */ "connect to SimpleX Chat developers." = "соединитесь с разработчиками."; +/* No comment provided by engineer. */ +"Connect to your friends faster." = "Соединяйтесь с друзьями быстрее."; + /* No comment provided by engineer. */ "Connect to yourself?" = "Соединиться с самим собой?"; @@ -849,18 +1216,27 @@ /* No comment provided by engineer. */ "connected" = "соединение установлено"; +/* No comment provided by engineer. */ +"Connected" = "Соединено"; + /* No comment provided by engineer. */ "Connected desktop" = "Подключенный компьютер"; /* rcv group event chat item */ "connected directly" = "соединен(а) напрямую"; +/* No comment provided by engineer. */ +"Connected servers" = "Подключенные серверы"; + /* No comment provided by engineer. */ "Connected to desktop" = "Компьютер подключен"; /* No comment provided by engineer. */ "connecting" = "соединяется"; +/* No comment provided by engineer. */ +"Connecting" = "Соединяется"; + /* No comment provided by engineer. */ "connecting (accepted)" = "соединяется (приглашение принято)"; @@ -882,15 +1258,24 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "Устанавливается соединение с сервером… (ошибка: %@)"; +/* No comment provided by engineer. */ +"Connecting to contact, please wait or check later!" = "Контакт соединяется, подождите или проверьте позже!"; + /* No comment provided by engineer. */ "Connecting to desktop" = "Подключение к компьютеру"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "соединяется…"; /* No comment provided by engineer. */ "Connection" = "Соединение"; +/* No comment provided by engineer. */ +"Connection and servers status." = "Состояние соединения и серверов."; + +/* No comment provided by engineer. */ +"Connection blocked" = "Соединение заблокировано"; + /* No comment provided by engineer. */ "Connection error" = "Ошибка соединения"; @@ -900,18 +1285,39 @@ /* chat list item title (it should not be shown */ "connection established" = "соединение установлено"; +/* No comment provided by engineer. */ +"Connection is blocked by server operator:\n%@" = "Соединение заблокировано сервером оператора:\n%@"; + +/* No comment provided by engineer. */ +"Connection not ready." = "Соединение не готово."; + +/* No comment provided by engineer. */ +"Connection notifications" = "Уведомления по соединениям"; + /* No comment provided by engineer. */ "Connection request sent!" = "Запрос на соединение отправлен!"; +/* No comment provided by engineer. */ +"Connection requires encryption renegotiation." = "Соединение требует повторного согласования шифрования."; + +/* No comment provided by engineer. */ +"Connection security" = "Безопасность соединения"; + /* No comment provided by engineer. */ "Connection terminated" = "Подключение прервано"; /* No comment provided by engineer. */ "Connection timeout" = "Превышено время соединения"; +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "Соединение с компьютером остановлено"; + /* connection information */ "connection:%@" = "connection:%@"; +/* No comment provided by engineer. */ +"Connections" = "Соединения"; + /* profile update event chat item */ "contact %@ changed to %@" = "контакт %1$@ изменён на %2$@"; @@ -921,6 +1327,9 @@ /* No comment provided by engineer. */ "Contact already exists" = "Существующий контакт"; +/* No comment provided by engineer. */ +"Contact deleted!" = "Контакт удален!"; + /* No comment provided by engineer. */ "contact has e2e encryption" = "у контакта есть e2e шифрование"; @@ -934,7 +1343,7 @@ "Contact is connected" = "Соединение с контактом установлено"; /* No comment provided by engineer. */ -"Contact is not connected yet!" = "Соединение еще не установлено!"; +"Contact is deleted." = "Контакт удален."; /* No comment provided by engineer. */ "Contact name" = "Имена контактов"; @@ -942,21 +1351,36 @@ /* No comment provided by engineer. */ "Contact preferences" = "Предпочтения контакта"; +/* No comment provided by engineer. */ +"Contact will be deleted - this cannot be undone!" = "Контакт будет удален — это нельзя отменить!"; + /* No comment provided by engineer. */ "Contacts" = "Контакты"; /* No comment provided by engineer. */ "Contacts can mark messages for deletion; you will be able to view them." = "Контакты могут помечать сообщения для удаления; Вы сможете просмотреть их."; +/* blocking reason */ +"Content violates conditions of use" = "Содержание нарушает условия использования"; + /* No comment provided by engineer. */ "Continue" = "Продолжить"; -/* chat item action */ +/* No comment provided by engineer. */ +"Conversation deleted!" = "Разговор удален!"; + +/* No comment provided by engineer. */ "Copy" = "Скопировать"; +/* No comment provided by engineer. */ +"Copy error" = "Ошибка копирования"; + /* No comment provided by engineer. */ "Core version: v%@" = "Версия ядра: v%@"; +/* No comment provided by engineer. */ +"Corner" = "Угол"; + /* No comment provided by engineer. */ "Correct name to %@?" = "Исправить имя на %@?"; @@ -964,10 +1388,10 @@ "Create" = "Создать"; /* No comment provided by engineer. */ -"Create a group using a random profile." = "Создайте группу, используя случайный профиль."; +"Create 1-time link" = "Создать одноразовую ссылку"; /* No comment provided by engineer. */ -"Create an address to let people connect with you." = "Создайте адрес, чтобы можно было соединиться с вами."; +"Create a group using a random profile." = "Создайте группу, используя случайный профиль."; /* server test step */ "Create file" = "Создание файла"; @@ -981,6 +1405,9 @@ /* No comment provided by engineer. */ "Create link" = "Создать ссылку"; +/* No comment provided by engineer. */ +"Create list" = "Создать список"; + /* No comment provided by engineer. */ "Create new profile in [desktop app](https://simplex.chat/downloads/). 💻" = "Создайте новый профиль в [приложении для компьютера](https://simplex.chat/downloads/). 💻"; @@ -999,6 +1426,9 @@ /* No comment provided by engineer. */ "Create your profile" = "Создать профиль"; +/* No comment provided by engineer. */ +"Created" = "Создано"; + /* No comment provided by engineer. */ "Created at" = "Создано"; @@ -1006,7 +1436,7 @@ "Created at: %@" = "Создано: %@"; /* No comment provided by engineer. */ -"Created on %@" = "Дата создания %@"; +"Creating archive link" = "Создание ссылки на архив"; /* No comment provided by engineer. */ "Creating link…" = "Создаётся ссылка…"; @@ -1014,12 +1444,18 @@ /* No comment provided by engineer. */ "creator" = "создатель"; +/* No comment provided by engineer. */ +"Current conditions text couldn't be loaded, you can review conditions via this link:" = "Текст условий использования не может быть показан, вы можете посмотреть их через ссылку:"; + /* No comment provided by engineer. */ "Current Passcode" = "Текущий Код"; /* No comment provided by engineer. */ "Current passphrase…" = "Текущий пароль…"; +/* No comment provided by engineer. */ +"Current profile" = "Текущий профиль"; + /* No comment provided by engineer. */ "Currently maximum supported file size is %@." = "Максимальный размер файла - %@."; @@ -1029,9 +1465,18 @@ /* No comment provided by engineer. */ "Custom time" = "Пользовательское время"; +/* No comment provided by engineer. */ +"Customizable message shape." = "Настраиваемая форма сообщений."; + +/* No comment provided by engineer. */ +"Customize theme" = "Настроить тему"; + /* No comment provided by engineer. */ "Dark" = "Тёмная"; +/* No comment provided by engineer. */ +"Dark mode colors" = "Цвета тёмного режима"; + /* No comment provided by engineer. */ "Database downgrade" = "Откат базы данных"; @@ -1092,13 +1537,20 @@ /* time unit */ "days" = "дней"; +/* No comment provided by engineer. */ +"Debug delivery" = "Отладка доставки"; + /* No comment provided by engineer. */ "Decentralized" = "Децентрализованный"; /* message decrypt error item */ "Decryption error" = "Ошибка расшифровки"; -/* pref value */ +/* No comment provided by engineer. */ +"decryption errors" = "ошибки расшифровки"; + +/* delete after time +pref value */ "default (%@)" = "по умолчанию (%@)"; /* No comment provided by engineer. */ @@ -1107,9 +1559,13 @@ /* No comment provided by engineer. */ "default (yes)" = "по умолчанию (да)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "Удалить"; +/* No comment provided by engineer. */ +"Delete %lld messages of members?" = "Удалить %lld сообщений членов группы?"; + /* No comment provided by engineer. */ "Delete %lld messages?" = "Удалить %lld сообщений?"; @@ -1129,10 +1585,10 @@ "Delete and notify contact" = "Удалить и уведомить контакт"; /* No comment provided by engineer. */ -"Delete archive" = "Удалить архив"; +"Delete chat" = "Удалить разговор"; /* No comment provided by engineer. */ -"Delete chat archive?" = "Удалить архив чата?"; +"Delete chat messages from your device." = "Удалить сообщения с вашего устройства."; /* No comment provided by engineer. */ "Delete chat profile" = "Удалить профиль чата"; @@ -1140,6 +1596,9 @@ /* No comment provided by engineer. */ "Delete chat profile?" = "Удалить профиль?"; +/* No comment provided by engineer. */ +"Delete chat?" = "Удалить разговор?"; + /* No comment provided by engineer. */ "Delete connection" = "Удалить соединение"; @@ -1147,14 +1606,14 @@ "Delete contact" = "Удалить контакт"; /* No comment provided by engineer. */ -"Delete Contact" = "Удалить контакт"; - -/* No comment provided by engineer. */ -"Delete contact?\nThis cannot be undone!" = "Удалить контакт?\nЭто не может быть отменено!"; +"Delete contact?" = "Удалить контакт?"; /* No comment provided by engineer. */ "Delete database" = "Удалить данные чата"; +/* No comment provided by engineer. */ +"Delete database from this device" = "Удалить базу данных с этого устройства"; + /* server test step */ "Delete file" = "Удалить файл"; @@ -1185,13 +1644,16 @@ /* No comment provided by engineer. */ "Delete link?" = "Удалить ссылку?"; +/* alert title */ +"Delete list?" = "Удалить список?"; + /* No comment provided by engineer. */ "Delete member message?" = "Удалить сообщение участника?"; /* No comment provided by engineer. */ "Delete message?" = "Удалить сообщение?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "Удалить сообщения"; /* No comment provided by engineer. */ @@ -1204,7 +1666,7 @@ "Delete old database?" = "Удалить предыдущую версию данных?"; /* No comment provided by engineer. */ -"Delete pending connection" = "Удалить соединение"; +"Delete or moderate up to 200 messages." = "Удаляйте или модерируйте до 200 сообщений."; /* No comment provided by engineer. */ "Delete pending connection?" = "Удалить ожидаемое соединение?"; @@ -1215,12 +1677,24 @@ /* server test step */ "Delete queue" = "Удаление очереди"; +/* No comment provided by engineer. */ +"Delete report" = "Удалить сообщение о нарушении"; + +/* No comment provided by engineer. */ +"Delete up to 20 messages at once." = "Удаляйте до 20 сообщений за раз."; + /* No comment provided by engineer. */ "Delete user profile?" = "Удалить профиль пользователя?"; +/* No comment provided by engineer. */ +"Delete without notification" = "Удалить без уведомления"; + /* deleted chat item */ "deleted" = "удалено"; +/* No comment provided by engineer. */ +"Deleted" = "Удалено"; + /* No comment provided by engineer. */ "Deleted at" = "Удалено"; @@ -1233,6 +1707,12 @@ /* rcv group event chat item */ "deleted group" = "удалил(а) группу"; +/* No comment provided by engineer. */ +"Deletion errors" = "Ошибки удаления"; + +/* No comment provided by engineer. */ +"Delivered even when Apple drops them." = "Доставляются даже тогда, когда Apple их теряет."; + /* No comment provided by engineer. */ "Delivery" = "Доставка"; @@ -1254,9 +1734,27 @@ /* No comment provided by engineer. */ "Desktop devices" = "Компьютеры"; +/* No comment provided by engineer. */ +"Destination server address of %@ is incompatible with forwarding server %@ settings." = "Адрес сервера назначения %@ несовместим с настройками пересылающего сервера %@."; + +/* snd error text */ +"Destination server error: %@" = "Ошибка сервера получателя: %@"; + +/* No comment provided by engineer. */ +"Destination server version of %@ is incompatible with forwarding server %@." = "Версия сервера назначения %@ несовместима с пересылающим сервером %@."; + +/* No comment provided by engineer. */ +"Detailed statistics" = "Подробная статистика"; + +/* No comment provided by engineer. */ +"Details" = "Подробности"; + /* No comment provided by engineer. */ "Develop" = "Для разработчиков"; +/* No comment provided by engineer. */ +"Developer options" = "Опции разработчика"; + /* No comment provided by engineer. */ "Developer tools" = "Инструменты разработчика"; @@ -1282,11 +1780,17 @@ "Direct messages" = "Прямые сообщения"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "Прямые сообщения между членами группы запрещены."; +"Direct messages between members are prohibited in this chat." = "Личные сообщения запрещены в этой группе."; /* No comment provided by engineer. */ "Disable (keep overrides)" = "Выключить (кроме исключений)"; +/* alert title */ +"Disable automatic message deletion?" = "Отключить автоматическое удаление сообщений?"; + +/* alert button */ +"Disable delete messages" = "Отключить удаление сообщений"; + /* No comment provided by engineer. */ "Disable for all" = "Выключить для всех"; @@ -1296,6 +1800,9 @@ /* No comment provided by engineer. */ "disabled" = "выключено"; +/* No comment provided by engineer. */ +"Disabled" = "Выключено"; + /* No comment provided by engineer. */ "Disappearing message" = "Исчезающее сообщение"; @@ -1306,7 +1813,7 @@ "Disappearing messages are prohibited in this chat." = "Исчезающие сообщения запрещены в этом чате."; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "Исчезающие сообщения запрещены в этой группе."; +"Disappearing messages are prohibited." = "Исчезающие сообщения запрещены в этой группе."; /* No comment provided by engineer. */ "Disappears at" = "Исчезает"; @@ -1330,38 +1837,84 @@ "Do it later" = "Отложить"; /* No comment provided by engineer. */ -"Do not send history to new members." = "Не отправлять историю новым членам."; +"Do NOT send messages directly, even if your or destination server does not support private routing." = "Не отправлять сообщения напрямую, даже если сервер получателя не поддерживает конфиденциальную доставку."; + +/* No comment provided by engineer. */ +"Do not use credentials with proxy." = "Не использовать учетные данные с прокси."; + +/* No comment provided by engineer. */ +"Do NOT use private routing." = "Не использовать конфиденциальную доставку."; /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "Не используйте SimpleX для экстренных звонков."; +/* No comment provided by engineer. */ +"Documents:" = "Документы:"; + /* No comment provided by engineer. */ "Don't create address" = "Не создавать адрес"; /* No comment provided by engineer. */ "Don't enable" = "Не включать"; +/* No comment provided by engineer. */ +"Don't miss important messages." = "Не пропустите важные сообщения."; + /* No comment provided by engineer. */ "Don't show again" = "Не показывать"; +/* No comment provided by engineer. */ +"Done" = "Готово"; + /* No comment provided by engineer. */ "Downgrade and open chat" = "Откатить версию и открыть чат"; +/* alert button +chat item action */ +"Download" = "Загрузить"; + +/* No comment provided by engineer. */ +"Download errors" = "Ошибки приема"; + +/* No comment provided by engineer. */ +"Download failed" = "Ошибка загрузки"; + /* server test step */ "Download file" = "Загрузка файла"; +/* alert action */ +"Download files" = "Загрузить файлы"; + +/* No comment provided by engineer. */ +"Downloaded" = "Принято"; + +/* No comment provided by engineer. */ +"Downloaded files" = "Принятые файлы"; + +/* No comment provided by engineer. */ +"Downloading archive" = "Загрузка архива"; + +/* No comment provided by engineer. */ +"Downloading link details" = "Загрузка ссылки архива"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Имя профиля уже используется!"; /* integrity error chat item */ "duplicate message" = "повторное сообщение"; +/* No comment provided by engineer. */ +"duplicates" = "дубликаты"; + /* No comment provided by engineer. */ "Duration" = "Длительность"; /* No comment provided by engineer. */ "e2e encrypted" = "e2e зашифровано"; +/* No comment provided by engineer. */ +"E2E encrypted notifications." = "E2E зашифрованные нотификации."; + /* chat item action */ "Edit" = "Редактировать"; @@ -1374,7 +1927,7 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "Включить (кроме исключений)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "Включить автоматическое удаление сообщений?"; /* No comment provided by engineer. */ @@ -1383,6 +1936,9 @@ /* No comment provided by engineer. */ "Enable for all" = "Включить для всех"; +/* No comment provided by engineer. */ +"Enable in direct chats (BETA)!" = "Включите для контактов (BETA)!"; + /* No comment provided by engineer. */ "Enable instant notifications?" = "Включить мгновенные уведомления?"; @@ -1410,6 +1966,12 @@ /* enabled status */ "enabled" = "включено"; +/* No comment provided by engineer. */ +"Enabled" = "Включено"; + +/* No comment provided by engineer. */ +"Enabled for" = "Включено для"; + /* enabled status */ "enabled for contact" = "включено для контакта"; @@ -1482,6 +2044,9 @@ /* chat item text */ "encryption re-negotiation required for %@" = "требуется новое соглашение о шифровании для %@"; +/* No comment provided by engineer. */ +"Encryption renegotiation in progress." = "Выполняется повторное согласование шифрования."; + /* No comment provided by engineer. */ "ended" = "завершён"; @@ -1497,6 +2062,9 @@ /* No comment provided by engineer. */ "Enter Passcode" = "Введите Код"; +/* No comment provided by engineer. */ +"Enter passphrase" = "Введите пароль"; + /* No comment provided by engineer. */ "Enter passphrase…" = "Введите пароль…"; @@ -1527,24 +2095,36 @@ /* No comment provided by engineer. */ "Error aborting address change" = "Ошибка при прекращении изменения адреса"; +/* alert title */ +"Error accepting conditions" = "Ошибка приема условий"; + /* No comment provided by engineer. */ "Error accepting contact request" = "Ошибка при принятии запроса на соединение"; -/* No comment provided by engineer. */ -"Error accessing database file" = "Ошибка при доступе к данным чата"; - -/* No comment provided by engineer. */ -"Error adding member(s)" = "Ошибка при добавлении членов группы"; +/* alert title */ +"Error adding server" = "Ошибка добавления сервера"; /* No comment provided by engineer. */ "Error changing address" = "Ошибка при изменении адреса"; +/* No comment provided by engineer. */ +"Error changing connection profile" = "Ошибка при изменении профиля соединения"; + /* No comment provided by engineer. */ "Error changing role" = "Ошибка при изменении роли"; /* No comment provided by engineer. */ "Error changing setting" = "Ошибка при изменении настройки"; +/* No comment provided by engineer. */ +"Error changing to incognito!" = "Ошибка при смене на Инкогнито!"; + +/* No comment provided by engineer. */ +"Error checking token status" = "Ошибка проверки статуса токена"; + +/* No comment provided by engineer. */ +"Error connecting to forwarding server %@. Please try later." = "Ошибка подключения к пересылающему серверу %@. Попробуйте позже."; + /* No comment provided by engineer. */ "Error creating address" = "Ошибка при создании адреса"; @@ -1554,8 +2134,8 @@ /* No comment provided by engineer. */ "Error creating group link" = "Ошибка при создании ссылки группы"; -/* No comment provided by engineer. */ -"Error creating member contact" = "Ошибка создания контакта с членом группы"; +/* alert title */ +"Error creating list" = "Ошибка создания списка"; /* No comment provided by engineer. */ "Error creating message" = "Ошибка создания сообщения"; @@ -1563,6 +2143,9 @@ /* No comment provided by engineer. */ "Error creating profile!" = "Ошибка создания профиля!"; +/* No comment provided by engineer. */ +"Error creating report" = "Ошибка создания сообщения о нарушении"; + /* No comment provided by engineer. */ "Error decrypting file" = "Ошибка расшифровки файла"; @@ -1575,9 +2158,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "Ошибка при удалении соединения"; -/* No comment provided by engineer. */ -"Error deleting contact" = "Ошибка при удалении контакта"; - /* No comment provided by engineer. */ "Error deleting database" = "Ошибка при удалении данных чата"; @@ -1590,6 +2170,9 @@ /* No comment provided by engineer. */ "Error deleting user profile" = "Ошибка удаления профиля пользователя"; +/* No comment provided by engineer. */ +"Error downloading the archive" = "Ошибка загрузки архива"; + /* No comment provided by engineer. */ "Error enabling delivery receipts!" = "Ошибка при включении отчётов о доставке!"; @@ -1602,26 +2185,44 @@ /* No comment provided by engineer. */ "Error exporting chat database" = "Ошибка при экспорте архива чата"; +/* No comment provided by engineer. */ +"Error exporting theme: %@" = "Ошибка экспорта темы: %@"; + /* No comment provided by engineer. */ "Error importing chat database" = "Ошибка при импорте архива чата"; /* No comment provided by engineer. */ "Error joining group" = "Ошибка при вступлении в группу"; +/* alert title */ +"Error loading servers" = "Ошибка загрузки серверов"; + /* No comment provided by engineer. */ -"Error loading %@ servers" = "Ошибка загрузки %@ серверов"; +"Error migrating settings" = "Ошибка миграции настроек"; /* No comment provided by engineer. */ "Error opening chat" = "Ошибка доступа к чату"; -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Ошибка при получении файла"; /* No comment provided by engineer. */ -"Error removing member" = "Ошибка при удалении члена группы"; +"Error reconnecting server" = "Ошибка переподключения к серверу"; /* No comment provided by engineer. */ -"Error saving %@ servers" = "Ошибка при сохранении %@ серверов"; +"Error reconnecting servers" = "Ошибка переподключения к серверам"; + +/* alert title */ +"Error registering for notifications" = "Ошибка регистрации для уведомлений"; + +/* alert title */ +"Error reordering lists" = "Ошибка сортировки списков"; + +/* No comment provided by engineer. */ +"Error resetting statistics" = "Ошибка сброса статистики"; + +/* alert title */ +"Error saving chat list" = "Ошибка сохранения списка чатов"; /* No comment provided by engineer. */ "Error saving group profile" = "Ошибка при сохранении профиля группы"; @@ -1635,6 +2236,12 @@ /* No comment provided by engineer. */ "Error saving passphrase to keychain" = "Ошибка сохранения пароля в Keychain"; +/* alert title */ +"Error saving servers" = "Ошибка сохранения серверов"; + +/* when migrating */ +"Error saving settings" = "Ошибка сохранения настроек"; + /* No comment provided by engineer. */ "Error saving user password" = "Ошибка при сохранении пароля пользователя"; @@ -1644,9 +2251,6 @@ /* No comment provided by engineer. */ "Error sending email" = "Ошибка отправки email"; -/* No comment provided by engineer. */ -"Error sending member contact invitation" = "Ошибка отправки приглашения члену группы"; - /* No comment provided by engineer. */ "Error sending message" = "Ошибка при отправке сообщения"; @@ -1660,17 +2264,26 @@ "Error stopping chat" = "Ошибка при остановке чата"; /* No comment provided by engineer. */ +"Error switching profile" = "Ошибка переключения профиля"; + +/* alertTitle */ "Error switching profile!" = "Ошибка выбора профиля!"; /* No comment provided by engineer. */ "Error synchronizing connection" = "Ошибка синхронизации соединения"; +/* No comment provided by engineer. */ +"Error testing server connection" = "Ошибка проверки соединения с сервером"; + /* No comment provided by engineer. */ "Error updating group link" = "Ошибка обновления ссылки группы"; /* No comment provided by engineer. */ "Error updating message" = "Ошибка при обновлении сообщения"; +/* alert title */ +"Error updating server" = "Ошибка сохранения сервера"; + /* No comment provided by engineer. */ "Error updating settings" = "Ошибка при сохранении настроек сети"; @@ -1678,9 +2291,17 @@ "Error updating user privacy" = "Ошибка при обновлении конфиденциальности"; /* No comment provided by engineer. */ -"Error: " = "Ошибка: "; +"Error uploading the archive" = "Ошибка загрузки архива"; /* No comment provided by engineer. */ +"Error verifying passphrase:" = "Ошибка подтверждения пароля:"; + +/* No comment provided by engineer. */ +"Error: " = "Ошибка: "; + +/* alert message +file error text +snd error text */ "Error: %@" = "Ошибка: %@"; /* No comment provided by engineer. */ @@ -1690,10 +2311,13 @@ "Error: URL is invalid" = "Ошибка: неверная ссылка"; /* No comment provided by engineer. */ -"Even when disabled in the conversation." = "Даже когда они выключены в разговоре."; +"Errors" = "Ошибки"; + +/* servers error */ +"Errors in servers configuration." = "Ошибки в настройках серверов."; /* No comment provided by engineer. */ -"event happened" = "событие произошло"; +"Even when disabled in the conversation." = "Даже когда они выключены в разговоре."; /* No comment provided by engineer. */ "Exit without saving" = "Выйти без сохранения"; @@ -1701,15 +2325,27 @@ /* chat item action */ "Expand" = "Раскрыть"; +/* No comment provided by engineer. */ +"expired" = "истекло"; + +/* token status text */ +"Expired" = "Истекший"; + /* No comment provided by engineer. */ "Export database" = "Экспорт архива чата"; /* No comment provided by engineer. */ "Export error:" = "Ошибка при экспорте:"; +/* No comment provided by engineer. */ +"Export theme" = "Экспорт темы"; + /* No comment provided by engineer. */ "Exported database archive." = "Архив чата экспортирован."; +/* No comment provided by engineer. */ +"Exported file doesn't exist" = "Экспортированный файл не существует"; + /* No comment provided by engineer. */ "Exporting database archive…" = "Архив чата экспортируется…"; @@ -1719,12 +2355,42 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "Быстрые и не нужно ждать, когда отправитель онлайн!"; +/* No comment provided by engineer. */ +"Faster deletion of groups." = "Ускорено удаление групп."; + /* No comment provided by engineer. */ "Faster joining and more reliable messages." = "Быстрое вступление и надежная доставка сообщений."; /* No comment provided by engineer. */ +"Faster sending messages." = "Ускорена отправка сообщений."; + +/* swipe action */ "Favorite" = "Избранный"; +/* No comment provided by engineer. */ +"Favorites" = "Избранное"; + +/* file error alert title */ +"File error" = "Ошибка файла"; + +/* alert message */ +"File errors:\n%@" = "Ошибки файлов:\n%@"; + +/* file error text */ +"File is blocked by server operator:\n%@." = "Файл заблокирован оператором сервера:\n%@."; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "Файл не найден - скорее всего, файл был удален или отменен."; + +/* file error text */ +"File server error: %@" = "Ошибка сервера файлов: %@"; + +/* No comment provided by engineer. */ +"File status" = "Статус файла"; + +/* copied message info */ +"File status: %@" = "Статус файла: %@"; + /* No comment provided by engineer. */ "File will be deleted from servers." = "Файл будет удалён с серверов."; @@ -1737,6 +2403,9 @@ /* No comment provided by engineer. */ "File: %@" = "Файл: %@"; +/* No comment provided by engineer. */ +"Files" = "Файлы"; + /* No comment provided by engineer. */ "Files & media" = "Файлы и медиа"; @@ -1744,7 +2413,10 @@ "Files and media" = "Файлы и медиа"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "Файлы и медиа запрещены в этой группе."; +"Files and media are prohibited." = "Файлы и медиа запрещены в этой группе."; + +/* No comment provided by engineer. */ +"Files and media not allowed" = "Файлы и медиа не разрешены"; /* No comment provided by engineer. */ "Files and media prohibited!" = "Файлы и медиа запрещены!"; @@ -1752,6 +2424,12 @@ /* No comment provided by engineer. */ "Filter unread and favorite chats." = "Фильтровать непрочитанные и избранные чаты."; +/* No comment provided by engineer. */ +"Finalize migration" = "Завершить миграцию"; + +/* No comment provided by engineer. */ +"Finalize migration on another device." = "Завершите миграцию на другом устройстве."; + /* No comment provided by engineer. */ "Finally, we have them! 🚀" = "Наконец-то, мы их добавили! 🚀"; @@ -1774,11 +2452,71 @@ "Fix not supported by contact" = "Починка не поддерживается контактом"; /* No comment provided by engineer. */ -"Fix not supported by group member" = "Починка не поддерживается членом группы"; +"For all moderators" = "Для всех модераторов"; + +/* servers error */ +"For chat profile %@:" = "Для профиля чата %@:"; /* No comment provided by engineer. */ "For console" = "Для консоли"; +/* No comment provided by engineer. */ +"For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server." = "Например, если Ваш контакт получает сообщения через сервер SimpleX Chat, Ваше приложение доставит их через сервер Flux."; + +/* No comment provided by engineer. */ +"For me" = "Для меня"; + +/* No comment provided by engineer. */ +"For private routing" = "Для доставки сообщений"; + +/* No comment provided by engineer. */ +"For social media" = "Для социальных сетей"; + +/* chat item action */ +"Forward" = "Переслать"; + +/* alert title */ +"Forward %d message(s)?" = "Переслать %d сообщение(й)?"; + +/* No comment provided by engineer. */ +"Forward and save messages" = "Переслать и сохранить сообщение"; + +/* alert action */ +"Forward messages" = "Переслать сообщения"; + +/* alert message */ +"Forward messages without files?" = "Переслать сообщения без файлов?"; + +/* No comment provided by engineer. */ +"Forward up to 20 messages at once." = "Пересылайте до 20 сообщений за раз."; + +/* No comment provided by engineer. */ +"forwarded" = "переслано"; + +/* No comment provided by engineer. */ +"Forwarded" = "Переслано"; + +/* No comment provided by engineer. */ +"Forwarded from" = "Переслано из"; + +/* No comment provided by engineer. */ +"Forwarding %lld messages" = "Пересылка %lld сообщений"; + +/* No comment provided by engineer. */ +"Forwarding server %@ failed to connect to destination server %@. Please try later." = "Пересылающий сервер %@ не смог подключиться к серверу назначения %@. Попробуйте позже."; + +/* No comment provided by engineer. */ +"Forwarding server address is incompatible with network settings: %@." = "Адрес пересылающего сервера несовместим с настройками сети: %@."; + +/* No comment provided by engineer. */ +"Forwarding server version is incompatible with network settings: %@." = "Версия пересылающего сервера несовместима с настройками сети: %@."; + +/* snd error text */ +"Forwarding server: %@\nDestination server error: %@" = "Пересылающий сервер: %1$@\nОшибка сервера получателя: %2$@"; + +/* snd error text */ +"Forwarding server: %@\nError: %@" = "Пересылающий сервер: %1$@\nОшибка: %2$@"; + /* No comment provided by engineer. */ "Found desktop" = "Компьютер найден"; @@ -1791,21 +2529,24 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Полное имя (не обязательно)"; -/* No comment provided by engineer. */ -"Full name:" = "Полное имя:"; - -/* No comment provided by engineer. */ -"Fully decentralized – visible only to members." = "Группа полностью децентрализована – она видна только членам."; - /* No comment provided by engineer. */ "Fully re-implemented - work in background!" = "Полностью обновлены - работают в фоне!"; /* No comment provided by engineer. */ "Further reduced battery usage" = "Уменьшенное потребление батареи"; +/* No comment provided by engineer. */ +"Get notified when mentioned." = "Уведомления, когда Вас упомянули."; + /* No comment provided by engineer. */ "GIFs and stickers" = "ГИФ файлы и стикеры"; +/* message preview */ +"Good afternoon!" = "Добрый день!"; + +/* message preview */ +"Good morning!" = "Доброе утро!"; + /* No comment provided by engineer. */ "Group" = "Группа"; @@ -1825,7 +2566,7 @@ "Group full name (optional)" = "Полное имя (необязательно)"; /* No comment provided by engineer. */ -"Group image" = "Аватар группы"; +"Group image" = "Картинка группы"; /* No comment provided by engineer. */ "Group invitation" = "Приглашение в группу"; @@ -1842,24 +2583,6 @@ /* No comment provided by engineer. */ "Group links" = "Ссылки групп"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "Члены группы могут добавлять реакции на сообщения."; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "Члены группы могут необратимо удалять отправленные сообщения. (24 часа)"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "Члены группы могут посылать прямые сообщения."; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "Члены группы могут посылать исчезающие сообщения."; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "Члены группы могут слать файлы и медиа."; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "Члены группы могут отправлять голосовые сообщения."; - /* notification */ "Group message:" = "Групповое сообщение:"; @@ -1872,24 +2595,24 @@ /* No comment provided by engineer. */ "Group profile" = "Профиль группы"; -/* No comment provided by engineer. */ -"Group profile is stored on members' devices, not on the servers." = "Профиль группы хранится на устройствах членов, а не на серверах."; - /* snd group event chat item */ "group profile updated" = "профиль группы обновлен"; /* No comment provided by engineer. */ "Group welcome message" = "Приветственное сообщение группы"; -/* No comment provided by engineer. */ -"Group will be deleted for all members - this cannot be undone!" = "Группа будет удалена для всех членов - это действие нельзя отменить!"; - /* No comment provided by engineer. */ "Group will be deleted for you - this cannot be undone!" = "Группа будет удалена для Вас - это действие нельзя отменить!"; +/* No comment provided by engineer. */ +"Groups" = "Группы"; + /* No comment provided by engineer. */ "Help" = "Помощь"; +/* No comment provided by engineer. */ +"Help admins moderating their groups." = "Помогайте администраторам модерировать их группы."; + /* No comment provided by engineer. */ "Hidden" = "Скрытое"; @@ -1914,13 +2637,16 @@ /* No comment provided by engineer. */ "History" = "История"; -/* No comment provided by engineer. */ -"History is not sent to new members." = "История не отправляется новым членам."; - /* time unit */ "hours" = "часов"; /* No comment provided by engineer. */ +"How it affects privacy" = "Как это влияет на конфиденциальность"; + +/* No comment provided by engineer. */ +"How it helps privacy" = "Как это улучшает конфиденциальность"; + +/* alert button */ "How it works" = "Как это работает"; /* No comment provided by engineer. */ @@ -1930,11 +2656,14 @@ "How to" = "Инфо"; /* No comment provided by engineer. */ -"How to use it" = "Как использовать"; +"How to use it" = "Про адрес"; /* No comment provided by engineer. */ "How to use your servers" = "Как использовать серверы"; +/* No comment provided by engineer. */ +"Hungarian interface" = "Венгерский интерфейс"; + /* No comment provided by engineer. */ "ICE servers (one per line)" = "ICE серверы (один на строке)"; @@ -1963,7 +2692,7 @@ "Immediately" = "Сразу"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "Защищен от спама"; +"Immune to spam" = "Защищен от спама"; /* No comment provided by engineer. */ "Import" = "Импортировать"; @@ -1974,6 +2703,18 @@ /* No comment provided by engineer. */ "Import database" = "Импорт архива чата"; +/* No comment provided by engineer. */ +"Import failed" = "Ошибка импорта"; + +/* No comment provided by engineer. */ +"Import theme" = "Импорт темы"; + +/* No comment provided by engineer. */ +"Importing archive" = "Импорт архива"; + +/* No comment provided by engineer. */ +"Improved delivery, reduced traffic usage.\nMore improvements are coming soon!" = "Улучшенная доставка, меньше трафик."; + /* No comment provided by engineer. */ "Improved message delivery" = "Улучшенная доставка сообщений"; @@ -1983,9 +2724,24 @@ /* No comment provided by engineer. */ "Improved server configuration" = "Улучшенная конфигурация серверов"; +/* No comment provided by engineer. */ +"In order to continue, chat should be stopped." = "Чтобы продолжить, чат должен быть остановлен."; + /* No comment provided by engineer. */ "In reply to" = "В ответ на"; +/* No comment provided by engineer. */ +"In-call sounds" = "Звуки во время звонков"; + +/* No comment provided by engineer. */ +"inactive" = "неактивен"; + +/* report reason */ +"Inappropriate content" = "Неприемлемый контент"; + +/* report reason */ +"Inappropriate profile" = "Неприемлемый профиль"; + /* No comment provided by engineer. */ "Incognito" = "Инкогнито"; @@ -2040,14 +2796,32 @@ /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "[SimpleX Chat для терминала](https://github.com/simplex-chat/simplex-chat)"; +/* No comment provided by engineer. */ +"Instant" = "Мгновенно"; + /* No comment provided by engineer. */ "Instant push notifications will be hidden!\n" = "Мгновенные уведомления будут скрыты!\n"; /* No comment provided by engineer. */ -"Instantly" = "Мгновенно"; +"Interface" = "Интерфейс"; /* No comment provided by engineer. */ -"Interface" = "Интерфейс"; +"Interface colors" = "Цвета интерфейса"; + +/* token status text */ +"Invalid" = "Недействительный"; + +/* token status text */ +"Invalid (bad token)" = "Недействительный (плохой токен)"; + +/* token status text */ +"Invalid (expired)" = "Недействительный (истекший)"; + +/* token status text */ +"Invalid (unregistered)" = "Недействительный (незарегистрированный)"; + +/* token status text */ +"Invalid (wrong topic)" = "Недействительный (плохой заголовок)"; /* invalid chat data */ "invalid chat" = "ошибка чата"; @@ -2067,6 +2841,9 @@ /* No comment provided by engineer. */ "Invalid link" = "Ошибка ссылки"; +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Ошибка подтверждения миграции"; + /* No comment provided by engineer. */ "Invalid name!" = "Неверное имя!"; @@ -2076,7 +2853,7 @@ /* No comment provided by engineer. */ "Invalid response" = "Ошибка ответа"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "Ошибка в адресе сервера!"; /* item status text */ @@ -2088,11 +2865,14 @@ /* group name */ "invitation to group %@" = "приглашение в группу %@"; +/* No comment provided by engineer. */ +"invite" = "пригласить"; + /* No comment provided by engineer. */ "Invite friends" = "Пригласить друзей"; /* No comment provided by engineer. */ -"Invite members" = "Пригласить членов группы"; +"Invite to chat" = "Пригласить в разговор"; /* No comment provided by engineer. */ "Invite to group" = "Пригласить в группу"; @@ -2115,6 +2895,9 @@ /* No comment provided by engineer. */ "iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "Пароль базы данных будет безопасно сохранен в iOS Keychain после запуска чата или изменения пароля - это позволит получать мгновенные уведомления."; +/* No comment provided by engineer. */ +"IP address" = "IP адрес"; + /* No comment provided by engineer. */ "Irreversible message deletion" = "Окончательное удаление сообщений"; @@ -2122,7 +2905,7 @@ "Irreversible message deletion is prohibited in this chat." = "Необратимое удаление сообщений запрещено в этом чате."; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "Необратимое удаление сообщений запрещено в этой группе."; +"Irreversible message deletion is prohibited." = "Необратимое удаление сообщений запрещено в этой группе."; /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "Это позволяет иметь много анонимных соединений без общих данных между ними в одном профиле пользователя."; @@ -2133,6 +2916,9 @@ /* No comment provided by engineer. */ "It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Это может произойти, когда:\n1. Клиент отправителя удалил неотправленные сообщения через 2 дня, или сервер – через 30 дней.\n2. Расшифровка сообщения была невозможна, когда Вы или Ваш контакт использовали старую копию базы данных.\n3. Соединение компроментировано."; +/* No comment provided by engineer. */ +"It protects your IP address and connections." = "Защищает ваш IP адрес и соединения."; + /* No comment provided by engineer. */ "It seems like you are already connected via this link. If it is not the case, there was an error (%@)." = "Возможно, Вы уже соединились через эту ссылку. Если это не так, то это ошибка (%@)."; @@ -2145,7 +2931,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "Японский интерфейс"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "Вступить"; /* No comment provided by engineer. */ @@ -2172,13 +2958,16 @@ /* No comment provided by engineer. */ "Joining group" = "Вступление в группу"; -/* No comment provided by engineer. */ +/* alert action */ "Keep" = "Оставить"; +/* No comment provided by engineer. */ +"Keep conversation" = "Оставить разговор"; + /* No comment provided by engineer. */ "Keep the app open to use it from desktop" = "Оставьте приложение открытым, чтобы использовать его с компьютера"; -/* No comment provided by engineer. */ +/* alert title */ "Keep unused invitation?" = "Оставить неиспользованное приглашение?"; /* No comment provided by engineer. */ @@ -2196,9 +2985,15 @@ /* No comment provided by engineer. */ "Learn more" = "Узнать больше"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "Выйти"; +/* No comment provided by engineer. */ +"Leave chat" = "Покинуть разговор"; + +/* No comment provided by engineer. */ +"Leave chat?" = "Покинуть разговор?"; + /* No comment provided by engineer. */ "Leave group" = "Выйти из группы"; @@ -2226,6 +3021,15 @@ /* No comment provided by engineer. */ "Linked desktops" = "Связанные компьютеры"; +/* swipe action */ +"List" = "Список"; + +/* No comment provided by engineer. */ +"List name and emoji should be different for all lists." = "Название списка и эмодзи должны быть разными для всех списков."; + +/* No comment provided by engineer. */ +"List name..." = "Имя списка..."; + /* No comment provided by engineer. */ "LIVE" = "LIVE"; @@ -2235,9 +3039,6 @@ /* No comment provided by engineer. */ "Live messages" = "\"Живые\" сообщения"; -/* No comment provided by engineer. */ -"Local" = "Локальные"; - /* No comment provided by engineer. */ "Local name" = "Локальное имя"; @@ -2250,24 +3051,15 @@ /* No comment provided by engineer. */ "Lock mode" = "Режим блокировки"; -/* No comment provided by engineer. */ -"Make a private connection" = "Добавьте контакт"; - /* No comment provided by engineer. */ "Make one message disappear" = "Одно исчезающее сообщение"; /* No comment provided by engineer. */ "Make profile private!" = "Сделайте профиль скрытым!"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "Пожалуйста, проверьте, что адреса %@ серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется (%@)."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Пожалуйста, проверьте, что адреса WebRTC ICE серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется."; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Много пользователей спросили: *как SimpleX доставляет сообщения без идентификаторов пользователей?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "Пометить как удаленное для всех"; @@ -2286,26 +3078,26 @@ /* No comment provided by engineer. */ "Max 30 seconds, received instantly." = "Макс. 30 секунд, доставляются мгновенно."; -/* member role */ -"member" = "член группы"; - /* No comment provided by engineer. */ -"Member" = "Член группы"; +"Media & file servers" = "Серверы файлов и медиа"; -/* profile update event chat item */ -"member %@ changed to %@" = "член %1$@ изменился на %2$@"; +/* blur media */ +"Medium" = "Среднее"; /* rcv group event chat item */ "member connected" = "соединен(а)"; -/* No comment provided by engineer. */ -"Member role will be changed to \"%@\". All group members will be notified." = "Роль члена группы будет изменена на \"%@\". Все члены группы получат сообщение."; +/* chat feature */ +"Member reports" = "Сообщения о нарушениях"; /* No comment provided by engineer. */ -"Member role will be changed to \"%@\". The member will receive a new invitation." = "Роль члена группы будет изменена на \"%@\". Будет отправлено новое приглашение."; +"Member role will be changed to \"%@\". All chat members will be notified." = "Роль участника будет изменена на \"%@\". Все участники разговора получат уведомление."; /* No comment provided by engineer. */ -"Member will be removed from group - this cannot be undone!" = "Член группы будет удален - это действие нельзя отменить!"; +"Menus" = "Меню"; + +/* No comment provided by engineer. */ +"message" = "написать"; /* item status text */ "Message delivery error" = "Ошибка доставки сообщения"; @@ -2313,9 +3105,18 @@ /* No comment provided by engineer. */ "Message delivery receipts!" = "Отчеты о доставке сообщений!"; +/* item status text */ +"Message delivery warning" = "Предупреждение доставки сообщения"; + /* No comment provided by engineer. */ "Message draft" = "Черновик сообщения"; +/* item status text */ +"Message forwarded" = "Сообщение переслано"; + +/* No comment provided by engineer. */ +"Message queue info" = "Информация об очереди сообщений"; + /* chat feature */ "Message reactions" = "Реакции на сообщения"; @@ -2323,14 +3124,35 @@ "Message reactions are prohibited in this chat." = "Реакции на сообщения в этом чате запрещены."; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "Реакции на сообщения запрещены в этой группе."; +"Message reactions are prohibited." = "Реакции на сообщения запрещены в этой группе."; /* notification */ "message received" = "получено сообщение"; +/* No comment provided by engineer. */ +"Message reception" = "Прием сообщений"; + +/* No comment provided by engineer. */ +"Message servers" = "Серверы сообщений"; + +/* No comment provided by engineer. */ +"Message shape" = "Форма сообщений"; + +/* No comment provided by engineer. */ +"Message source remains private." = "Источник сообщения остаётся конфиденциальным."; + +/* No comment provided by engineer. */ +"Message status" = "Статус сообщения"; + +/* copied message info */ +"Message status: %@" = "Статус сообщения: %@"; + /* No comment provided by engineer. */ "Message text" = "Текст сообщения"; +/* No comment provided by engineer. */ +"Message too large" = "Сообщение слишком большое"; + /* No comment provided by engineer. */ "Messages" = "Сообщения"; @@ -2340,9 +3162,48 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "Сообщения от %@ будут показаны!"; +/* alert message */ +"Messages in this chat will never be deleted." = "Сообщения в этом чате никогда не будут удалены."; + +/* No comment provided by engineer. */ +"Messages received" = "Получено сообщений"; + +/* No comment provided by engineer. */ +"Messages sent" = "Сообщений отправлено"; + +/* alert message */ +"Messages were deleted after you selected them." = "Сообщения были удалены после того, как вы их выбрали."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Сообщения, файлы и звонки защищены **end-to-end шифрованием** с прямой секретностью (PFS), правдоподобным отрицанием и восстановлением от взлома."; + +/* No comment provided by engineer. */ +"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Сообщения, файлы и звонки защищены **квантово-устойчивым end-to-end шифрованием** с прямой секретностью (PFS), правдоподобным отрицанием и восстановлением от взлома."; + +/* No comment provided by engineer. */ +"Migrate device" = "Мигрировать устройство"; + +/* No comment provided by engineer. */ +"Migrate from another device" = "Миграция с другого устройства"; + +/* No comment provided by engineer. */ +"Migrate here" = "Мигрировать сюда"; + +/* No comment provided by engineer. */ +"Migrate to another device" = "Мигрировать на другое устройство"; + +/* No comment provided by engineer. */ +"Migrate to another device via QR code." = "Мигрируйте на другое устройство через QR код."; + +/* No comment provided by engineer. */ +"Migrating" = "Миграция"; + /* No comment provided by engineer. */ "Migrating database archive…" = "Данные чата перемещаются…"; +/* No comment provided by engineer. */ +"Migration complete" = "Миграция завершена"; + /* No comment provided by engineer. */ "Migration error:" = "Ошибка при перемещении данных:"; @@ -2353,7 +3214,7 @@ "Migration is completed" = "Перемещение данных завершено"; /* No comment provided by engineer. */ -"Migrations: %@" = "Миграции: %@"; +"Migrations:" = "Миграции:"; /* time unit */ "minutes" = "минут"; @@ -2376,65 +3237,98 @@ /* marked deleted chat item preview text */ "moderated by %@" = "удалено %@"; +/* member role */ +"moderator" = "модератор"; + /* time unit */ "months" = "месяцев"; +/* swipe action */ +"More" = "Больше"; + /* No comment provided by engineer. */ "More improvements are coming soon!" = "Дополнительные улучшения скоро!"; +/* No comment provided by engineer. */ +"More reliable network connection." = "Более надежное соединение с сетью."; + +/* No comment provided by engineer. */ +"More reliable notifications" = "Более надежные уведомления"; + /* item status description */ "Most likely this connection is deleted." = "Скорее всего, соединение удалено."; -/* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "Скорее всего, этот контакт удалил соединение с Вами."; - /* No comment provided by engineer. */ "Multiple chat profiles" = "Много профилей чата"; -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "Без звука"; +/* notification label action */ +"Mute all" = "Все без звука"; + /* No comment provided by engineer. */ "Muted when inactive!" = "Без звука, когда не активный!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "Имя"; /* No comment provided by engineer. */ "Network & servers" = "Сеть & серверы"; +/* No comment provided by engineer. */ +"Network connection" = "Интернет-соединение"; + +/* No comment provided by engineer. */ +"Network decentralization" = "Децентрализация сети"; + +/* snd error text */ +"Network issues - message expired after many attempts to send it." = "Ошибка сети - сообщение не было отправлено после многократных попыток."; + +/* No comment provided by engineer. */ +"Network management" = "Статус сети"; + +/* No comment provided by engineer. */ +"Network operator" = "Оператор сети"; + /* No comment provided by engineer. */ "Network settings" = "Настройки сети"; /* No comment provided by engineer. */ "Network status" = "Состояние сети"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "никогда"; +/* token status text */ +"New" = "Новый"; + /* No comment provided by engineer. */ "New chat" = "Новый чат"; +/* No comment provided by engineer. */ +"New chat experience 🎉" = "Новый интерфейс 🎉"; + /* notification */ "New contact request" = "Новый запрос на соединение"; /* notification */ "New contact:" = "Новый контакт:"; -/* No comment provided by engineer. */ -"New database archive" = "Новый архив чата"; - /* No comment provided by engineer. */ "New desktop app!" = "Приложение для компьютера!"; /* No comment provided by engineer. */ "New display name" = "Новое имя"; +/* notification */ +"New events" = "Новые события"; + /* No comment provided by engineer. */ "New in %@" = "Новое в %@"; /* No comment provided by engineer. */ -"New member role" = "Роль члена группы"; +"New media options" = "Новые медиа-опции"; /* notification */ "new message" = "новое сообщение"; @@ -2448,6 +3342,15 @@ /* No comment provided by engineer. */ "New passphrase…" = "Новый пароль…"; +/* No comment provided by engineer. */ +"New server" = "Новый сервер"; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used every time you start the app." = "Новые учетные данные SOCKS будут использоваться при каждом запуске приложения."; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used for each server." = "Новые учетные данные SOCKS будут использоваться для каждого сервера."; + /* pref value */ "no" = "нет"; @@ -2457,6 +3360,15 @@ /* Authentication unavailable */ "No app password" = "Нет кода доступа"; +/* No comment provided by engineer. */ +"No chats" = "Нет чатов"; + +/* No comment provided by engineer. */ +"No chats found" = "Чаты не найдены"; + +/* No comment provided by engineer. */ +"No chats in list %@" = "Нет чатов в списке %@"; + /* No comment provided by engineer. */ "No contacts selected" = "Контакты не выбраны"; @@ -2469,6 +3381,9 @@ /* No comment provided by engineer. */ "No device token!" = "Отсутствует токен устройства!"; +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Прямого соединения пока нет, сообщение переслано или будет переслано админом."; + /* No comment provided by engineer. */ "no e2e encryption" = "нет e2e шифрования"; @@ -2481,36 +3396,96 @@ /* No comment provided by engineer. */ "No history" = "Нет истории"; +/* No comment provided by engineer. */ +"No info, try to reload" = "Нет информации, попробуйте перезагрузить"; + +/* servers error */ +"No media & file servers." = "Нет серверов файлов и медиа."; + +/* No comment provided by engineer. */ +"No message" = "Нет сообщения"; + +/* servers error */ +"No message servers." = "Нет серверов сообщений."; + +/* No comment provided by engineer. */ +"No network connection" = "Нет интернет-соединения"; + +/* No comment provided by engineer. */ +"No permission to record speech" = "Нет разрешения на запись речи"; + +/* No comment provided by engineer. */ +"No permission to record video" = "Нет разрешения на запись видео"; + /* No comment provided by engineer. */ "No permission to record voice message" = "Нет разрешения для записи голосового сообщения"; +/* No comment provided by engineer. */ +"No push server" = "Без сервера нотификаций"; + /* No comment provided by engineer. */ "No received or sent files" = "Нет полученных или отправленных файлов"; +/* servers error */ +"No servers for private message routing." = "Нет серверов для доставки сообщений."; + +/* servers error */ +"No servers to receive files." = "Нет серверов для приема файлов."; + +/* servers error */ +"No servers to receive messages." = "Нет серверов для приема сообщений."; + +/* servers error */ +"No servers to send files." = "Нет серверов для отправки файлов."; + /* copied message info in history */ "no text" = "нет текста"; +/* alert title */ +"No token!" = "Нет токена!"; + +/* No comment provided by engineer. */ +"No unread chats" = "Нет непрочитанных чатов"; + +/* No comment provided by engineer. */ +"No user identifiers." = "Без идентификаторов пользователей."; + /* No comment provided by engineer. */ "Not compatible!" = "Несовместимая версия!"; +/* No comment provided by engineer. */ +"Notes" = "Заметки"; + +/* No comment provided by engineer. */ +"Nothing selected" = "Ничего не выбрано"; + +/* alert title */ +"Nothing to forward!" = "Нет сообщений, которые можно переслать!"; + /* No comment provided by engineer. */ "Notifications" = "Уведомления"; /* No comment provided by engineer. */ "Notifications are disabled!" = "Уведомления выключены"; +/* alert title */ +"Notifications error" = "Ошибка уведомлений"; + /* No comment provided by engineer. */ -"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Теперь админы могут:\n- удалять сообщения членов.\n- приостанавливать членов (роль \"наблюдатель\")"; +"Notifications privacy" = "Конфиденциальность уведомлений"; + +/* alert title */ +"Notifications status" = "Статус уведомлений"; /* member role */ "observer" = "читатель"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "нет"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Выключено"; /* feature offered item */ @@ -2519,7 +3494,7 @@ /* feature offered item */ "offered %@: %@" = "предложил(a) %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ок"; /* No comment provided by engineer. */ @@ -2528,9 +3503,6 @@ /* No comment provided by engineer. */ "Old database" = "Предыдущая версия данных чата"; -/* No comment provided by engineer. */ -"Old database archive" = "Старый архив чата"; - /* group pref value */ "on" = "да"; @@ -2538,16 +3510,22 @@ "One-time invitation link" = "Одноразовая ссылка"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "Подключаться только к onion хостам. Требуется включенный VPN."; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "Подключаться только к **onion** хостам.\nТребуется совместимый VPN."; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "Onion хосты используются, если возможно. Требуется включенный VPN."; +"Onion hosts will be used when available.\nRequires compatible VPN." = "Onion хосты используются, если возможно.\nТребуется совместимый VPN."; /* No comment provided by engineer. */ "Onion hosts will not be used." = "Onion хосты не используются."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Только пользовательские устройства хранят контакты, группы и сообщения, которые отправляются **с двухуровневым end-to-end шифрованием**."; +"Only chat owners can change preferences." = "Только владельцы разговора могут поменять предпочтения."; + +/* No comment provided by engineer. */ +"Only client devices store user profiles, contacts, groups, and messages." = "Только пользовательские устройства хранят контакты, группы и сообщения."; + +/* No comment provided by engineer. */ +"Only delete conversation" = "Удалить только разговор"; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Только владельцы группы могут изменять предпочтения группы."; @@ -2558,6 +3536,12 @@ /* No comment provided by engineer. */ "Only group owners can enable voice messages." = "Только владельцы группы могут разрешить голосовые сообщения."; +/* No comment provided by engineer. */ +"Only sender and moderators see it" = "Только отправитель и модераторы видят это"; + +/* No comment provided by engineer. */ +"Only you and moderators see it" = "Только вы и модераторы видят это"; + /* No comment provided by engineer. */ "Only you can add message reactions." = "Только Вы можете добавлять реакции на сообщения."; @@ -2588,39 +3572,78 @@ /* No comment provided by engineer. */ "Only your contact can send voice messages." = "Только Ваш контакт может отправлять голосовые сообщения."; -/* No comment provided by engineer. */ +/* alert action */ "Open" = "Открыть"; +/* No comment provided by engineer. */ +"Open changes" = "Открыть изменения"; + /* No comment provided by engineer. */ "Open chat" = "Открыть чат"; /* authentication reason */ "Open chat console" = "Открыть консоль"; +/* No comment provided by engineer. */ +"Open conditions" = "Открыть условия"; + /* No comment provided by engineer. */ "Open group" = "Открыть группу"; +/* authentication reason */ +"Open migration to another device" = "Открытие миграции на другое устройство"; + /* No comment provided by engineer. */ "Open Settings" = "Открыть Настройки"; -/* authentication reason */ -"Open user profiles" = "Открыть профили пользователя"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "Открытый протокол и код - кто угодно может запустить сервер."; - /* No comment provided by engineer. */ "Opening app…" = "Приложение отрывается…"; +/* No comment provided by engineer. */ +"Operator" = "Оператор"; + +/* alert title */ +"Operator server" = "Сервер оператора"; + +/* No comment provided by engineer. */ +"Or import archive file" = "Или импортировать файл архива"; + +/* No comment provided by engineer. */ +"Or paste archive link" = "Или вставьте ссылку архива"; + /* No comment provided by engineer. */ "Or scan QR code" = "Или отсканируйте QR код"; +/* No comment provided by engineer. */ +"Or securely share this file link" = "Или передайте эту ссылку"; + /* No comment provided by engineer. */ "Or show this code" = "Или покажите этот код"; +/* No comment provided by engineer. */ +"Or to share privately" = "Или поделиться конфиденциально"; + +/* No comment provided by engineer. */ +"Organize chats into lists" = "Организуйте чаты в списки"; + +/* No comment provided by engineer. */ +"other" = "другое"; + +/* No comment provided by engineer. */ +"Other" = "Другaя сеть"; + +/* No comment provided by engineer. */ +"other errors" = "другие ошибки"; + +/* alert message */ +"Other file errors:\n%@" = "Другие ошибки файлов:\n%@"; + /* member role */ "owner" = "владелец"; +/* feature role */ +"owners" = "владельцы"; + /* No comment provided by engineer. */ "Passcode" = "Код доступа"; @@ -2637,10 +3660,10 @@ "Passcode set!" = "Код доступа установлен!"; /* No comment provided by engineer. */ -"Password to show" = "Пароль чтобы раскрыть"; +"Password" = "Пароль"; -/* past/unknown group member */ -"Past member %@" = "Бывший член %@"; +/* No comment provided by engineer. */ +"Password to show" = "Пароль чтобы раскрыть"; /* No comment provided by engineer. */ "Paste desktop address" = "Вставить адрес компьютера"; @@ -2658,23 +3681,41 @@ "peer-to-peer" = "peer-to-peer"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "С Вами можно соединиться только через созданные Вами ссылки."; +"pending" = "ожидает"; /* No comment provided by engineer. */ -"Periodically" = "Периодически"; +"Pending" = "Ожидает"; + +/* No comment provided by engineer. */ +"pending approval" = "ожидает утверждения"; + +/* No comment provided by engineer. */ +"Periodic" = "Периодически"; /* message decrypt error item */ "Permanent decryption error" = "Ошибка расшифровки"; +/* No comment provided by engineer. */ +"Picture-in-picture calls" = "Звонки с картинкой-в-картинке"; + /* No comment provided by engineer. */ "PING count" = "Количество PING"; /* No comment provided by engineer. */ "PING interval" = "Интервал PING"; +/* No comment provided by engineer. */ +"Play from the chat list." = "Открыть из списка чатов."; + +/* No comment provided by engineer. */ +"Please ask your contact to enable calls." = "Попросите Вашего контакта разрешить звонки."; + /* No comment provided by engineer. */ "Please ask your contact to enable sending voice messages." = "Попросите у Вашего контакта разрешить отправку голосовых сообщений."; +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Пожалуйста, проверьте, что мобильный и компьютер находятся в одной и той же локальной сети, и что брандмауэр компьютера разрешает подключение.\nПожалуйста, поделитесь любыми другими ошибками с разработчиками."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Пожалуйста, проверьте, что Вы использовали правильную ссылку или попросите, чтобы Ваш контакт отправил Вам другую ссылку."; @@ -2684,6 +3725,9 @@ /* No comment provided by engineer. */ "Please check yours and your contact preferences." = "Проверьте предпочтения Вашего контакта."; +/* No comment provided by engineer. */ +"Please confirm that network settings are correct for this device." = "Пожалуйста, подтвердите, что настройки сети верны для этого устройства."; + /* No comment provided by engineer. */ "Please contact developers.\nError: %@" = "Пожалуйста, сообщите разработчикам.\nОшибка: %@"; @@ -2711,9 +3755,21 @@ /* No comment provided by engineer. */ "Please store passphrase securely, you will NOT be able to change it if you lose it." = "Пожалуйста, надежно сохраните пароль, Вы НЕ сможете его поменять, если потеряете."; +/* token info */ +"Please try to disable and re-enable notfications." = "Попробуйте выключить и снова включить уведомления."; + +/* token info */ +"Please wait for token activation to complete." = "Пожалуйста, дождитесь завершения активации токена."; + +/* token info */ +"Please wait for token to be registered." = "Пожалуйста, дождитесь регистрации токена."; + /* No comment provided by engineer. */ "Polish interface" = "Польский интерфейс"; +/* No comment provided by engineer. */ +"Port" = "Порт"; + /* server test error */ "Possibly, certificate fingerprint in server address is incorrect" = "Возможно, хэш сертификата в адресе сервера неверный"; @@ -2721,42 +3777,69 @@ "Preserve the last message draft, with attachments." = "Сохранить последний черновик, вместе с вложениями."; /* No comment provided by engineer. */ -"Preset server" = "Сервер по умолчанию"; +"Preset server address" = "Адрес сервера по умолчанию"; /* No comment provided by engineer. */ -"Preset server address" = "Адрес сервера по умолчанию"; +"Preset servers" = "Серверы по умолчанию"; /* No comment provided by engineer. */ "Preview" = "Просмотр"; +/* No comment provided by engineer. */ +"Previously connected servers" = "Ранее подключенные серверы"; + /* No comment provided by engineer. */ "Privacy & security" = "Конфиденциальность"; +/* No comment provided by engineer. */ +"Privacy for your customers." = "Конфиденциальность для ваших покупателей."; + +/* No comment provided by engineer. */ +"Privacy policy and conditions of use." = "Политика конфиденциальности и условия использования."; + /* No comment provided by engineer. */ "Privacy redefined" = "Более конфиденциальный"; +/* No comment provided by engineer. */ +"Private chats, groups and your contacts are not accessible to server operators." = "Частные разговоры, группы и Ваши контакты недоступны для операторов серверов."; + /* No comment provided by engineer. */ "Private filenames" = "Защищенные имена файлов"; +/* No comment provided by engineer. */ +"Private media file names." = "Конфиденциальные названия медиафайлов."; + +/* No comment provided by engineer. */ +"Private message routing" = "Конфиденциальная доставка сообщений"; + +/* No comment provided by engineer. */ +"Private message routing 🚀" = "Конфиденциальная доставка 🚀"; + /* name of notes to self */ "Private notes" = "Личные заметки"; +/* No comment provided by engineer. */ +"Private routing" = "Конфиденциальная доставка"; + +/* No comment provided by engineer. */ +"Private routing error" = "Ошибка конфиденциальной доставки"; + /* No comment provided by engineer. */ "Profile and server connections" = "Профиль и соединения на сервере"; /* No comment provided by engineer. */ -"Profile image" = "Аватар"; +"Profile image" = "Картинка профиля"; /* No comment provided by engineer. */ -"Profile name" = "Имя профиля"; - -/* No comment provided by engineer. */ -"Profile name:" = "Имя профиля:"; +"Profile images" = "Картинки профилей"; /* No comment provided by engineer. */ "Profile password" = "Пароль профиля"; /* No comment provided by engineer. */ +"Profile theme" = "Тема профиля"; + +/* alert message */ "Profile update will be sent to your contacts." = "Обновлённый профиль будет отправлен Вашим контактам."; /* No comment provided by engineer. */ @@ -2772,7 +3855,7 @@ "Prohibit messages reactions." = "Запретить реакции на сообщения."; /* No comment provided by engineer. */ -"Prohibit sending direct messages to members." = "Запретить посылать прямые сообщения членам группы."; +"Prohibit reporting messages to moderators." = "Запретить жаловаться модераторам группы."; /* No comment provided by engineer. */ "Prohibit sending disappearing messages." = "Запретить посылать исчезающие сообщения."; @@ -2780,41 +3863,71 @@ /* No comment provided by engineer. */ "Prohibit sending files and media." = "Запретить слать файлы и медиа."; +/* No comment provided by engineer. */ +"Prohibit sending SimpleX links." = "Запретить отправку ссылок SimpleX."; + /* No comment provided by engineer. */ "Prohibit sending voice messages." = "Запретить отправлять голосовые сообщений."; /* No comment provided by engineer. */ "Protect app screen" = "Защитить экран приложения"; +/* No comment provided by engineer. */ +"Protect IP address" = "Защитить IP адрес"; + /* No comment provided by engineer. */ "Protect your chat profiles with a password!" = "Защитите Ваши профили чата паролем!"; +/* No comment provided by engineer. */ +"Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." = "Защитите ваш IP адрес от серверов сообщений, выбранных Вашими контактами.\nВключите в настройках *Сети и серверов*."; + /* No comment provided by engineer. */ "Protocol timeout" = "Таймаут протокола"; /* No comment provided by engineer. */ "Protocol timeout per KB" = "Таймаут протокола на KB"; +/* No comment provided by engineer. */ +"Proxied" = "Проксировано"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Проксированные серверы"; + +/* No comment provided by engineer. */ +"Proxy requires password" = "Прокси требует пароль"; + /* No comment provided by engineer. */ "Push notifications" = "Доставка уведомлений"; +/* No comment provided by engineer. */ +"Push server" = "Сервер уведомлений"; + +/* chat item text */ +"quantum resistant e2e encryption" = "квантово-устойчивое e2e шифрование"; + +/* No comment provided by engineer. */ +"Quantum resistant encryption" = "Квантово-устойчивое шифрование"; + /* No comment provided by engineer. */ "Rate the app" = "Оценить приложение"; +/* No comment provided by engineer. */ +"Reachable chat toolbar" = "Доступная панель чата"; + /* chat item menu */ "React…" = "Реакция…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "Прочитано"; /* No comment provided by engineer. */ "Read more" = "Узнать больше"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Узнать больше в [Руководстве пользователя](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Дополнительная информация в [Руководстве пользователя](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Дополнительная информация в [Руководстве пользователя](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)."; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "Узнать больше в [Руководстве пользователя](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)."; /* No comment provided by engineer. */ "Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Узнать больше в [Руководстве пользователя](https://simplex.chat/docs/guide/readme.html#connect-to-friends)."; @@ -2823,10 +3936,10 @@ "Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Узнайте больше из нашего [GitHub репозитория](https://github.com/simplex-chat/simplex-chat#readme)."; /* No comment provided by engineer. */ -"Read more in our GitHub repository." = "Узнайте больше из нашего GitHub репозитория."; +"Receipts are disabled" = "Отчёты о доставке выключены"; /* No comment provided by engineer. */ -"Receipts are disabled" = "Отчёты о доставке выключены"; +"Receive errors" = "Ошибки приема"; /* No comment provided by engineer. */ "received answer…" = "получен ответ…"; @@ -2846,6 +3959,15 @@ /* message info title */ "Received message" = "Полученное сообщение"; +/* No comment provided by engineer. */ +"Received messages" = "Полученные сообщения"; + +/* No comment provided by engineer. */ +"Received reply" = "Полученный ответ"; + +/* No comment provided by engineer. */ +"Received total" = "Всего получено"; + /* No comment provided by engineer. */ "Receiving address will be changed to a different server. Address change will complete after sender comes online." = "Адрес получения сообщений будет перемещён на другой сервер. Изменение адреса завершится после того как отправитель будет онлайн."; @@ -2858,12 +3980,30 @@ /* No comment provided by engineer. */ "Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)." = "История сообщений и улучшенный [каталог групп](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion)."; +/* No comment provided by engineer. */ +"Recipient(s) can't see who this message is from." = "Получатели не видят от кого это сообщение."; + /* No comment provided by engineer. */ "Recipients see updates as you type them." = "Получатели видят их в то время как Вы их набираете."; +/* No comment provided by engineer. */ +"Reconnect" = "Переподключить"; + /* No comment provided by engineer. */ "Reconnect all connected servers to force message delivery. It uses additional traffic." = "Повторно подключите все серверы, чтобы принудительно доставить сообщения. Используется дополнительный трафик."; +/* No comment provided by engineer. */ +"Reconnect all servers" = "Переподключить все серверы"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "Переподключить все серверы?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "Переподключить сервер для устранения неполадок доставки сообщений. Это использует дополнительный трафик."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "Переподключить сервер?"; + /* No comment provided by engineer. */ "Reconnect servers?" = "Переподключить серверы?"; @@ -2876,7 +4016,17 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "Уменьшенное потребление батареи"; -/* reject incoming call via notification */ +/* No comment provided by engineer. */ +"Register" = "Зарегистрировать"; + +/* token info */ +"Register notification token?" = "Зарегистрировать токен уведомлений?"; + +/* token status text */ +"Registered" = "Зарегистрирован"; + +/* reject incoming call via notification +swipe action */ "Reject" = "Отклонить"; /* No comment provided by engineer. */ @@ -2885,6 +4035,9 @@ /* No comment provided by engineer. */ "Reject contact request" = "Отклонить запрос"; +/* No comment provided by engineer. */ +"rejected" = "отклонён"; + /* call status */ "rejected call" = "отклонённый звонок"; @@ -2898,10 +4051,10 @@ "Remove" = "Удалить"; /* No comment provided by engineer. */ -"Remove member" = "Удалить члена группы"; +"Remove archive?" = "Удалить архив?"; /* No comment provided by engineer. */ -"Remove member?" = "Удалить члена группы?"; +"Remove image" = "Удалить изображение"; /* No comment provided by engineer. */ "Remove passphrase from keychain?" = "Удалить пароль из Keychain?"; @@ -2933,24 +4086,81 @@ /* No comment provided by engineer. */ "Repeat connection request?" = "Повторить запрос на соединение?"; +/* No comment provided by engineer. */ +"Repeat download" = "Повторить загрузку"; + +/* No comment provided by engineer. */ +"Repeat import" = "Повторить импорт"; + /* No comment provided by engineer. */ "Repeat join request?" = "Повторить запрос на вступление?"; +/* No comment provided by engineer. */ +"Repeat upload" = "Повторить загрузку"; + /* chat item action */ "Reply" = "Ответить"; +/* chat item action */ +"Report" = "Пожаловаться"; + +/* report reason */ +"Report content: only group moderators will see it." = "Пожаловаться на сообщение: увидят только модераторы группы."; + +/* report reason */ +"Report member profile: only group moderators will see it." = "Пожаловаться на профиль: увидят только модераторы группы."; + +/* report reason */ +"Report other: only group moderators will see it." = "Пожаловаться: увидят только модераторы группы."; + +/* No comment provided by engineer. */ +"Report reason?" = "Причина сообщения?"; + +/* report reason */ +"Report spam: only group moderators will see it." = "Пожаловаться на спам: увидят только модераторы группы."; + +/* report reason */ +"Report violation: only group moderators will see it." = "Пожаловаться на нарушение: увидят только модераторы группы."; + +/* report in notification */ +"Report: %@" = "Сообщение о нарушении: %@"; + +/* No comment provided by engineer. */ +"Reporting messages to moderators is prohibited." = "Сообщения о нарушениях запрещены в этой группе."; + +/* No comment provided by engineer. */ +"Reports" = "Сообщения о нарушениях"; + +/* chat list item title */ +"requested to connect" = "запрошено соединение"; + /* No comment provided by engineer. */ "Required" = "Обязательно"; /* No comment provided by engineer. */ "Reset" = "Сбросить"; +/* No comment provided by engineer. */ +"Reset all hints" = "Сбросить все подсказки"; + +/* No comment provided by engineer. */ +"Reset all statistics" = "Сбросить всю статистику"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "Сбросить всю статистику?"; + /* No comment provided by engineer. */ "Reset colors" = "Сбросить цвета"; +/* No comment provided by engineer. */ +"Reset to app theme" = "Сбросить на тему приложения"; + /* No comment provided by engineer. */ "Reset to defaults" = "Сбросить настройки"; +/* No comment provided by engineer. */ +"Reset to user theme" = "Сбросить на тему пользователя"; + /* No comment provided by engineer. */ "Restart the app to create a new chat profile" = "Перезапустите приложение, чтобы создать новый профиль."; @@ -2976,7 +4186,7 @@ "Reveal" = "Показать"; /* No comment provided by engineer. */ -"Revert" = "Отменить изменения"; +"Review conditions" = "Посмотреть условия"; /* No comment provided by engineer. */ "Revoke" = "Отозвать"; @@ -2993,37 +4203,41 @@ /* No comment provided by engineer. */ "Run chat" = "Запустить chat"; -/* chat item action */ +/* No comment provided by engineer. */ +"Safely receive files" = "Получайте файлы безопасно"; + +/* No comment provided by engineer. */ +"Safer groups" = "Более безопасные группы"; + +/* alert button +chat item action */ "Save" = "Сохранить"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Сохранить (и уведомить контакты)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "Сохранить и уведомить контакт"; /* No comment provided by engineer. */ -"Save and notify group members" = "Сохранить и уведомить членов группы"; +"Save and reconnect" = "Сохранить и переподключиться"; /* No comment provided by engineer. */ "Save and update group profile" = "Сохранить сообщение и обновить группу"; -/* No comment provided by engineer. */ -"Save archive" = "Сохранить архив"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Сохранить настройки автоприема"; - /* No comment provided by engineer. */ "Save group profile" = "Сохранить профиль группы"; +/* No comment provided by engineer. */ +"Save list" = "Сохранить список"; + /* No comment provided by engineer. */ "Save passphrase and open chat" = "Сохранить пароль и открыть чат"; /* No comment provided by engineer. */ "Save passphrase in Keychain" = "Сохранить пароль в Keychain"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "Сохранить предпочтения?"; /* No comment provided by engineer. */ @@ -3032,14 +4246,26 @@ /* No comment provided by engineer. */ "Save servers" = "Сохранить серверы"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "Сохранить серверы?"; /* No comment provided by engineer. */ -"Save settings?" = "Сохранить настройки?"; +"Save welcome message?" = "Сохранить приветственное сообщение?"; + +/* alert title */ +"Save your profile?" = "Сохранить ваш профиль?"; /* No comment provided by engineer. */ -"Save welcome message?" = "Сохранить приветственное сообщение?"; +"saved" = "сохранено"; + +/* No comment provided by engineer. */ +"Saved" = "Сохранено"; + +/* No comment provided by engineer. */ +"Saved from" = "Сохранено из"; + +/* No comment provided by engineer. */ +"saved from %@" = "сохранено из %@"; /* message info title */ "Saved message" = "Сохраненное сообщение"; @@ -3047,6 +4273,15 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Сохраненные WebRTC ICE серверы будут удалены"; +/* No comment provided by engineer. */ +"Saving %lld messages" = "Сохранение %lld сообщений"; + +/* No comment provided by engineer. */ +"Scale" = "Масштаб"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Сканировать / Вставить ссылку"; + /* No comment provided by engineer. */ "Scan code" = "Сканировать код"; @@ -3062,6 +4297,9 @@ /* No comment provided by engineer. */ "Scan server QR code" = "Сканировать QR код сервера"; +/* No comment provided by engineer. */ +"search" = "поиск"; + /* No comment provided by engineer. */ "Search" = "Поиск"; @@ -3074,6 +4312,9 @@ /* network option */ "sec" = "сек"; +/* No comment provided by engineer. */ +"Secondary" = "Вторичный"; + /* time unit */ "seconds" = "секунд"; @@ -3083,6 +4324,9 @@ /* server test step */ "Secure queue" = "Защита очереди"; +/* No comment provided by engineer. */ +"Secured" = "Защищено"; + /* No comment provided by engineer. */ "Security assessment" = "Аудит безопасности"; @@ -3092,9 +4336,18 @@ /* chat item text */ "security code changed" = "код безопасности изменился"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "Выбрать"; +/* No comment provided by engineer. */ +"Select chat profile" = "Выберите профиль чата"; + +/* No comment provided by engineer. */ +"Selected %lld" = "Выбрано %lld"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Выбранные настройки чата запрещают это сообщение."; + /* No comment provided by engineer. */ "Self-destruct" = "Самоуничтожение"; @@ -3119,26 +4372,35 @@ /* No comment provided by engineer. */ "send direct message" = "отправьте сообщение"; -/* No comment provided by engineer. */ -"Send direct message" = "Отправить сообщение"; - /* No comment provided by engineer. */ "Send direct message to connect" = "Отправьте сообщение чтобы соединиться"; /* No comment provided by engineer. */ "Send disappearing message" = "Отправить исчезающее сообщение"; +/* No comment provided by engineer. */ +"Send errors" = "Ошибки отправки"; + /* No comment provided by engineer. */ "Send link previews" = "Отправлять картинки ссылок"; /* No comment provided by engineer. */ "Send live message" = "Отправить живое сообщение"; +/* No comment provided by engineer. */ +"Send message to enable calls." = "Отправьте сообщение, чтобы включить звонки."; + +/* No comment provided by engineer. */ +"Send messages directly when IP address is protected and your or destination server does not support private routing." = "Отправлять сообщения напрямую, когда IP адрес защищен, и Ваш сервер или сервер получателя не поддерживает конфиденциальную доставку."; + +/* No comment provided by engineer. */ +"Send messages directly when your or destination server does not support private routing." = "Отправлять сообщения напрямую, когда Ваш сервер или сервер получателя не поддерживает конфиденциальную доставку."; + /* No comment provided by engineer. */ "Send notifications" = "Отправлять уведомления"; /* No comment provided by engineer. */ -"Send notifications:" = "Отправлять уведомления:"; +"Send private reports" = "Вы можете сообщить о нарушениях"; /* No comment provided by engineer. */ "Send questions and ideas" = "Отправьте вопросы и идеи"; @@ -3149,10 +4411,7 @@ /* No comment provided by engineer. */ "Send them from gallery or custom keyboards." = "Отправьте из галереи или из дополнительных клавиатур."; -/* No comment provided by engineer. */ -"Send up to 100 last messages to new members." = "Отправить до 100 последних сообщений новым членам."; - -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Отправитель отменил передачу файла."; /* No comment provided by engineer. */ @@ -3188,15 +4447,57 @@ /* copied message info */ "Sent at: %@" = "Отправлено: %@"; +/* No comment provided by engineer. */ +"Sent directly" = "Отправлено напрямую"; + /* notification */ "Sent file event" = "Отправка файла"; /* message info title */ "Sent message" = "Отправленное сообщение"; +/* No comment provided by engineer. */ +"Sent messages" = "Отправленные сообщения"; + /* No comment provided by engineer. */ "Sent messages will be deleted after set time." = "Отправленные сообщения будут удалены через заданное время."; +/* No comment provided by engineer. */ +"Sent reply" = "Отправленный ответ"; + +/* No comment provided by engineer. */ +"Sent total" = "Всего отправлено"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Отправлено через прокси"; + +/* No comment provided by engineer. */ +"Server" = "Сервер"; + +/* alert message */ +"Server added to operator %@." = "Сервер добавлен к оператору %@."; + +/* No comment provided by engineer. */ +"Server address" = "Адрес сервера"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "Адрес сервера несовместим с сетевыми настройками: %@."; + +/* srv error text. */ +"Server address is incompatible with network settings." = "Адрес сервера несовместим с настройками сети."; + +/* alert title */ +"Server operator changed." = "Оператор серверов изменен."; + +/* No comment provided by engineer. */ +"Server operators" = "Операторы серверов"; + +/* alert title */ +"Server protocol changed." = "Протокол сервера изменен."; + +/* queue info */ +"server queue info: %@\n\nlast received msg: %@" = "информация сервера об очереди: %1$@\n\nпоследнее полученное сообщение: %2$@"; + /* server test error */ "Server requires authorization to create queues, check password" = "Сервер требует авторизации для создания очередей, проверьте пароль"; @@ -3206,24 +4507,48 @@ /* No comment provided by engineer. */ "Server test failed!" = "Ошибка теста сервера!"; +/* No comment provided by engineer. */ +"Server type" = "Тип сервера"; + +/* srv error text */ +"Server version is incompatible with network settings." = "Версия сервера несовместима с настройками сети."; + +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "Версия сервера несовместима с вашим приложением: %@."; + /* No comment provided by engineer. */ "Servers" = "Серверы"; +/* No comment provided by engineer. */ +"Servers info" = "Информация о серверах"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "Статистика серверов будет сброшена - это нельзя отменить!"; + /* No comment provided by engineer. */ "Session code" = "Код сессии"; /* No comment provided by engineer. */ "Set 1 day" = "Установить 1 день"; +/* No comment provided by engineer. */ +"Set chat name…" = "Имя чата…"; + /* No comment provided by engineer. */ "Set contact name…" = "Имя контакта…"; +/* No comment provided by engineer. */ +"Set default theme" = "Установить тему по умолчанию"; + /* No comment provided by engineer. */ "Set group preferences" = "Предпочтения группы"; /* No comment provided by engineer. */ "Set it instead of system authentication." = "Установите код вместо системной аутентификации."; +/* No comment provided by engineer. */ +"Set message expiration in chats." = "Установите срок хранения сообщений в чатах."; + /* profile update event chat item */ "set new contact address" = "установлен новый адрес контакта"; @@ -3234,10 +4559,10 @@ "Set passcode" = "Установить код доступа"; /* No comment provided by engineer. */ -"Set passphrase to export" = "Установите пароль"; +"Set passphrase" = "Установить пароль"; /* No comment provided by engineer. */ -"Set the message shown to new members!" = "Установить сообщение для новых членов группы!"; +"Set passphrase to export" = "Установите пароль"; /* No comment provided by engineer. */ "Set timeouts for proxy/VPN" = "Установить таймауты для прокси/VPN"; @@ -3245,27 +4570,55 @@ /* No comment provided by engineer. */ "Settings" = "Настройки"; -/* chat item action */ +/* alert message */ +"Settings were changed." = "Настройки были изменены."; + +/* No comment provided by engineer. */ +"Shape profile images" = "Форма картинок профилей"; + +/* alert action +chat item action */ "Share" = "Поделиться"; /* No comment provided by engineer. */ "Share 1-time link" = "Поделиться одноразовой ссылкой"; +/* No comment provided by engineer. */ +"Share 1-time link with a friend" = "Поделитесь одноразовой ссылкой с другом"; + /* No comment provided by engineer. */ "Share address" = "Поделиться адресом"; /* No comment provided by engineer. */ +"Share address publicly" = "Поделитесь адресом"; + +/* alert title */ "Share address with contacts?" = "Поделиться адресом с контактами?"; +/* No comment provided by engineer. */ +"Share from other apps." = "Поделитесь из других приложений."; + /* No comment provided by engineer. */ "Share link" = "Поделиться ссылкой"; +/* No comment provided by engineer. */ +"Share profile" = "Поделиться профилем"; + +/* No comment provided by engineer. */ +"Share SimpleX address on social media." = "Поделитесь SimpleX адресом в социальных сетях."; + /* No comment provided by engineer. */ "Share this 1-time invite link" = "Поделиться одноразовой ссылкой-приглашением"; +/* No comment provided by engineer. */ +"Share to SimpleX" = "Поделиться в SimpleX"; + /* No comment provided by engineer. */ "Share with contacts" = "Поделиться с контактами"; +/* No comment provided by engineer. */ +"Show → on messages sent via private routing." = "Показать → на сообщениях доставленных конфиденциально."; + /* No comment provided by engineer. */ "Show calls in phone history" = "Показать звонки в истории телефона"; @@ -3275,18 +4628,39 @@ /* No comment provided by engineer. */ "Show last messages" = "Показывать последние сообщения"; +/* No comment provided by engineer. */ +"Show message status" = "Показать статус сообщения"; + +/* No comment provided by engineer. */ +"Show percentage" = "Показать процент"; + /* No comment provided by engineer. */ "Show preview" = "Показывать уведомления"; +/* No comment provided by engineer. */ +"Show QR code" = "Показать QR код"; + /* No comment provided by engineer. */ "Show:" = "Показать:"; +/* No comment provided by engineer. */ +"SimpleX" = "SimpleX"; + /* No comment provided by engineer. */ "SimpleX address" = "Адрес SimpleX"; /* No comment provided by engineer. */ "SimpleX Address" = "Адрес SimpleX"; +/* No comment provided by engineer. */ +"SimpleX address and 1-time links are safe to share via any messenger." = "Адрес SimpleX и одноразовые ссылки безопасно отправлять через любой мессенджер."; + +/* No comment provided by engineer. */ +"SimpleX address or 1-time link?" = "Адрес SimpleX или одноразовая ссылка?"; + +/* No comment provided by engineer. */ +"SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app." = "SimpleX Chat и Flux заключили соглашение добавить серверы под управлением Flux в приложение."; + /* No comment provided by engineer. */ "SimpleX Chat security was audited by Trail of Bits." = "Безопасность SimpleX Chat была проверена Trail of Bits."; @@ -3299,9 +4673,15 @@ /* simplex link type */ "SimpleX group link" = "SimpleX ссылка группы"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "SimpleX ссылки"; +/* No comment provided by engineer. */ +"SimpleX links are prohibited." = "Ссылки SimpleX запрещены в этой группе."; + +/* No comment provided by engineer. */ +"SimpleX links not allowed" = "Ссылки SimpleX не разрешены"; + /* No comment provided by engineer. */ "SimpleX Lock" = "Блокировка SimpleX"; @@ -3317,9 +4697,15 @@ /* simplex link type */ "SimpleX one-time invitation" = "SimpleX одноразовая ссылка"; +/* No comment provided by engineer. */ +"SimpleX protocols reviewed by Trail of Bits." = "Аудит SimpleX протоколов от Trail of Bits."; + /* No comment provided by engineer. */ "Simplified incognito mode" = "Упрощенный режим Инкогнито"; +/* No comment provided by engineer. */ +"Size" = "Размер"; + /* No comment provided by engineer. */ "Skip" = "Пропустить"; @@ -3330,14 +4716,42 @@ "Small groups (max 20)" = "Маленькие группы (до 20)"; /* No comment provided by engineer. */ -"SMP servers" = "SMP серверы"; +"SMP server" = "SMP сервер"; + +/* No comment provided by engineer. */ +"SOCKS proxy" = "SOCKS прокси"; + +/* blur media */ +"Soft" = "Слабое"; + +/* No comment provided by engineer. */ +"Some app settings were not migrated." = "Некоторые настройки приложения не были перенесены."; + +/* No comment provided by engineer. */ +"Some file(s) were not exported:" = "Некоторые файл(ы) не были экспортированы:"; /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "Во время импорта произошли некоторые ошибки - для получения более подробной информации вы можете обратиться к консоли."; +/* No comment provided by engineer. */ +"Some non-fatal errors occurred during import:" = "Во время импорта произошли некоторые ошибки:"; + +/* alert message */ +"Some servers failed the test:\n%@" = "Серверы не прошли тест:\n%@"; + /* notification title */ "Somebody" = "Контакт"; +/* blocking reason +report reason */ +"Spam" = "Спам"; + +/* No comment provided by engineer. */ +"Square, circle, or anything in between." = "Квадрат, круг и все, что между ними."; + +/* chat item text */ +"standard end-to-end encryption" = "стандартное end-to-end шифрование"; + /* No comment provided by engineer. */ "Start chat" = "Запустить чат"; @@ -3347,14 +4761,20 @@ /* No comment provided by engineer. */ "Start migration" = "Запустить перемещение данных"; +/* No comment provided by engineer. */ +"Starting from %@." = "Начиная с %@."; + /* No comment provided by engineer. */ "starting…" = "инициализация…"; +/* No comment provided by engineer. */ +"Statistics" = "Статистика"; + /* No comment provided by engineer. */ "Stop" = "Остановить"; /* No comment provided by engineer. */ -"Stop chat to enable database actions" = "Остановите чат, чтобы разблокировать операции с архивом чата"; +"Stop chat" = "Остановить чат"; /* No comment provided by engineer. */ "Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Остановите чат, чтобы экспортировать или импортировать архив чата или удалить данные чата. Вы не сможете получать и отправлять сообщения, пока чат остановлен."; @@ -3371,36 +4791,66 @@ /* No comment provided by engineer. */ "Stop sending file?" = "Остановить отправку файла?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "Прекратить делиться"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "Прекратить делиться адресом?"; /* authentication reason */ "Stop SimpleX" = "Остановить SimpleX"; +/* No comment provided by engineer. */ +"Stopping chat" = "Остановка чата"; + +/* No comment provided by engineer. */ +"Storage" = "Хранилище"; + /* No comment provided by engineer. */ "strike" = "зачеркнуть"; +/* blur media */ +"Strong" = "Сильное"; + /* No comment provided by engineer. */ "Submit" = "Продолжить"; +/* No comment provided by engineer. */ +"Subscribed" = "Подписано"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Ошибки подписки"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Подписок игнорировано"; + /* No comment provided by engineer. */ "Support SimpleX Chat" = "Поддержать SimpleX Chat"; +/* No comment provided by engineer. */ +"Switch audio and video during the call." = "Переключайте звук и видео во время звонка."; + +/* No comment provided by engineer. */ +"Switch chat profile for 1-time invitations." = "Переключайте профиль чата для одноразовых приглашений."; + /* No comment provided by engineer. */ "System" = "Системная"; /* No comment provided by engineer. */ "System authentication" = "Системная аутентификация"; +/* No comment provided by engineer. */ +"Tail" = "Хвост"; + /* No comment provided by engineer. */ "Take picture" = "Сделать фото"; /* No comment provided by engineer. */ "Tap button " = "Нажмите кнопку "; +/* No comment provided by engineer. */ +"Tap Create SimpleX address in the menu to create it later." = "Нажмите Создать адрес SimpleX в меню, чтобы создать его позже."; + /* No comment provided by engineer. */ "Tap to activate profile." = "Нажмите, чтобы сделать профиль активным."; @@ -3420,11 +4870,14 @@ "Tap to scan" = "Нажмите, чтобы сканировать"; /* No comment provided by engineer. */ -"Tap to start a new chat" = "Нажмите, чтобы начать чат"; +"TCP connection" = "TCP-соединение"; /* No comment provided by engineer. */ "TCP connection timeout" = "Таймаут TCP соединения"; +/* No comment provided by engineer. */ +"TCP port for messaging" = "TCP-порт для отправки сообщений"; + /* No comment provided by engineer. */ "TCP_KEEPCNT" = "TCP_KEEPCNT"; @@ -3434,16 +4887,22 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* file error alert title */ +"Temporary file error" = "Временная ошибка файла"; + /* server test failure */ "Test failed at step %@." = "Ошибка теста на шаге %@."; +/* No comment provided by engineer. */ +"Test notifications" = "Протестировать уведомления"; + /* No comment provided by engineer. */ "Test server" = "Тестировать сервер"; /* No comment provided by engineer. */ "Test servers" = "Тестировать серверы"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "Ошибка тестов!"; /* No comment provided by engineer. */ @@ -3456,10 +4915,13 @@ "Thanks to the users – contribute via Weblate!" = "Благодаря пользователям – добавьте переводы через Weblate!"; /* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "Первая в мире платформа без идентификаторов пользователей."; +"The app can notify you when you receive messages or contact requests - please open settings to enable." = "Приложение может посылать Вам уведомления о сообщениях и запросах на соединение - уведомления можно включить в Настройках."; /* No comment provided by engineer. */ -"The app can notify you when you receive messages or contact requests - please open settings to enable." = "Приложение может посылать Вам уведомления о сообщениях и запросах на соединение - уведомления можно включить в Настройках."; +"The app protects your privacy by using different operators in each conversation." = "Приложение улучшает конфиденциальность используя разных операторов в каждом разговоре."; + +/* No comment provided by engineer. */ +"The app will ask to confirm downloads from unknown file servers (except .onion)." = "Приложение будет запрашивать подтверждение загрузки с неизвестных серверов (за исключением .onion адресов)."; /* No comment provided by engineer. */ "The attempt to change database passphrase was not completed." = "Попытка поменять пароль базы данных не была завершена."; @@ -3467,6 +4929,9 @@ /* No comment provided by engineer. */ "The code you scanned is not a SimpleX link QR code." = "Этот QR код не является SimpleX-ccылкой."; +/* No comment provided by engineer. */ +"The connection reached the limit of undelivered messages, your contact may be offline." = "Соединение достигло предела недоставленных сообщений. Возможно, Ваш контакт не в сети."; + /* No comment provided by engineer. */ "The connection you accepted will be cancelled!" = "Подтвержденное соединение будет отменено!"; @@ -3479,26 +4944,26 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "Шифрование работает, и новое соглашение не требуется. Это может привести к ошибкам соединения!"; +/* No comment provided by engineer. */ +"The future of messaging" = "Будущее коммуникаций"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "Хэш предыдущего сообщения отличается."; /* No comment provided by engineer. */ "The ID of the next message is incorrect (less or equal to the previous).\nIt can happen because of some bug or when the connection is compromised." = "Неправильный ID предыдущего сообщения (меньше или равен предыдущему).\nЭто может произойти из-за ошибки программы, или когда соединение компроментировано."; -/* No comment provided by engineer. */ -"The message will be deleted for all members." = "Сообщение будет удалено для всех членов группы."; - -/* No comment provided by engineer. */ -"The message will be marked as moderated for all members." = "Сообщение будет помечено как удаленное для всех членов группы."; - -/* No comment provided by engineer. */ -"The next generation of private messaging" = "Новое поколение приватных сообщений"; - /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "Предыдущая версия данных чата не удалена при перемещении, её можно удалить."; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "Профиль отправляется только Вашим контактам."; +"Your profile is stored on your device and only shared with your contacts." = "Ваш профиль храниться на Вашем устройстве и отправляется только контактам."; + +/* No comment provided by engineer. */ +"The same conditions will apply to operator **%@**." = "Те же самые условия будут приняты для оператора **%@**."; + +/* No comment provided by engineer. */ +"The second preset operator in the app!" = "Второй оператор серверов в приложении!"; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "Вторая галочка - знать, что доставлено! ✅"; @@ -3509,11 +4974,20 @@ /* No comment provided by engineer. */ "The servers for new connections of your current chat profile **%@**." = "Серверы для новых соединений Вашего текущего профиля чата **%@**."; +/* No comment provided by engineer. */ +"The servers for new files of your current chat profile **%@**." = "Серверы для новых файлов Вашего текущего профиля **%@**."; + /* No comment provided by engineer. */ "The text you pasted is not a SimpleX link." = "Вставленный текст не является SimpleX-ссылкой."; /* No comment provided by engineer. */ -"Theme" = "Тема"; +"The uploaded database archive will be permanently removed from the servers." = "Загруженный архив базы данных будет навсегда удален с серверов."; + +/* No comment provided by engineer. */ +"Themes" = "Темы"; + +/* No comment provided by engineer. */ +"These conditions will also apply for: **%@**." = "Эти условия также будут применены к: **%@**."; /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Установки для Вашего активного профиля **%@**."; @@ -3527,9 +5001,18 @@ /* No comment provided by engineer. */ "This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Это действие нельзя отменить — все сообщения, отправленные или полученные раньше чем выбрано, будут удалены. Это может занять несколько минут."; +/* alert message */ +"This action cannot be undone - the messages sent and received in this chat earlier than selected will be deleted." = "Это действие нельзя отменить - сообщения в этом чате, отправленные или полученные раньше чем выбрано, будут удалены."; + /* No comment provided by engineer. */ "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Это действие нельзя отменить — Ваш профиль, контакты, сообщения и файлы будут безвозвратно утеряны."; +/* E2EE info chat item */ +"This chat is protected by end-to-end encryption." = "Чат защищен end-to-end шифрованием."; + +/* E2EE info chat item */ +"This chat is protected by quantum resistant end-to-end encryption." = "Чат защищен квантово-устойчивым end-to-end шифрованием."; + /* notification title */ "this contact" = "этот контакт"; @@ -3539,9 +5022,6 @@ /* No comment provided by engineer. */ "This display name is invalid. Please choose another name." = "Ошибка имени профиля. Пожалуйста, выберите другое имя."; -/* No comment provided by engineer. */ -"This group has over %lld members, delivery receipts are not sent." = "В группе более %lld членов, отчёты о доставке выключены."; - /* No comment provided by engineer. */ "This group no longer exists." = "Эта группа больше не существует."; @@ -3551,9 +5031,18 @@ /* No comment provided by engineer. */ "This is your own SimpleX address!" = "Это ваш собственный адрес SimpleX!"; +/* No comment provided by engineer. */ +"This link was used with another mobile device, please create a new link on the desktop." = "Эта ссылка была использована на другом мобильном, пожалуйста, создайте новую ссылку на компьютере."; + +/* No comment provided by engineer. */ +"This message was deleted or not received yet." = "Это сообщение было удалено или еще не получено."; + /* No comment provided by engineer. */ "This setting applies to messages in your current chat profile **%@**." = "Эта настройка применяется к сообщениям в Вашем текущем профиле чата **%@**."; +/* No comment provided by engineer. */ +"Title" = "Заголовок"; + /* No comment provided by engineer. */ "To ask any questions and to receive updates:" = "Чтобы задать вопросы и получать уведомления о новых версиях,"; @@ -3567,7 +5056,7 @@ "To make a new connection" = "Чтобы соединиться"; /* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Чтобы защитить Вашу конфиденциальность, вместо ID пользователей, которые есть в других платформах, SimpleX использует ID для очередей сообщений, разные для каждого контакта."; +"To protect against your link being replaced, you can compare contact security codes." = "Чтобы защитить Вашу ссылку от замены, Вы можете сравнить код безопасности."; /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "Чтобы защитить Ваш часовой пояс, файлы картинок и голосовых сообщений используют UTC."; @@ -3575,24 +5064,60 @@ /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Чтобы защитить Вашу информацию, включите блокировку SimpleX Chat.\nВам будет нужно пройти аутентификацию для включения блокировки."; +/* No comment provided by engineer. */ +"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Чтобы защитить ваш IP адрес, приложение использует Ваши SMP серверы для конфиденциальной доставки сообщений."; + +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "Чтобы защитить Вашу конфиденциальность, SimpleX использует разные идентификаторы для каждого Вашeго контакта."; + +/* No comment provided by engineer. */ +"To receive" = "Для получения"; + +/* No comment provided by engineer. */ +"To record speech please grant permission to use Microphone." = "Для записи речи, пожалуйста, дайте разрешение на использование микрофона."; + +/* No comment provided by engineer. */ +"To record video please grant permission to use Camera." = "Для записи видео, пожалуйста, дайте разрешение на использование камеры."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Для записи голосового сообщения, пожалуйста разрешите доступ к микрофону."; /* No comment provided by engineer. */ "To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Чтобы показать Ваш скрытый профиль, введите его пароль в поле поиска на странице **Ваши профили чата**."; +/* No comment provided by engineer. */ +"To send" = "Для оправки"; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Для поддержки мгновенный доставки уведомлений данные чата должны быть перемещены."; +/* No comment provided by engineer. */ +"To use the servers of **%@**, accept conditions of use." = "Чтобы использовать серверы оператора **%@**, примите условия использования."; + /* No comment provided by engineer. */ "To verify end-to-end encryption with your contact compare (or scan) the code on your devices." = "Чтобы подтвердить end-to-end шифрование с Вашим контактом сравните (или сканируйте) код безопасности на Ваших устройствах."; +/* No comment provided by engineer. */ +"Toggle chat list:" = "Переключите список чатов:"; + /* No comment provided by engineer. */ "Toggle incognito when connecting." = "Установите режим Инкогнито при соединении."; +/* token status */ +"Token status: %@." = "Статус токена: %@."; + +/* No comment provided by engineer. */ +"Toolbar opacity" = "Прозрачность тулбара"; + +/* No comment provided by engineer. */ +"Total" = "Всего"; + /* No comment provided by engineer. */ "Transport isolation" = "Отдельные сессии для"; +/* No comment provided by engineer. */ +"Transport sessions" = "Транспортные сессии"; + /* No comment provided by engineer. */ "Trying to connect to the server used to receive messages from this contact (error: %@)." = "Устанавливается соединение с сервером, через который Вы получаете сообщения от этого контакта (ошибка: %@)."; @@ -3617,25 +5142,16 @@ /* No comment provided by engineer. */ "Unblock for all" = "Разблокировать для всех"; -/* No comment provided by engineer. */ -"Unblock member" = "Разблокировать члена группы"; - -/* No comment provided by engineer. */ -"Unblock member for all?" = "Разблокировать члена для всех?"; - -/* No comment provided by engineer. */ -"Unblock member?" = "Разблокировать члена группы?"; - /* rcv group event chat item */ "unblocked %@" = "%@ разблокирован"; -/* item status description */ -"Unexpected error: %@" = "Неожиданная ошибка: %@"; +/* No comment provided by engineer. */ +"Undelivered messages" = "Недоставленные сообщения"; /* No comment provided by engineer. */ "Unexpected migration state" = "Неожиданная ошибка при перемещении данных чата"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "Не избр."; /* No comment provided by engineer. */ @@ -3662,6 +5178,12 @@ /* No comment provided by engineer. */ "Unknown error" = "Неизвестная ошибка"; +/* No comment provided by engineer. */ +"unknown servers" = "неизвестные серверы"; + +/* alert title */ +"Unknown servers!" = "Неизвестные серверы!"; + /* No comment provided by engineer. */ "unknown status" = "неизвестный статус"; @@ -3683,21 +5205,18 @@ /* authentication reason */ "Unlock app" = "Разблокировать"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "Уведомлять"; /* No comment provided by engineer. */ +"unprotected" = "незащищённый"; + +/* swipe action */ "Unread" = "Не прочитано"; -/* No comment provided by engineer. */ -"Up to 100 last messages are sent to new members." = "До 100 последних сообщений отправляются новым членам."; - /* No comment provided by engineer. */ "Update" = "Обновить"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = "Обновить настройки .onion хостов?"; - /* No comment provided by engineer. */ "Update database passphrase" = "Поменять пароль"; @@ -3705,7 +5224,10 @@ "Update network settings?" = "Обновить настройки сети?"; /* No comment provided by engineer. */ -"Update transport isolation mode?" = "Обновить режим отдельных сессий?"; +"Update settings?" = "Обновить настройки?"; + +/* No comment provided by engineer. */ +"Updated conditions" = "Обновленные условия"; /* rcv group event chat item */ "updated group profile" = "обновил(а) профиль группы"; @@ -3717,23 +5239,44 @@ "Updating settings will re-connect the client to all servers." = "Обновление настроек приведет к сбросу и установке нового соединения со всеми серверами."; /* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "Обновление этих настроек приведет к сбросу и установке нового соединения со всеми серверами."; +"Upgrade and open chat" = "Обновить и открыть чат"; /* No comment provided by engineer. */ -"Upgrade and open chat" = "Обновить и открыть чат"; +"Upload errors" = "Ошибки загрузки"; + +/* No comment provided by engineer. */ +"Upload failed" = "Ошибка загрузки"; /* server test step */ "Upload file" = "Загрузка файла"; +/* No comment provided by engineer. */ +"Uploaded" = "Загружено"; + +/* No comment provided by engineer. */ +"Uploaded files" = "Отправленные файлы"; + +/* No comment provided by engineer. */ +"Uploading archive" = "Загрузка архива"; + /* No comment provided by engineer. */ "Use .onion hosts" = "Использовать .onion хосты"; +/* No comment provided by engineer. */ +"Use %@" = "Использовать %@"; + /* No comment provided by engineer. */ "Use chat" = "Использовать чат"; /* No comment provided by engineer. */ "Use current profile" = "Использовать активный профиль"; +/* No comment provided by engineer. */ +"Use for files" = "Использовать для файлов"; + +/* No comment provided by engineer. */ +"Use for messages" = "Использовать для сообщений"; + /* No comment provided by engineer. */ "Use for new connections" = "Использовать для новых соединений"; @@ -3749,17 +5292,41 @@ /* No comment provided by engineer. */ "Use only local notifications?" = "Использовать только локальные нотификации?"; +/* No comment provided by engineer. */ +"Use private routing with unknown servers when IP address is not protected." = "Использовать конфиденциальную доставку с неизвестными серверами, когда IP адрес не защищен."; + +/* No comment provided by engineer. */ +"Use private routing with unknown servers." = "Использовать конфиденциальную доставку с неизвестными серверами."; + /* No comment provided by engineer. */ "Use server" = "Использовать сервер"; +/* No comment provided by engineer. */ +"Use servers" = "Использовать серверы"; + /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Использовать серверы предосталенные SimpleX Chat?"; /* No comment provided by engineer. */ -"User profile" = "Профиль чата"; +"Use SOCKS proxy" = "Использовать SOCKS прокси"; /* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = "Для использования .onion хостов требуется совместимый VPN провайдер."; +"Use TCP port %@ when no port is specified." = "Использовать TCP-порт %@, когда порт не указан."; + +/* No comment provided by engineer. */ +"Use the app while in the call." = "Используйте приложение во время звонка."; + +/* No comment provided by engineer. */ +"Use the app with one hand." = "Используйте приложение одной рукой."; + +/* No comment provided by engineer. */ +"Use web port" = "Использовать веб-порт"; + +/* No comment provided by engineer. */ +"User selection" = "Выбор пользователя"; + +/* No comment provided by engineer. */ +"Username" = "Имя пользователя"; /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Используются серверы, предоставленные SimpleX Chat."; @@ -3782,6 +5349,12 @@ /* No comment provided by engineer. */ "Verify connections" = "Проверять соединения"; +/* No comment provided by engineer. */ +"Verify database passphrase" = "Проверка пароля базы данных"; + +/* No comment provided by engineer. */ +"Verify passphrase" = "Проверить пароль"; + /* No comment provided by engineer. */ "Verify security code" = "Подтвердить код безопасности"; @@ -3803,6 +5376,9 @@ /* No comment provided by engineer. */ "Via secure quantum resistant protocol." = "Через безопасный квантово-устойчивый протокол."; +/* No comment provided by engineer. */ +"video" = "видеозвонок"; + /* No comment provided by engineer. */ "Video call" = "Видеозвонок"; @@ -3818,9 +5394,15 @@ /* No comment provided by engineer. */ "Videos and files up to 1gb" = "Видео и файлы до 1гб"; +/* No comment provided by engineer. */ +"View conditions" = "Посмотреть условия"; + /* No comment provided by engineer. */ "View security code" = "Показать код безопасности"; +/* No comment provided by engineer. */ +"View updated conditions" = "Посмотреть измененные условия"; + /* chat feature */ "Visible history" = "Доступ к истории"; @@ -3834,7 +5416,10 @@ "Voice messages are prohibited in this chat." = "Голосовые сообщения запрещены в этом чате."; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "Голосовые сообщения запрещены в этой группе."; +"Voice messages are prohibited." = "Голосовые сообщения запрещены в этой группе."; + +/* No comment provided by engineer. */ +"Voice messages not allowed" = "Голосовые сообщения не разрешены"; /* No comment provided by engineer. */ "Voice messages prohibited!" = "Голосовые сообщения запрещены!"; @@ -3857,9 +5442,18 @@ /* No comment provided by engineer. */ "Waiting for video" = "Ожидание видео"; +/* No comment provided by engineer. */ +"Wallpaper accent" = "Рисунок обоев"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Фон обоев"; + /* No comment provided by engineer. */ "wants to connect to you!" = "хочет соединиться с Вами!"; +/* No comment provided by engineer. */ +"Warning: starting chat on multiple devices is not supported and will cause message delivery failures" = "Внимание: запуск чата на нескольких устройствах не поддерживается и приведет к сбоям доставки сообщений"; + /* No comment provided by engineer. */ "Warning: you may lose some data!" = "Предупреждение: Вы можете потерять какие то данные!"; @@ -3875,6 +5469,9 @@ /* No comment provided by engineer. */ "Welcome message" = "Приветственное сообщение"; +/* No comment provided by engineer. */ +"Welcome message is too long" = "Приветственное сообщение слишком длинное"; + /* No comment provided by engineer. */ "What's new" = "Новые функции"; @@ -3882,11 +5479,26 @@ "When available" = "Когда возможно"; /* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "Когда Вы получите запрос на соединение, Вы можете принять или отклонить его."; +"When connecting audio and video calls." = "Во время соединения аудио и видео звонков."; + +/* No comment provided by engineer. */ +"when IP hidden" = "когда IP защищен"; + +/* No comment provided by engineer. */ +"When more than one operator is enabled, none of them has metadata to learn who communicates with whom." = "Когда больше чем один оператор включен, ни один из них не видит метаданные, чтобы определить, кто соединен с кем."; /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Когда Вы соединены с контактом инкогнито, тот же самый инкогнито профиль будет использоваться для групп с этим контактом."; +/* No comment provided by engineer. */ +"WiFi" = "WiFi"; + +/* No comment provided by engineer. */ +"Will be enabled in direct chats!" = "Будет включено в прямых разговорах!"; + +/* No comment provided by engineer. */ +"Wired ethernet" = "Проводная сеть"; + /* No comment provided by engineer. */ "With encrypted files and media." = "С зашифрованными файлами и медиа."; @@ -3896,20 +5508,35 @@ /* No comment provided by engineer. */ "With reduced battery usage." = "С уменьшенным потреблением батареи."; +/* No comment provided by engineer. */ +"Without Tor or VPN, your IP address will be visible to file servers." = "Без Тора или ВПН, Ваш IP адрес будет доступен серверам файлов."; + +/* alert message */ +"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Без Тора или ВПН, Ваш IP адрес будет доступен этим серверам файлов: %@."; + /* No comment provided by engineer. */ "Wrong database passphrase" = "Неправильный пароль базы данных"; +/* snd error text */ +"Wrong key or unknown connection - most likely this connection is deleted." = "Неверный ключ или неизвестное соединение - скорее всего, это соединение удалено."; + +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Неверный ключ или неизвестный адрес блока файла - скорее всего, файл удален."; + /* No comment provided by engineer. */ "Wrong passphrase!" = "Неправильный пароль!"; /* No comment provided by engineer. */ -"XFTP servers" = "XFTP серверы"; +"XFTP server" = "XFTP сервер"; /* pref value */ "yes" = "да"; /* No comment provided by engineer. */ -"You" = "Вы"; +"you" = "Вы"; + +/* No comment provided by engineer. */ +"You **must not** use the same database on two devices." = "Вы **не должны** использовать одну и ту же базу данных на двух устройствах."; /* No comment provided by engineer. */ "You accepted connection" = "Вы приняли приглашение соединиться"; @@ -3923,6 +5550,9 @@ /* No comment provided by engineer. */ "You are already connected to %@." = "Вы уже соединены с контактом %@."; +/* No comment provided by engineer. */ +"You are already connected with %@." = "Вы уже соединены с %@."; + /* No comment provided by engineer. */ "You are already connecting to %@." = "Вы уже соединяетесь с %@."; @@ -3953,6 +5583,9 @@ /* No comment provided by engineer. */ "You are invited to group" = "Вы приглашены в группу"; +/* No comment provided by engineer. */ +"You are not connected to these servers. Private routing is used to deliver messages to them." = "Вы не подключены к этим серверам. Для доставки сообщений на них используется конфиденциальная доставка."; + /* No comment provided by engineer. */ "you are observer" = "только чтение сообщений"; @@ -3962,6 +5595,12 @@ /* No comment provided by engineer. */ "You can accept calls from lock screen, without device and app authentication." = "Вы можете принимать звонки на экране блокировки, без аутентификации."; +/* No comment provided by engineer. */ +"You can change it in Appearance settings." = "Вы можете изменить это в настройках Интерфейса."; + +/* No comment provided by engineer. */ +"You can configure servers via settings." = "Вы можете настроить серверы позже."; + /* No comment provided by engineer. */ "You can create it later" = "Вы можете создать его позже"; @@ -3971,6 +5610,9 @@ /* No comment provided by engineer. */ "You can enable them later via app Privacy & Security settings." = "Вы можете включить их позже в настройках Конфиденциальности."; +/* No comment provided by engineer. */ +"You can give another try." = "Вы можете попробовать еще раз."; + /* No comment provided by engineer. */ "You can hide or mute a user profile - swipe it to the right." = "Вы можете скрыть профиль или выключить уведомления - потяните его вправо."; @@ -3978,22 +5620,25 @@ "You can make it visible to your SimpleX contacts via Settings." = "Вы можете сделать его видимым для ваших контактов в SimpleX через Настройки."; /* notification body */ -"You can now send messages to %@" = "Вы теперь можете отправлять сообщения %@"; +"You can now chat with %@" = "Вы теперь можете общаться с %@"; + +/* No comment provided by engineer. */ +"You can send messages to %@ from Archived contacts." = "Вы можете отправлять сообщения %@ из Архивированных контактов."; + +/* No comment provided by engineer. */ +"You can set connection name, to remember who the link was shared with." = "Вы можете установить имя соединения, чтобы запомнить кому Вы отправили ссылку."; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "Вы можете установить просмотр уведомлений на экране блокировки в настройках."; -/* No comment provided by engineer. */ -"You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it." = "Вы можете поделиться ссылкой или QR кодом - через них можно присоединиться к группе. Вы сможете удалить ссылку, сохранив членов группы, которые через нее соединились."; - /* No comment provided by engineer. */ "You can share this address with your contacts to let them connect with **%@**." = "Вы можете поделиться этим адресом с Вашими контактами, чтобы они могли соединиться с **%@**."; /* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "Вы можете использовать Ваш адрес как ссылку или как QR код - кто угодно сможет соединиться с Вами."; +"You can start chat via app Settings / Database or by restarting the app" = "Вы можете запустить чат через Настройки приложения или перезапустив приложение."; /* No comment provided by engineer. */ -"You can start chat via app Settings / Database or by restarting the app" = "Вы можете запустить чат через Настройки приложения или перезапустив приложение."; +"You can still view conversation with %@ in the list of chats." = "Вы по-прежнему можете просмотреть разговор с %@ в списке чатов."; /* No comment provided by engineer. */ "You can turn on SimpleX Lock via Settings." = "Вы можете включить Блокировку SimpleX через Настройки."; @@ -4001,7 +5646,7 @@ /* No comment provided by engineer. */ "You can use markdown to format messages:" = "Вы можете форматировать сообщения:"; -/* No comment provided by engineer. */ +/* alert message */ "You can view invitation link again in connection details." = "Вы можете увидеть ссылку-приглашение снова открыв соединение."; /* No comment provided by engineer. */ @@ -4020,10 +5665,10 @@ "you changed role of %@ to %@" = "Вы поменяли роль члена %1$@ на: %2$@"; /* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "Вы определяете через какие серверы Вы **получаете сообщения**, Ваши контакты - серверы, которые Вы используете для отправки."; +"You could not be verified; please try again." = "Верификация не удалась; пожалуйста, попробуйте ещё раз."; /* No comment provided by engineer. */ -"You could not be verified; please try again." = "Верификация не удалась; пожалуйста, попробуйте ещё раз."; +"You decide who can connect." = "Вы определяете, кто может соединиться."; /* No comment provided by engineer. */ "You have already requested connection via this address!" = "Вы уже запросили соединение через этот адрес!"; @@ -4031,9 +5676,6 @@ /* No comment provided by engineer. */ "You have already requested connection!\nRepeat connection request?" = "Вы уже запросили соединение!\nПовторить запрос?"; -/* No comment provided by engineer. */ -"You have no chats" = "У Вас нет чатов"; - /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "Пароль не сохранен на устройстве — Вы будете должны ввести его при каждом запуске чата."; @@ -4043,15 +5685,21 @@ /* No comment provided by engineer. */ "You joined this group" = "Вы вступили в эту группу"; -/* No comment provided by engineer. */ -"You joined this group. Connecting to inviting group member." = "Вы вступили в эту группу. Устанавливается соединение с пригласившим членом группы."; - /* snd group event chat item */ "you left" = "Вы покинули группу"; +/* No comment provided by engineer. */ +"You may migrate the exported database." = "Вы можете мигрировать экспортированную базу данных."; + +/* No comment provided by engineer. */ +"You may save the exported archive." = "Вы можете сохранить экспортированный архив."; + /* No comment provided by engineer. */ "You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." = "Вы должны всегда использовать самую новую версию данных чата, ТОЛЬКО на одном устройстве, иначе Вы можете перестать получать сообщения от каких то контактов."; +/* No comment provided by engineer. */ +"You need to allow your contact to call to be able to call them." = "Чтобы включить звонки, разрешите их Вашему контакту."; + /* No comment provided by engineer. */ "You need to allow your contact to send voice messages to be able to send them." = "Чтобы включить отправку голосовых сообщений, разрешите их Вашему контакту."; @@ -4070,6 +5718,9 @@ /* chat list item description */ "you shared one-time link incognito" = "Вы создали ссылку инкогнито"; +/* token info */ +"You should receive notifications." = "Вы должны получать уведомления."; + /* snd group event chat item */ "you unblocked %@" = "Вы разблокировали %@"; @@ -4089,10 +5740,10 @@ "You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Вы будете аутентифицированы при запуске и возобновлении приложения, которое было 30 секунд в фоновом режиме."; /* No comment provided by engineer. */ -"You will connect to all group members." = "Вы соединитесь со всеми членами группы."; +"You will still receive calls and notifications from muted profiles when they are active." = "Вы все равно получите звонки и уведомления в профилях без звука, когда они активные."; /* No comment provided by engineer. */ -"You will still receive calls and notifications from muted profiles when they are active." = "Вы все равно получите звонки и уведомления в профилях без звука, когда они активные."; +"You will stop receiving messages from this chat. Chat history will be preserved." = "Вы прекратите получать сообщения в этом разговоре. История будет сохранена."; /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Вы перестанете получать сообщения от этой группы. История чата будет сохранена."; @@ -4109,9 +5760,6 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "Вы используете инкогнито профиль для этой группы - чтобы предотвратить раскрытие Вашего основного профиля, приглашать контакты не разрешено"; -/* No comment provided by engineer. */ -"Your %@ servers" = "Ваши %@ серверы"; - /* No comment provided by engineer. */ "Your calls" = "Ваши звонки"; @@ -4121,11 +5769,14 @@ /* No comment provided by engineer. */ "Your chat database is not encrypted - set passphrase to encrypt it." = "База данных НЕ зашифрована. Установите пароль, чтобы защитить Ваши данные."; +/* alert title */ +"Your chat preferences" = "Ваши настройки чата"; + /* No comment provided by engineer. */ "Your chat profiles" = "Ваши профили чата"; /* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Ваш контакт должен быть в сети чтобы установить соединение.\nВы можете отменить соединение и удалить контакт (и попробовать позже с другой ссылкой)."; +"Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." = "Соединение было перемещено на %@, но при смене профиля произошла неожиданная ошибка."; /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "Ваш контакт отправил файл, размер которого превышает максимальный размер (%@)."; @@ -4136,6 +5787,9 @@ /* No comment provided by engineer. */ "Your contacts will remain connected." = "Ваши контакты сохранятся."; +/* No comment provided by engineer. */ +"Your credentials may be sent unencrypted." = "Ваши учетные данные могут быть отправлены в незашифрованном виде."; + /* No comment provided by engineer. */ "Your current chat database will be DELETED and REPLACED with the imported one." = "Текущие данные Вашего чата будет УДАЛЕНЫ и ЗАМЕНЕНЫ импортированными."; @@ -4158,7 +5812,10 @@ "Your profile **%@** will be shared." = "Будет отправлен Ваш профиль **%@**."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Ваш профиль хранится на Вашем устройстве и отправляется только Вашим контактам.\nSimpleX серверы не могут получить доступ к Вашему профилю."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Ваш профиль хранится на Вашем устройстве и отправляется только Вашим контактам. SimpleX серверы не могут получить доступ к Вашему профилю."; + +/* alert message */ +"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Ваш профиль был изменен. Если вы сохраните его, обновленный профиль будет отправлен всем вашим контактам."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Ваш профиль, контакты и доставленные сообщения хранятся на Вашем устройстве."; @@ -4167,10 +5824,10 @@ "Your random profile" = "Случайный профиль"; /* No comment provided by engineer. */ -"Your server" = "Ваш сервер"; +"Your server address" = "Адрес Вашего сервера"; /* No comment provided by engineer. */ -"Your server address" = "Адрес Вашего сервера"; +"Your servers" = "Ваши серверы"; /* No comment provided by engineer. */ "Your settings" = "Настройки"; @@ -4178,9 +5835,3 @@ /* No comment provided by engineer. */ "Your SimpleX address" = "Ваш адрес SimpleX"; -/* No comment provided by engineer. */ -"Your SMP servers" = "Ваши SMP серверы"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "Ваши XFTP серверы"; - diff --git a/apps/ios/sounds/connecting_call.mp3 b/apps/ios/sounds/connecting_call.mp3 new file mode 100644 index 0000000000..fc425bab97 Binary files /dev/null and b/apps/ios/sounds/connecting_call.mp3 differ diff --git a/apps/ios/sounds/in_call.mp3 b/apps/ios/sounds/in_call.mp3 new file mode 100644 index 0000000000..1049be4462 Binary files /dev/null and b/apps/ios/sounds/in_call.mp3 differ diff --git a/apps/ios/th.lproj/Localizable.strings b/apps/ios/th.lproj/Localizable.strings index b20c2b4b60..57c0466eb9 100644 --- a/apps/ios/th.lproj/Localizable.strings +++ b/apps/ios/th.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (สามารถคัดลอกได้)"; @@ -25,24 +10,9 @@ /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- ข้อความเสียงนานสุด 5 นาที\n- เวลาที่กำหนดเองที่จะหายไป\n- ประวัติการแก้ไข"; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 มีสี!"; -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - -/* No comment provided by engineer. */ -")" = ")"; - /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[มีส่วนร่วม](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -52,9 +22,6 @@ /* No comment provided by engineer. */ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[ติดดาวบน GitHub](https://github.com/simplex-chat/simplex-chat)"; -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**เพิ่มผู้ติดต่อใหม่**: เพื่อสร้างคิวอาร์โค้ดแบบใช้ครั้งเดียวหรือลิงก์สำหรับผู้ติดต่อของคุณ"; - /* No comment provided by engineer. */ "**e2e encrypted** audio call" = "การโทรเสียงแบบ **encrypted จากต้นจนจบ**"; @@ -62,16 +29,16 @@ "**e2e encrypted** video call" = "**encrypted จากต้นจนจบ** การสนทนาทางวิดีโอ"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**เป็นส่วนตัวมากขึ้น**: ตรวจสอบข้อความใหม่ทุกๆ 20 นาที โทเค็นอุปกรณ์แชร์กับเซิร์ฟเวอร์ SimpleX Chat แต่ไม่ระบุจำนวนผู้ติดต่อหรือข้อความที่คุณมี"; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**เป็นส่วนตัวมากขึ้น**: ตรวจสอบข้อความใหม่ทุกๆ 20 นาที โทเค็นอุปกรณ์แชร์กับเซิร์ฟเวอร์ SimpleX Chat แต่ไม่ระบุจำนวนผู้ติดต่อหรือข้อความที่คุณมี"; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**ส่วนตัวที่สุด**: ไม่ใช้เซิร์ฟเวอร์การแจ้งเตือนของ SimpleX Chat ตรวจสอบข้อความเป็นระยะในพื้นหลัง (ขึ้นอยู่กับความถี่ที่คุณใช้แอป)"; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**ส่วนตัวที่สุด**: ไม่ใช้เซิร์ฟเวอร์การแจ้งเตือนของ SimpleX Chat ตรวจสอบข้อความเป็นระยะในพื้นหลัง (ขึ้นอยู่กับความถี่ที่คุณใช้แอป)"; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**โปรดทราบ**: คุณจะไม่สามารถกู้คืนหรือเปลี่ยนรหัสผ่านได้หากคุณทำรหัสผ่านหาย"; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**แนะนำ**: โทเค็นอุปกรณ์และการแจ้งเตือนจะถูกส่งไปยังเซิร์ฟเวอร์การแจ้งเตือนของ SimpleX Chat แต่ไม่ใช่เนื้อหาข้อความ ขนาด หรือผู้ที่ส่ง"; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**แนะนำ**: โทเค็นอุปกรณ์และการแจ้งเตือนจะถูกส่งไปยังเซิร์ฟเวอร์การแจ้งเตือนของ SimpleX Chat แต่ไม่ใช่เนื้อหาข้อความ ขนาด หรือผู้ที่ส่ง"; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**คำเตือน**: การแจ้งเตือนแบบพุชทันทีจำเป็นต้องบันทึกรหัสผ่านไว้ใน Keychain"; @@ -109,9 +76,6 @@ /* No comment provided by engineer. */ "%@ is verified" = "%@ ได้รับการตรวจสอบแล้ว"; -/* No comment provided by engineer. */ -"%@ servers" = "%@ เซิร์ฟเวอร์"; - /* notification title */ "%@ wants to connect!" = "%@ อยากเชื่อมต่อ!"; @@ -157,9 +121,6 @@ /* No comment provided by engineer. */ "%lld minutes" = "%lld นาที"; -/* No comment provided by engineer. */ -"%lld second(s)" = "%lld วินาที"; - /* No comment provided by engineer. */ "%lld seconds" = "%lld วินาที"; @@ -202,7 +163,8 @@ /* No comment provided by engineer. */ "0s" = "0s"; -/* time interval */ +/* delete after time +time interval */ "1 day" = "1 วัน"; /* time interval */ @@ -211,10 +173,12 @@ /* No comment provided by engineer. */ "1 minute" = "1 นาที"; -/* time interval */ +/* delete after time +time interval */ "1 month" = "1 เดือน"; -/* time interval */ +/* delete after time +time interval */ "1 week" = "1 สัปดาห์"; /* No comment provided by engineer. */ @@ -247,29 +211,22 @@ /* No comment provided by engineer. */ "Abort changing address?" = "ยกเลิกการเปลี่ยนที่อยู่?"; -/* No comment provided by engineer. */ -"About SimpleX" = "เกี่ยวกับ SimpleX"; - -/* No comment provided by engineer. */ -"About SimpleX address" = "เกี่ยวกับที่อยู่ SimpleX"; - /* No comment provided by engineer. */ "About SimpleX Chat" = "เกี่ยวกับ SimpleX Chat"; /* No comment provided by engineer. */ "above, then choose:" = "ด้านบน จากนั้นเลือก:"; -/* No comment provided by engineer. */ -"Accent color" = "สีเน้น"; - /* accept contact request via notification - accept incoming call via notification */ +accept incoming call via notification +swipe action */ "Accept" = "รับ"; /* notification body */ "Accept contact request from %@?" = "รับการขอติดต่อจาก %@?"; -/* accept contact request via notification */ +/* accept contact request via notification +swipe action */ "Accept incognito" = "ยอมรับโหมดไม่ระบุตัวตน"; /* call status */ @@ -278,14 +235,11 @@ /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "เพิ่มที่อยู่ลงในโปรไฟล์ของคุณ เพื่อให้ผู้ติดต่อของคุณสามารถแชร์กับผู้อื่นได้ การอัปเดตโปรไฟล์จะถูกส่งไปยังผู้ติดต่อของคุณ"; -/* No comment provided by engineer. */ -"Add preset servers" = "เพิ่มเซิร์ฟเวอร์ที่ตั้งไว้ล่วงหน้า"; - /* No comment provided by engineer. */ "Add profile" = "เพิ่มโปรไฟล์"; /* No comment provided by engineer. */ -"Add server…" = "เพิ่มเซิร์ฟเวอร์…"; +"Add server" = "เพิ่มเซิร์ฟเวอร์"; /* No comment provided by engineer. */ "Add servers by scanning QR codes." = "เพิ่มเซิร์ฟเวอร์โดยการสแกนรหัสคิวอาร์โค้ด"; @@ -407,6 +361,9 @@ /* No comment provided by engineer. */ "Answer call" = "รับสาย"; +/* No comment provided by engineer. */ +"Anybody can host servers." = "โปรโตคอลและโค้ดโอเพ่นซอร์ส – ใคร ๆ ก็สามารถเปิดใช้เซิร์ฟเวอร์ได้"; + /* No comment provided by engineer. */ "App build: %@" = "รุ่นแอป: %@"; @@ -527,7 +484,8 @@ /* No comment provided by engineer. */ "Can't invite contacts!" = "ไม่สามารถเชิญผู้ติดต่อได้!"; -/* No comment provided by engineer. */ +/* alert action +alert button */ "Cancel" = "ยกเลิก"; /* feature offered item */ @@ -536,7 +494,7 @@ /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "ไม่สามารถเข้าถึง keychain เพื่อบันทึกรหัสผ่านฐานข้อมูล"; -/* No comment provided by engineer. */ +/* alert title */ "Cannot receive file" = "ไม่สามารถรับไฟล์ได้"; /* No comment provided by engineer. */ @@ -567,7 +525,7 @@ "Change self-destruct mode" = "เปลี่ยนโหมดทําลายตัวเอง"; /* authentication reason - set passcode view */ +set passcode view */ "Change self-destruct passcode" = "เปลี่ยนรหัสผ่านแบบทำลายตัวเอง"; /* chat item text */ @@ -585,9 +543,6 @@ /* chat item text */ "changing address…" = "กำลังเปลี่ยนที่อยู่…"; -/* No comment provided by engineer. */ -"Chat archive" = "ที่เก็บแชทถาวร"; - /* No comment provided by engineer. */ "Chat console" = "คอนโซลแชท"; @@ -610,9 +565,12 @@ "Chat preferences" = "ค่ากําหนดในการแชท"; /* No comment provided by engineer. */ -"Chats" = "แชท"; +"Chat profile" = "โปรไฟล์ผู้ใช้"; /* No comment provided by engineer. */ +"Chats" = "แชท"; + +/* alert title */ "Check server address and try again." = "ตรวจสอบที่อยู่เซิร์ฟเวอร์แล้วลองอีกครั้ง"; /* No comment provided by engineer. */ @@ -624,7 +582,7 @@ /* No comment provided by engineer. */ "Choose from library" = "เลือกจากอัลบั้ม"; -/* No comment provided by engineer. */ +/* swipe action */ "Clear" = "ลบ"; /* No comment provided by engineer. */ @@ -639,9 +597,6 @@ /* No comment provided by engineer. */ "colored" = "มีสี"; -/* No comment provided by engineer. */ -"Colors" = "สี"; - /* server test step */ "Compare file" = "เปรียบเทียบไฟล์"; @@ -705,7 +660,7 @@ /* No comment provided by engineer. */ "Connecting server… (error: %@)" = "กำลังเชื่อมต่อกับเซิร์ฟเวอร์... (ข้อผิดพลาด: %@)"; -/* chat list item title */ +/* No comment provided by engineer. */ "connecting…" = "กำลังเชื่อมต่อ…"; /* No comment provided by engineer. */ @@ -747,9 +702,6 @@ /* notification */ "Contact is connected" = "เชื่อมต่อกับผู้ติดต่อแล้ว"; -/* No comment provided by engineer. */ -"Contact is not connected yet!" = "ผู้ติดต่อยังไม่ได้เชื่อมต่อ!"; - /* No comment provided by engineer. */ "Contact name" = "ชื่อผู้ติดต่อ"; @@ -765,7 +717,7 @@ /* No comment provided by engineer. */ "Continue" = "ดำเนินการต่อ"; -/* chat item action */ +/* No comment provided by engineer. */ "Copy" = "คัดลอก"; /* No comment provided by engineer. */ @@ -774,9 +726,6 @@ /* No comment provided by engineer. */ "Create" = "สร้าง"; -/* No comment provided by engineer. */ -"Create an address to let people connect with you." = "สร้างที่อยู่เพื่อให้ผู้อื่นเชื่อมต่อกับคุณ"; - /* server test step */ "Create file" = "สร้างไฟล์"; @@ -798,9 +747,6 @@ /* No comment provided by engineer. */ "Create your profile" = "สร้างโปรไฟล์ของคุณ"; -/* No comment provided by engineer. */ -"Created on %@" = "สร้างเมื่อ %@"; - /* No comment provided by engineer. */ "creator" = "ผู้สร้าง"; @@ -888,7 +834,8 @@ /* message decrypt error item */ "Decryption error" = "ข้อผิดพลาดในการ decrypt"; -/* pref value */ +/* delete after time +pref value */ "default (%@)" = "ค่าเริ่มต้น (%@)"; /* No comment provided by engineer. */ @@ -897,7 +844,8 @@ /* No comment provided by engineer. */ "default (yes)" = "ค่าเริ่มต้น (ใช่)"; -/* chat item action */ +/* alert action +swipe action */ "Delete" = "ลบ"; /* No comment provided by engineer. */ @@ -912,12 +860,6 @@ /* No comment provided by engineer. */ "Delete all files" = "ลบไฟล์ทั้งหมด"; -/* No comment provided by engineer. */ -"Delete archive" = "ลบที่เก็บถาวร"; - -/* No comment provided by engineer. */ -"Delete chat archive?" = "ลบที่เก็บแชทถาวร?"; - /* No comment provided by engineer. */ "Delete chat profile" = "ลบโปรไฟล์แชท"; @@ -930,9 +872,6 @@ /* No comment provided by engineer. */ "Delete contact" = "ลบผู้ติดต่อ"; -/* No comment provided by engineer. */ -"Delete Contact" = "ลบผู้ติดต่อ"; - /* No comment provided by engineer. */ "Delete database" = "ลบฐานข้อมูล"; @@ -972,7 +911,7 @@ /* No comment provided by engineer. */ "Delete message?" = "ลบข้อความ?"; -/* No comment provided by engineer. */ +/* alert button */ "Delete messages" = "ลบข้อความ"; /* No comment provided by engineer. */ @@ -984,9 +923,6 @@ /* No comment provided by engineer. */ "Delete old database?" = "ลบฐานข้อมูลเก่า?"; -/* No comment provided by engineer. */ -"Delete pending connection" = "ลบการเชื่อมต่อที่รอดำเนินการ"; - /* No comment provided by engineer. */ "Delete pending connection?" = "ลบการเชื่อมต่อที่รอดำเนินการหรือไม่?"; @@ -1048,7 +984,7 @@ "Direct messages" = "ข้อความโดยตรง"; /* No comment provided by engineer. */ -"Direct messages between members are prohibited in this group." = "ข้อความโดยตรงระหว่างสมาชิกเป็นสิ่งต้องห้ามในกลุ่มนี้"; +"Direct messages between members are prohibited." = "ข้อความโดยตรงระหว่างสมาชิกเป็นสิ่งต้องห้ามในกลุ่มนี้"; /* No comment provided by engineer. */ "Disable (keep overrides)" = "ปิดใช้งาน (เก็บการแทนที่)"; @@ -1069,7 +1005,7 @@ "Disappearing messages are prohibited in this chat." = "ข้อความที่จะหายไปหลังเวลาที่กําหนด (disappearing message) เป็นสิ่งต้องห้ามในแชทนี้"; /* No comment provided by engineer. */ -"Disappearing messages are prohibited in this group." = "ข้อความที่จะหายไปหลังเวลาที่กําหนด (disappearing message) เป็นสิ่งต้องห้ามในกลุ่มนี้"; +"Disappearing messages are prohibited." = "ข้อความที่จะหายไปหลังเวลาที่กําหนด (disappearing message) เป็นสิ่งต้องห้ามในกลุ่มนี้"; /* No comment provided by engineer. */ "Disappears at" = "หายไปที่"; @@ -1125,7 +1061,7 @@ /* No comment provided by engineer. */ "Enable (keep overrides)" = "เปิดใช้งาน (เก็บการแทนที่)"; -/* No comment provided by engineer. */ +/* alert title */ "Enable automatic message deletion?" = "เปิดใช้งานการลบข้อความอัตโนมัติ?"; /* No comment provided by engineer. */ @@ -1254,9 +1190,6 @@ /* No comment provided by engineer. */ "Error accepting contact request" = "เกิดข้อผิดพลาดในการรับคำขอติดต่อ"; -/* No comment provided by engineer. */ -"Error accessing database file" = "เกิดข้อผิดพลาดในการเข้าถึงไฟล์ฐานข้อมูล"; - /* No comment provided by engineer. */ "Error adding member(s)" = "เกิดข้อผิดพลาดในการเพิ่มสมาชิก"; @@ -1290,9 +1223,6 @@ /* No comment provided by engineer. */ "Error deleting connection" = "เกิดข้อผิดพลาดในการลบการเชื่อมต่อ"; -/* No comment provided by engineer. */ -"Error deleting contact" = "เกิดข้อผิดพลาดในการลบผู้ติดต่อ"; - /* No comment provided by engineer. */ "Error deleting database" = "เกิดข้อผิดพลาดในการลบฐานข้อมูล"; @@ -1323,18 +1253,12 @@ /* No comment provided by engineer. */ "Error joining group" = "เกิดข้อผิดพลาดในการเข้าร่วมกลุ่ม"; -/* No comment provided by engineer. */ -"Error loading %@ servers" = "โหลดเซิร์ฟเวอร์ %@ ผิดพลาด"; - -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "เกิดข้อผิดพลาดในการรับไฟล์"; /* No comment provided by engineer. */ "Error removing member" = "เกิดข้อผิดพลาดในการลบสมาชิก"; -/* No comment provided by engineer. */ -"Error saving %@ servers" = "เกิดข้อผิดพลาดในการบันทึกเซิร์ฟเวอร์ %@"; - /* No comment provided by engineer. */ "Error saving group profile" = "เกิดข้อผิดพลาดในการบันทึกโปรไฟล์กลุ่ม"; @@ -1365,7 +1289,7 @@ /* No comment provided by engineer. */ "Error stopping chat" = "เกิดข้อผิดพลาดในการหยุดแชท"; -/* No comment provided by engineer. */ +/* alertTitle */ "Error switching profile!" = "เกิดข้อผิดพลาดในการเปลี่ยนโปรไฟล์!"; /* No comment provided by engineer. */ @@ -1386,7 +1310,9 @@ /* No comment provided by engineer. */ "Error: " = "ผิดพลาด: "; -/* No comment provided by engineer. */ +/* alert message +file error text +snd error text */ "Error: %@" = "ข้อผิดพลาด: % @"; /* No comment provided by engineer. */ @@ -1419,7 +1345,7 @@ /* No comment provided by engineer. */ "Fast and no wait until the sender is online!" = "รวดเร็วและไม่ต้องรอจนกว่าผู้ส่งจะออนไลน์!"; -/* No comment provided by engineer. */ +/* swipe action */ "Favorite" = "ที่ชอบ"; /* No comment provided by engineer. */ @@ -1441,7 +1367,7 @@ "Files and media" = "ไฟล์และสื่อ"; /* No comment provided by engineer. */ -"Files and media are prohibited in this group." = "ไฟล์และสื่อเป็นสิ่งต้องห้ามในกลุ่มนี้"; +"Files and media are prohibited." = "ไฟล์และสื่อเป็นสิ่งต้องห้ามในกลุ่มนี้"; /* No comment provided by engineer. */ "Files and media prohibited!" = "ไฟล์และสื่อต้องห้าม!"; @@ -1485,9 +1411,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "ชื่อเต็ม (ไม่บังคับ)"; -/* No comment provided by engineer. */ -"Full name:" = "ชื่อเต็ม:"; - /* No comment provided by engineer. */ "Fully re-implemented - work in background!" = "ดำเนินการใหม่อย่างสมบูรณ์ - ทำงานในพื้นหลัง!"; @@ -1527,24 +1450,6 @@ /* No comment provided by engineer. */ "Group links" = "ลิงค์กลุ่ม"; -/* No comment provided by engineer. */ -"Group members can add message reactions." = "สมาชิกกลุ่มสามารถเพิ่มการแสดงปฏิกิริยาต่อข้อความได้"; - -/* No comment provided by engineer. */ -"Group members can irreversibly delete sent messages. (24 hours)" = "สมาชิกกลุ่มสามารถลบข้อความที่ส่งแล้วอย่างถาวร"; - -/* No comment provided by engineer. */ -"Group members can send direct messages." = "สมาชิกกลุ่มสามารถส่งข้อความโดยตรงได้"; - -/* No comment provided by engineer. */ -"Group members can send disappearing messages." = "สมาชิกกลุ่มสามารถส่งข้อความที่จะหายไปหลังจากเวลาที่กำหนดหลังการอ่าน (disappearing messages) ได้"; - -/* No comment provided by engineer. */ -"Group members can send files and media." = "สมาชิกกลุ่มสามารถส่งไฟล์และสื่อ"; - -/* No comment provided by engineer. */ -"Group members can send voice messages." = "สมาชิกกลุ่มสามารถส่งข้อความเสียง"; - /* notification */ "Group message:" = "ข้อความกลุ่ม:"; @@ -1602,9 +1507,6 @@ /* time unit */ "hours" = "ชั่วโมง"; -/* No comment provided by engineer. */ -"How it works" = "มันทำงานอย่างไร"; - /* No comment provided by engineer. */ "How SimpleX works" = "วิธีการ SimpleX ทํางานอย่างไร"; @@ -1645,7 +1547,7 @@ "Immediately" = "โดยทันที"; /* No comment provided by engineer. */ -"Immune to spam and abuse" = "มีภูมิคุ้มกันต่อสแปมและการละเมิด"; +"Immune to spam" = "มีภูมิคุ้มกันต่อสแปมและการละเมิด"; /* No comment provided by engineer. */ "Import" = "นำเข้า"; @@ -1711,10 +1613,10 @@ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "ติดตั้ง [SimpleX Chat สำหรับเทอร์มินัล](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"Instant push notifications will be hidden!\n" = "การแจ้งเตือนโดยทันทีจะถูกซ่อน!\n"; +"Instant" = "ทันที"; /* No comment provided by engineer. */ -"Instantly" = "ทันที"; +"Instant push notifications will be hidden!\n" = "การแจ้งเตือนโดยทันทีจะถูกซ่อน!\n"; /* No comment provided by engineer. */ "Interface" = "อินเตอร์เฟซ"; @@ -1731,7 +1633,7 @@ /* invalid chat item */ "invalid data" = "ข้อมูลไม่ถูกต้อง"; -/* No comment provided by engineer. */ +/* alert title */ "Invalid server address!" = "ที่อยู่เซิร์ฟเวอร์ไม่ถูกต้อง!"; /* No comment provided by engineer. */ @@ -1774,7 +1676,7 @@ "Irreversible message deletion is prohibited in this chat." = "ไม่สามารถลบข้อความแบบแก้ไขไม่ได้ในแชทนี้"; /* No comment provided by engineer. */ -"Irreversible message deletion is prohibited in this group." = "การลบข้อความแบบแก้ไขไม่ได้เป็นสิ่งที่ห้ามในกลุ่มนี้"; +"Irreversible message deletion is prohibited." = "การลบข้อความแบบแก้ไขไม่ได้เป็นสิ่งที่ห้ามในกลุ่มนี้"; /* No comment provided by engineer. */ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "อนุญาตให้มีการเชื่อมต่อที่ไม่ระบุตัวตนจำนวนมากโดยไม่มีข้อมูลที่ใช้ร่วมกันระหว่างกันในโปรไฟล์การแชทเดียว"; @@ -1797,7 +1699,7 @@ /* No comment provided by engineer. */ "Japanese interface" = "อินเทอร์เฟซภาษาญี่ปุ่น"; -/* No comment provided by engineer. */ +/* swipe action */ "Join" = "เข้าร่วม"; /* No comment provided by engineer. */ @@ -1827,7 +1729,7 @@ /* No comment provided by engineer. */ "Learn more" = "ศึกษาเพิ่มเติม"; -/* No comment provided by engineer. */ +/* swipe action */ "Leave" = "ออกจาก"; /* No comment provided by engineer. */ @@ -1857,9 +1759,6 @@ /* No comment provided by engineer. */ "Live messages" = "ข้อความสด"; -/* No comment provided by engineer. */ -"Local" = "ในเครื่อง"; - /* No comment provided by engineer. */ "Local name" = "ชื่อภายในเครื่องเท่านั้น"; @@ -1872,24 +1771,15 @@ /* No comment provided by engineer. */ "Lock mode" = "โหมดล็อค"; -/* No comment provided by engineer. */ -"Make a private connection" = "สร้างการเชื่อมต่อแบบส่วนตัว"; - /* No comment provided by engineer. */ "Make one message disappear" = "ทำให้ข้อความหายไปหนึ่งข้อความ"; /* No comment provided by engineer. */ "Make profile private!" = "ทำให้โปรไฟล์เป็นส่วนตัว!"; -/* No comment provided by engineer. */ -"Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." = "ตรวจสอบให้แน่ใจว่าที่อยู่เซิร์ฟเวอร์ %@ อยู่ในรูปแบบที่ถูกต้อง แยกบรรทัดและไม่ซ้ำกัน (%@)"; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "ตรวจสอบให้แน่ใจว่าที่อยู่เซิร์ฟเวอร์ WebRTC ICE อยู่ในรูปแบบที่ถูกต้อง แยกบรรทัดและไม่ซ้ำกัน"; -/* No comment provided by engineer. */ -"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "หลายคนถามว่า: *หาก SimpleX ไม่มีตัวระบุผู้ใช้ จะส่งข้อความได้อย่างไร?*"; - /* No comment provided by engineer. */ "Mark deleted for everyone" = "ทำเครื่องหมายว่าลบแล้วสำหรับทุกคน"; @@ -1926,6 +1816,24 @@ /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "สมาชิกจะถูกลบออกจากกลุ่ม - ไม่สามารถยกเลิกได้!"; +/* No comment provided by engineer. */ +"Members can add message reactions." = "สมาชิกกลุ่มสามารถเพิ่มการแสดงปฏิกิริยาต่อข้อความได้"; + +/* No comment provided by engineer. */ +"Members can irreversibly delete sent messages. (24 hours)" = "สมาชิกกลุ่มสามารถลบข้อความที่ส่งแล้วอย่างถาวร"; + +/* No comment provided by engineer. */ +"Members can send direct messages." = "สมาชิกกลุ่มสามารถส่งข้อความโดยตรงได้"; + +/* No comment provided by engineer. */ +"Members can send disappearing messages." = "สมาชิกกลุ่มสามารถส่งข้อความที่จะหายไปหลังจากเวลาที่กำหนดหลังการอ่าน (disappearing messages) ได้"; + +/* No comment provided by engineer. */ +"Members can send files and media." = "สมาชิกกลุ่มสามารถส่งไฟล์และสื่อ"; + +/* No comment provided by engineer. */ +"Members can send voice messages." = "สมาชิกกลุ่มสามารถส่งข้อความเสียง"; + /* item status text */ "Message delivery error" = "ข้อผิดพลาดในการส่งข้อความ"; @@ -1942,7 +1850,7 @@ "Message reactions are prohibited in this chat." = "ห้ามแสดงปฏิกิริยาบนข้อความในแชทนี้"; /* No comment provided by engineer. */ -"Message reactions are prohibited in this group." = "ปฏิกิริยาบนข้อความเป็นสิ่งต้องห้ามในกลุ่มนี้"; +"Message reactions are prohibited." = "ปฏิกิริยาบนข้อความเป็นสิ่งต้องห้ามในกลุ่มนี้"; /* notification */ "message received" = "ข้อความที่ได้รับ"; @@ -1969,7 +1877,7 @@ "Migration is completed" = "การโยกย้ายเสร็จสมบูรณ์"; /* No comment provided by engineer. */ -"Migrations: %@" = "การย้ายข้อมูล: %@"; +"Migrations:" = "การย้ายข้อมูล"; /* time unit */ "minutes" = "นาที"; @@ -1998,19 +1906,16 @@ /* No comment provided by engineer. */ "More improvements are coming soon!" = "การปรับปรุงเพิ่มเติมกำลังจะมาเร็ว ๆ นี้!"; -/* No comment provided by engineer. */ -"Most likely this contact has deleted the connection with you." = "เป็นไปได้มากว่าผู้ติดต่อนี้ได้ลบการเชื่อมต่อกับคุณ"; - /* No comment provided by engineer. */ "Multiple chat profiles" = "โปรไฟล์การแชทหลายรายการ"; -/* No comment provided by engineer. */ +/* notification label action */ "Mute" = "ปิดเสียง"; /* No comment provided by engineer. */ "Muted when inactive!" = "ปิดเสียงเมื่อไม่ได้ใช้งาน!"; -/* No comment provided by engineer. */ +/* swipe action */ "Name" = "ชื่อ"; /* No comment provided by engineer. */ @@ -2022,7 +1927,7 @@ /* No comment provided by engineer. */ "Network status" = "สถานะเครือข่าย"; -/* No comment provided by engineer. */ +/* delete after time */ "never" = "ไม่เคย"; /* notification */ @@ -2031,9 +1936,6 @@ /* notification */ "New contact:" = "คำขอติดต่อใหม่:"; -/* No comment provided by engineer. */ -"New database archive" = "ฐานข้อมูลใหม่สำหรับการเก็บถาวร"; - /* No comment provided by engineer. */ "New display name" = "ชื่อที่แสดงใหม่"; @@ -2088,12 +1990,18 @@ /* No comment provided by engineer. */ "No permission to record voice message" = "ไม่อนุญาตให้บันทึกข้อความเสียง"; +/* No comment provided by engineer. */ +"No push server" = "ในเครื่อง"; + /* No comment provided by engineer. */ "No received or sent files" = "ไม่มีไฟล์ที่ได้รับหรือส่ง"; /* copied message info in history */ "no text" = "ไม่มีข้อความ"; +/* No comment provided by engineer. */ +"No user identifiers." = "แพลตฟอร์มแรกที่ไม่มีตัวระบุผู้ใช้ - ถูกออกแบบให้เป็นส่วนตัว"; + /* No comment provided by engineer. */ "Notifications" = "การแจ้งเตือน"; @@ -2107,11 +2015,11 @@ "observer" = "ผู้สังเกตการณ์"; /* enabled status - group pref value - time to disappear */ +group pref value +time to disappear */ "off" = "ปิด"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "ปิด"; /* feature offered item */ @@ -2120,15 +2028,12 @@ /* feature offered item */ "offered %@: %@" = "เสนอแล้ว %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "ตกลง"; /* No comment provided by engineer. */ "Old database" = "ฐานข้อมูลเก่า"; -/* No comment provided by engineer. */ -"Old database archive" = "คลังฐานข้อมูลเก่า"; - /* group pref value */ "on" = "เปิด"; @@ -2136,16 +2041,16 @@ "One-time invitation link" = "ลิงก์คำเชิญแบบใช้ครั้งเดียว"; /* No comment provided by engineer. */ -"Onion hosts will be required for connection. Requires enabling VPN." = "จำเป็นต้องมีโฮสต์หัวหอมสำหรับการเชื่อมต่อ ต้องเปิดใช้งาน VPN สำหรับการเชื่อมต่อ"; +"Onion hosts will be **required** for connection.\nRequires compatible VPN." = "จำเป็นต้องมีโฮสต์หัวหอมสำหรับการเชื่อมต่อ ต้องเปิดใช้งาน VPN สำหรับการเชื่อมต่อ"; /* No comment provided by engineer. */ -"Onion hosts will be used when available. Requires enabling VPN." = "จำเป็นต้องมีโฮสต์หัวหอมสำหรับการเชื่อมต่อ ต้องเปิดใช้งาน VPN สำหรับการเชื่อมต่อ"; +"Onion hosts will be used when available.\nRequires compatible VPN." = "จำเป็นต้องมีโฮสต์หัวหอมสำหรับการเชื่อมต่อ ต้องเปิดใช้งาน VPN สำหรับการเชื่อมต่อ"; /* No comment provided by engineer. */ "Onion hosts will not be used." = "โฮสต์หัวหอมจะไม่ถูกใช้"; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "เฉพาะอุปกรณ์ไคลเอนต์เท่านั้นที่จัดเก็บโปรไฟล์ผู้ใช้ ผู้ติดต่อ กลุ่ม และข้อความที่ส่งด้วย **การเข้ารหัส encrypt แบบ 2 ชั้น**"; +"Only client devices store user profiles, contacts, groups, and messages." = "เฉพาะอุปกรณ์ไคลเอนต์เท่านั้นที่จัดเก็บโปรไฟล์ผู้ใช้ ผู้ติดต่อ กลุ่ม และข้อความที่ส่งด้วย **การเข้ารหัส encrypt แบบ 2 ชั้น**"; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "เฉพาะเจ้าของกลุ่มเท่านั้นที่สามารถเปลี่ยนค่ากําหนดลักษณะกลุ่มได้"; @@ -2195,12 +2100,6 @@ /* No comment provided by engineer. */ "Open Settings" = "เปิดการตั้งค่า"; -/* authentication reason */ -"Open user profiles" = "เปิดโปรไฟล์ผู้ใช้"; - -/* No comment provided by engineer. */ -"Open-source protocol and code – anybody can run the servers." = "โปรโตคอลและโค้ดโอเพ่นซอร์ส – ใคร ๆ ก็สามารถเปิดใช้เซิร์ฟเวอร์ได้"; - /* member role */ "owner" = "เจ้าของ"; @@ -2229,10 +2128,7 @@ "peer-to-peer" = "เพื่อนต่อเพื่อน"; /* No comment provided by engineer. */ -"People can connect to you only via the links you share." = "ผู้คนสามารถเชื่อมต่อกับคุณผ่านลิงก์ที่คุณแบ่งปันเท่านั้น"; - -/* No comment provided by engineer. */ -"Periodically" = "เป็นระยะๆ"; +"Periodic" = "เป็นระยะๆ"; /* message decrypt error item */ "Permanent decryption error" = "ข้อผิดพลาดในการถอดรหัสอย่างถาวร"; @@ -2288,9 +2184,6 @@ /* No comment provided by engineer. */ "Preserve the last message draft, with attachments." = "เก็บข้อความที่ร่างไว้ล่าสุดพร้อมไฟล์แนบ"; -/* No comment provided by engineer. */ -"Preset server" = "เซิร์ฟเวอร์ที่ตั้งไว้ล่วงหน้า"; - /* No comment provided by engineer. */ "Preset server address" = "ที่อยู่เซิร์ฟเวอร์ที่ตั้งไว้ล่วงหน้า"; @@ -2315,7 +2208,7 @@ /* No comment provided by engineer. */ "Profile password" = "รหัสผ่านโปรไฟล์"; -/* No comment provided by engineer. */ +/* alert message */ "Profile update will be sent to your contacts." = "การอัปเดตโปรไฟล์จะถูกส่งไปยังผู้ติดต่อของคุณ"; /* No comment provided by engineer. */ @@ -2363,14 +2256,14 @@ /* chat item menu */ "React…" = "ตอบสนอง…"; -/* No comment provided by engineer. */ +/* swipe action */ "Read" = "อ่าน"; /* No comment provided by engineer. */ "Read more" = "อ่านเพิ่มเติม"; /* No comment provided by engineer. */ -"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "อ่านเพิ่มเติมใน[คู่มือผู้ใช้](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)"; +"Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)." = "อ่านเพิ่มเติมใน[คู่มือผู้ใช้](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses)"; /* No comment provided by engineer. */ "Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "อ่านเพิ่มเติมใน[คู่มือผู้ใช้](https://simplex.chat/docs/guide/readme.html#connect-to-friends)"; @@ -2378,9 +2271,6 @@ /* No comment provided by engineer. */ "Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "อ่านเพิ่มเติมใน[พื้นที่เก็บข้อมูล GitHub](https://github.com/simplex-chat/simplex-chat#readme)"; -/* No comment provided by engineer. */ -"Read more in our GitHub repository." = "อ่านเพิ่มเติมในที่เก็บ GitHub ของเรา"; - /* No comment provided by engineer. */ "received answer…" = "ได้รับคำตอบ…"; @@ -2426,7 +2316,8 @@ /* No comment provided by engineer. */ "Reduced battery usage" = "ลดการใช้แบตเตอรี่"; -/* reject incoming call via notification */ +/* reject incoming call via notification +swipe action */ "Reject" = "ปฏิเสธ"; /* No comment provided by engineer. */ @@ -2507,9 +2398,6 @@ /* chat item action */ "Reveal" = "เปิดเผย"; -/* No comment provided by engineer. */ -"Revert" = "เปลี่ยนกลับ"; - /* No comment provided by engineer. */ "Revoke" = "ถอน"; @@ -2525,13 +2413,14 @@ /* No comment provided by engineer. */ "Run chat" = "เรียกใช้แชท"; -/* chat item action */ +/* alert button +chat item action */ "Save" = "บันทึก"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "บันทึก (และแจ้งผู้ติดต่อ)"; -/* No comment provided by engineer. */ +/* alert button */ "Save and notify contact" = "บันทึกและแจ้งผู้ติดต่อ"; /* No comment provided by engineer. */ @@ -2540,12 +2429,6 @@ /* No comment provided by engineer. */ "Save and update group profile" = "บันทึกและอัปเดตโปรไฟล์กลุ่ม"; -/* No comment provided by engineer. */ -"Save archive" = "บันทึกไฟล์เก็บถาวร"; - -/* No comment provided by engineer. */ -"Save auto-accept settings" = "บันทึกการตั้งค่าการยอมรับอัตโนมัติ"; - /* No comment provided by engineer. */ "Save group profile" = "บันทึกโปรไฟล์กลุ่ม"; @@ -2555,7 +2438,7 @@ /* No comment provided by engineer. */ "Save passphrase in Keychain" = "บันทึกข้อความรหัสผ่านใน Keychain"; -/* No comment provided by engineer. */ +/* alert title */ "Save preferences?" = "บันทึกการตั้งค่า?"; /* No comment provided by engineer. */ @@ -2564,12 +2447,9 @@ /* No comment provided by engineer. */ "Save servers" = "บันทึกเซิร์ฟเวอร์"; -/* No comment provided by engineer. */ +/* alert title */ "Save servers?" = "บันทึกเซิร์ฟเวอร์?"; -/* No comment provided by engineer. */ -"Save settings?" = "บันทึกการตั้งค่า?"; - /* No comment provided by engineer. */ "Save welcome message?" = "บันทึกข้อความต้อนรับ?"; @@ -2612,7 +2492,7 @@ /* chat item text */ "security code changed" = "เปลี่ยนรหัสความปลอดภัยแล้ว"; -/* No comment provided by engineer. */ +/* chat item action */ "Select" = "เลือก"; /* No comment provided by engineer. */ @@ -2636,9 +2516,6 @@ /* No comment provided by engineer. */ "Send delivery receipts to" = "ส่งใบเสร็จรับการจัดส่งข้อความไปที่"; -/* No comment provided by engineer. */ -"Send direct message" = "ส่งข้อความโดยตรง"; - /* No comment provided by engineer. */ "Send disappearing message" = "ส่งข้อความแบบที่หายไป"; @@ -2651,9 +2528,6 @@ /* No comment provided by engineer. */ "Send notifications" = "ส่งการแจ้งเตือน"; -/* No comment provided by engineer. */ -"Send notifications:" = "ส่งการแจ้งเตือน:"; - /* No comment provided by engineer. */ "Send questions and ideas" = "ส่งคําถามและความคิด"; @@ -2663,7 +2537,7 @@ /* No comment provided by engineer. */ "Send them from gallery or custom keyboards." = "ส่งจากแกลเลอรีหรือแป้นพิมพ์แบบกำหนดเอง"; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "ผู้ส่งยกเลิกการโอนไฟล์"; /* No comment provided by engineer. */ @@ -2741,7 +2615,8 @@ /* No comment provided by engineer. */ "Settings" = "การตั้งค่า"; -/* chat item action */ +/* alert action +chat item action */ "Share" = "แชร์"; /* No comment provided by engineer. */ @@ -2750,7 +2625,7 @@ /* No comment provided by engineer. */ "Share address" = "แชร์ที่อยู่"; -/* No comment provided by engineer. */ +/* alert title */ "Share address with contacts?" = "แชร์ที่อยู่กับผู้ติดต่อ?"; /* No comment provided by engineer. */ @@ -2789,7 +2664,7 @@ /* simplex link type */ "SimpleX group link" = "ลิงค์กลุ่ม SimpleX"; -/* No comment provided by engineer. */ +/* chat feature */ "SimpleX links" = "ลิงก์ SimpleX"; /* No comment provided by engineer. */ @@ -2813,9 +2688,6 @@ /* No comment provided by engineer. */ "Skipped messages" = "ข้อความที่ข้ามไป"; -/* No comment provided by engineer. */ -"SMP servers" = "เซิร์ฟเวอร์ SMP"; - /* No comment provided by engineer. */ "Some non-fatal errors occurred during import - you may see Chat console for more details." = "ข้อผิดพลาดที่ไม่ร้ายแรงบางอย่างเกิดขึ้นระหว่างการนำเข้า - คุณอาจดูรายละเอียดเพิ่มเติมได้ที่คอนโซล Chat"; @@ -2834,9 +2706,6 @@ /* No comment provided by engineer. */ "Stop" = "หยุด"; -/* No comment provided by engineer. */ -"Stop chat to enable database actions" = "หยุดการแชทเพื่อเปิดใช้งานการดำเนินการกับฐานข้อมูล"; - /* No comment provided by engineer. */ "Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "หยุดแชทเพื่อส่งออก นำเข้า หรือลบฐานข้อมูลแชท คุณจะไม่สามารถรับและส่งข้อความได้ในขณะที่การแชทหยุดลง"; @@ -2852,10 +2721,10 @@ /* No comment provided by engineer. */ "Stop sending file?" = "หยุดส่งไฟล์ไหม?"; -/* No comment provided by engineer. */ +/* alert action */ "Stop sharing" = "หยุดแชร์"; -/* No comment provided by engineer. */ +/* alert title */ "Stop sharing address?" = "หยุดแชร์ที่อยู่ไหม?"; /* authentication reason */ @@ -2891,9 +2760,6 @@ /* No comment provided by engineer. */ "Tap to join incognito" = "แตะเพื่อเข้าร่วมโหมดไม่ระบุตัวตน"; -/* No comment provided by engineer. */ -"Tap to start a new chat" = "แตะเพื่อเริ่มแชทใหม่"; - /* No comment provided by engineer. */ "TCP connection timeout" = "หมดเวลาการเชื่อมต่อ TCP"; @@ -2915,7 +2781,7 @@ /* No comment provided by engineer. */ "Test servers" = "เซิร์ฟเวอร์ทดสอบ"; -/* No comment provided by engineer. */ +/* alert title */ "Tests failed!" = "การทดสอบล้มเหลว!"; /* No comment provided by engineer. */ @@ -2927,9 +2793,6 @@ /* No comment provided by engineer. */ "Thanks to the users – contribute via Weblate!" = "ขอบคุณผู้ใช้ – มีส่วนร่วมผ่าน Weblate!"; -/* No comment provided by engineer. */ -"The 1st platform without any user identifiers – private by design." = "แพลตฟอร์มแรกที่ไม่มีตัวระบุผู้ใช้ - ถูกออกแบบให้เป็นส่วนตัว"; - /* No comment provided by engineer. */ "The app can notify you when you receive messages or contact requests - please open settings to enable." = "แอปสามารถแจ้งให้คุณทราบเมื่อคุณได้รับข้อความหรือคำขอติดต่อ - โปรดเปิดการตั้งค่าเพื่อเปิดใช้งาน"; @@ -2948,6 +2811,9 @@ /* No comment provided by engineer. */ "The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "encryption กำลังทำงานและไม่จำเป็นต้องใช้ข้อตกลง encryption ใหม่ อาจทำให้การเชื่อมต่อผิดพลาดได้!"; +/* No comment provided by engineer. */ +"The future of messaging" = "การส่งข้อความส่วนตัวรุ่นต่อไป"; + /* No comment provided by engineer. */ "The hash of the previous message is different." = "แฮชของข้อความก่อนหน้านี้แตกต่างกัน"; @@ -2960,14 +2826,11 @@ /* No comment provided by engineer. */ "The message will be marked as moderated for all members." = "ข้อความจะถูกทำเครื่องหมายว่ากลั่นกรองสำหรับสมาชิกทุกคน"; -/* No comment provided by engineer. */ -"The next generation of private messaging" = "การส่งข้อความส่วนตัวรุ่นต่อไป"; - /* No comment provided by engineer. */ "The old database was not removed during the migration, it can be deleted." = "ฐานข้อมูลเก่าไม่ได้ถูกลบในระหว่างการย้ายข้อมูล แต่สามารถลบได้"; /* No comment provided by engineer. */ -"The profile is only shared with your contacts." = "โปรไฟล์นี้แชร์กับผู้ติดต่อของคุณเท่านั้น"; +"Your profile is stored on your device and only shared with your contacts." = "โปรไฟล์นี้แชร์กับผู้ติดต่อของคุณเท่านั้น"; /* No comment provided by engineer. */ "The second tick we missed! ✅" = "ขีดที่สองที่เราพลาด! ✅"; @@ -2978,9 +2841,6 @@ /* No comment provided by engineer. */ "The servers for new connections of your current chat profile **%@**." = "เซิร์ฟเวอร์สำหรับการเชื่อมต่อใหม่ของโปรไฟล์การแชทปัจจุบันของคุณ **%@**"; -/* No comment provided by engineer. */ -"Theme" = "ธีม"; - /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "การตั้งค่าเหล่านี้ใช้สำหรับโปรไฟล์ปัจจุบันของคุณ **%@**"; @@ -3011,15 +2871,15 @@ /* No comment provided by engineer. */ "To make a new connection" = "เพื่อสร้างการเชื่อมต่อใหม่"; -/* No comment provided by engineer. */ -"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "เพื่อปกป้องความเป็นส่วนตัว แทนที่จะใช้ ID ผู้ใช้เหมือนที่แพลตฟอร์มอื่นๆใช้ SimpleX มีตัวระบุสำหรับคิวข้อความ โดยแยกจากกันสำหรับผู้ติดต่อแต่ละราย"; - /* No comment provided by engineer. */ "To protect timezone, image/voice files use UTC." = "ไฟล์ภาพ/เสียงใช้ UTC เพื่อป้องกันเขตเวลา"; /* No comment provided by engineer. */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "เพื่อปกป้องข้อมูลของคุณ ให้เปิด SimpleX Lock\nคุณจะได้รับแจ้งให้ยืนยันตัวตนให้เสร็จสมบูรณ์ก่อนที่จะเปิดใช้งานคุณลักษณะนี้"; +/* No comment provided by engineer. */ +"To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "เพื่อปกป้องความเป็นส่วนตัว แทนที่จะใช้ ID ผู้ใช้เหมือนที่แพลตฟอร์มอื่นๆใช้ SimpleX มีตัวระบุสำหรับคิวข้อความ โดยแยกจากกันสำหรับผู้ติดต่อแต่ละราย"; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "ในการบันทึกข้อความเสียง โปรดให้สิทธิ์ในการใช้ไมโครโฟน"; @@ -3050,13 +2910,10 @@ /* No comment provided by engineer. */ "Unable to record voice message" = "ไม่สามารถบันทึกข้อความเสียง"; -/* item status description */ -"Unexpected error: %@" = "ข้อผิดพลาดที่ไม่คาดคิด: %@"; - /* No comment provided by engineer. */ "Unexpected migration state" = "สถานะการย้ายข้อมูลที่ไม่คาดคิด"; -/* No comment provided by engineer. */ +/* swipe action */ "Unfav." = "เลิกชอบ"; /* No comment provided by engineer. */ @@ -3095,36 +2952,27 @@ /* authentication reason */ "Unlock app" = "ปลดล็อคแอป"; -/* No comment provided by engineer. */ +/* notification label action */ "Unmute" = "เปิดเสียง"; -/* No comment provided by engineer. */ +/* swipe action */ "Unread" = "เปลี่ยนเป็นยังไม่ได้อ่าน"; /* No comment provided by engineer. */ "Update" = "อัปเดต"; -/* No comment provided by engineer. */ -"Update .onion hosts setting?" = "อัปเดตการตั้งค่าโฮสต์ .onion ไหม?"; - /* No comment provided by engineer. */ "Update database passphrase" = "อัปเดตรหัสผ่านของฐานข้อมูล"; /* No comment provided by engineer. */ "Update network settings?" = "อัปเดตการตั้งค่าเครือข่ายไหม?"; -/* No comment provided by engineer. */ -"Update transport isolation mode?" = "อัปเดตโหมดการแยกการขนส่งไหม?"; - /* rcv group event chat item */ "updated group profile" = "อัปเดตโปรไฟล์กลุ่มแล้ว"; /* No comment provided by engineer. */ "Updating settings will re-connect the client to all servers." = "การอัปเดตการตั้งค่าจะเชื่อมต่อไคลเอนต์กับเซิร์ฟเวอร์ทั้งหมดอีกครั้ง"; -/* No comment provided by engineer. */ -"Updating this setting will re-connect the client to all servers." = "การอัปเดตการตั้งค่านี้จะเชื่อมต่อไคลเอนต์กับเซิร์ฟเวอร์ทั้งหมดอีกครั้ง"; - /* No comment provided by engineer. */ "Upgrade and open chat" = "อัปเกรดและเปิดการแชท"; @@ -3149,12 +2997,6 @@ /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "ใช้เซิร์ฟเวอร์ SimpleX Chat ไหม?"; -/* No comment provided by engineer. */ -"User profile" = "โปรไฟล์ผู้ใช้"; - -/* No comment provided by engineer. */ -"Using .onion hosts requires compatible VPN provider." = "การใช้โฮสต์ .onion ต้องการผู้ให้บริการ VPN ที่เข้ากันได้"; - /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "กำลังใช้เซิร์ฟเวอร์ SimpleX Chat อยู่"; @@ -3210,7 +3052,7 @@ "Voice messages are prohibited in this chat." = "ห้ามส่งข้อความเสียงในแชทนี้"; /* No comment provided by engineer. */ -"Voice messages are prohibited in this group." = "ข้อความเสียงเป็นสิ่งต้องห้ามในกลุ่มนี้"; +"Voice messages are prohibited." = "ข้อความเสียงเป็นสิ่งต้องห้ามในกลุ่มนี้"; /* No comment provided by engineer. */ "Voice messages prohibited!" = "ห้ามข้อความเสียง!"; @@ -3254,9 +3096,6 @@ /* No comment provided by engineer. */ "When available" = "เมื่อพร้อมใช้งาน"; -/* No comment provided by engineer. */ -"When people request to connect, you can accept or reject it." = "เมื่อมีคนขอเชื่อมต่อ คุณสามารถยอมรับหรือปฏิเสธได้"; - /* No comment provided by engineer. */ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "เมื่อคุณแชร์โปรไฟล์ที่ไม่ระบุตัวตนกับใครสักคน โปรไฟล์นี้จะใช้สำหรับกลุ่มที่พวกเขาเชิญคุณ"; @@ -3269,15 +3108,9 @@ /* No comment provided by engineer. */ "Wrong passphrase!" = "รหัสผ่านผิด!"; -/* No comment provided by engineer. */ -"XFTP servers" = "เซิร์ฟเวอร์ XFTP"; - /* pref value */ "yes" = "ใช่"; -/* No comment provided by engineer. */ -"You" = "คุณ"; - /* No comment provided by engineer. */ "You accepted connection" = "คุณยอมรับการเชื่อมต่อ"; @@ -3318,7 +3151,7 @@ "You can hide or mute a user profile - swipe it to the right." = "คุณสามารถซ่อนหรือปิดเสียงโปรไฟล์ผู้ใช้ - ปัดไปทางขวา"; /* notification body */ -"You can now send messages to %@" = "ตอนนี้คุณสามารถส่งข้อความถึง %@"; +"You can now chat with %@" = "ตอนนี้คุณสามารถส่งข้อความถึง %@"; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "คุณสามารถตั้งค่าแสดงตัวอย่างการแจ้งเตือนบนหน้าจอล็อคผ่านการตั้งค่า"; @@ -3329,9 +3162,6 @@ /* No comment provided by engineer. */ "You can share this address with your contacts to let them connect with **%@**." = "คุณสามารถแบ่งปันที่อยู่นี้กับผู้ติดต่อของคุณเพื่อให้พวกเขาเชื่อมต่อกับ **%@**"; -/* No comment provided by engineer. */ -"You can share your address as a link or QR code - anybody can connect to you." = "คุณสามารถแชร์ที่อยู่ของคุณเป็นลิงก์หรือรหัสคิวอาร์ - ใคร ๆ ก็สามารถเชื่อมต่อกับคุณได้"; - /* No comment provided by engineer. */ "You can start chat via app Settings / Database or by restarting the app" = "คุณสามารถเริ่มแชทผ่านการตั้งค่าแอป / ฐานข้อมูล หรือโดยการรีสตาร์ทแอป"; @@ -3356,14 +3186,11 @@ /* snd group event chat item */ "you changed role of %@ to %@" = "คุณเปลี่ยนบทบาทของ %1$@ เป็น %2$@"; -/* No comment provided by engineer. */ -"You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them." = "คุณควบคุมผ่านเซิร์ฟเวอร์ **เพื่อรับ** ข้อความผู้ติดต่อของคุณ - เซิร์ฟเวอร์ที่คุณใช้เพื่อส่งข้อความถึงพวกเขา"; - /* No comment provided by engineer. */ "You could not be verified; please try again." = "เราไม่สามารถตรวจสอบคุณได้ กรุณาลองอีกครั้ง."; /* No comment provided by engineer. */ -"You have no chats" = "คุณไม่มีการแชท"; +"You decide who can connect." = "ผู้คนสามารถเชื่อมต่อกับคุณผ่านลิงก์ที่คุณแบ่งปันเท่านั้น"; /* No comment provided by engineer. */ "You have to enter passphrase every time the app starts - it is not stored on the device." = "คุณต้องใส่รหัสผ่านทุกครั้งที่เริ่มแอป - รหัสผ่านไม่ได้จัดเก็บไว้ในอุปกรณ์"; @@ -3428,9 +3255,6 @@ /* No comment provided by engineer. */ "You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed" = "คุณกำลังใช้โปรไฟล์ที่ไม่ระบุตัวตนสำหรับกลุ่มนี้ - ไม่อนุญาตให้เชิญผู้ติดต่อเพื่อป้องกันการแชร์โปรไฟล์หลักของคุณ"; -/* No comment provided by engineer. */ -"Your %@ servers" = "เซิร์ฟเวอร์ %@ ของคุณ"; - /* No comment provided by engineer. */ "Your calls" = "การโทรของคุณ"; @@ -3443,9 +3267,6 @@ /* No comment provided by engineer. */ "Your chat profiles" = "โปรไฟล์แชทของคุณ"; -/* No comment provided by engineer. */ -"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "ผู้ติดต่อของคุณจะต้องออนไลน์เพื่อให้การเชื่อมต่อเสร็จสมบูรณ์\nคุณสามารถยกเลิกการเชื่อมต่อนี้และลบผู้ติดต่อออก (และลองใหม่ในภายหลังด้วยลิงก์ใหม่)"; - /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "ผู้ติดต่อของคุณส่งไฟล์ที่ใหญ่กว่าขนาดสูงสุดที่รองรับในปัจจุบัน (%@)"; @@ -3471,7 +3292,7 @@ "Your privacy" = "ความเป็นส่วนตัวของคุณ"; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "โปรไฟล์ของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณและแชร์กับผู้ติดต่อของคุณเท่านั้น\nเซิร์ฟเวอร์ SimpleX ไม่สามารถดูโปรไฟล์ของคุณได้"; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "โปรไฟล์ของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณและแชร์กับผู้ติดต่อของคุณเท่านั้น เซิร์ฟเวอร์ SimpleX ไม่สามารถดูโปรไฟล์ของคุณได้"; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "โปรไฟล์ รายชื่อผู้ติดต่อ และข้อความที่ส่งของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณ"; @@ -3479,9 +3300,6 @@ /* No comment provided by engineer. */ "Your random profile" = "โปรไฟล์แบบสุ่มของคุณ"; -/* No comment provided by engineer. */ -"Your server" = "เซิร์ฟเวอร์ของคุณ"; - /* No comment provided by engineer. */ "Your server address" = "ที่อยู่เซิร์ฟเวอร์ของคุณ"; @@ -3491,9 +3309,3 @@ /* No comment provided by engineer. */ "Your SimpleX address" = "ที่อยู่ SimpleX ของคุณ"; -/* No comment provided by engineer. */ -"Your SMP servers" = "เซิร์ฟเวอร์ SMP ของคุณ"; - -/* No comment provided by engineer. */ -"Your XFTP servers" = "เซิร์ฟเวอร์ XFTP ของคุณ"; - diff --git a/apps/ios/tr.lproj/Localizable.strings b/apps/ios/tr.lproj/Localizable.strings index 8f7eea47c5..e3bb11d1cc 100644 --- a/apps/ios/tr.lproj/Localizable.strings +++ b/apps/ios/tr.lproj/Localizable.strings @@ -1,18 +1,3 @@ -/* No comment provided by engineer. */ -"\n" = "\n"; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" " = " "; - -/* No comment provided by engineer. */ -" (" = " ("; - /* No comment provided by engineer. */ " (can be copied)" = " (kopyalanabilir)"; @@ -31,30 +16,15 @@ /* No comment provided by engineer. */ "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- 5 dakikaya kadar süren sesli mesajlar.\n- mesaj kaybolması için özel zaman.\n- düzenleme geçmişi."; -/* No comment provided by engineer. */ -", " = ", "; - -/* No comment provided by engineer. */ -": " = ": "; - /* No comment provided by engineer. */ "!1 colored!" = "!1 renklendirilmiş!"; -/* No comment provided by engineer. */ -"." = "."; - -/* No comment provided by engineer. */ -"(" = "("; - /* No comment provided by engineer. */ "(new)" = "(yeni)"; /* No comment provided by engineer. */ "(this device v%@)" = "(bu cihaz v%@)"; -/* No comment provided by engineer. */ -")" = ")"; - /* No comment provided by engineer. */ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[Katkıda bulun](https://github.com/simplex-chat/simplex-chat#contribute)"; @@ -65,10 +35,7 @@ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Bize GitHub'da yıldız verin](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add contact**: to create a new invitation link, or connect via a link you received." = "**Kişi ekle**: yeni bir davet bağlantısı oluşturmak için, ya da aldığın bağlantıyla bağlan."; - -/* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Yeni kişi ekleyin**: tek seferlik QR Kodunuzu oluşturmak veya kişisel ulaşım bilgileri bağlantısı için."; +"**Create 1-time link**: to create and share a new invitation link." = "**Kişi ekle**: yeni bir davet bağlantısı oluşturmak için, ya da aldığın bağlantıyla bağlan."; /* No comment provided by engineer. */ "**Create group**: to create a new group." = "**Grup oluştur**: yeni bir grup oluşturmak için."; @@ -80,20 +47,29 @@ "**e2e encrypted** video call" = "**uçtan uca şifrelenmiş** görüntülü arama"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Daha gizli**: her 20 dakikada yeni mesajlar için kontrol et. Cihaz jetonu SimpleX Chat sunucusuyla paylaşılacak, ama ne kadar kişi veya mesaja sahip olduğun paylaşılmayacak."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Daha gizli**: her 20 dakikada yeni mesajlar için kontrol et. Cihaz jetonu SimpleX Chat sunucusuyla paylaşılacak, ama ne kadar kişi veya mesaja sahip olduğun paylaşılmayacak."; /* No comment provided by engineer. */ -"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**En gizli**: SimpleX Chat bildirim sunucusunu kullanma, arkaplanda mesajları periyodik olarak kontrol edin (uygulamayı ne sıklıkta kullandığınıza bağlıdır)."; +"**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**En gizli**: SimpleX Chat bildirim sunucusunu kullanma, arkaplanda mesajları periyodik olarak kontrol edin (uygulamayı ne sıklıkta kullandığınıza bağlıdır)."; + +/* No comment provided by engineer. */ +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Lütfen dikkat**: Aynı veritabanını iki cihazda kullanmak, güvenlik koruması olarak bağlantılarınızdaki mesajların şifresinin çözülmesini engelleyecektir."; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Lütfen aklınızda bulunsun**: eğer parolanızı kaybederseniz parolanızı değiştirme veya geri kurtarma ihtimaliniz YOKTUR."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Önerilen**: cihaz tokeni ve bildirimler SimpleX Chat bildirim sunucularına gönderilir, ama mesajın içeriği, boyutu veya kimden geldiği gönderilmez."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Önerilen**: cihaz tokeni ve bildirimler SimpleX Chat bildirim sunucularına gönderilir, ama mesajın içeriği, boyutu veya kimden geldiği gönderilmez."; + +/* No comment provided by engineer. */ +"**Scan / Paste link**: to connect via a link you received." = "edindiğiniz bağlantı aracılığıyla bağlanmak için **Linki tarayın/yapıştırın**."; /* No comment provided by engineer. */ "**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Dikkat**: Anında iletilen bildirimlere Anahtar Zinciri'nde kaydedilmiş parola gereklidir."; +/* No comment provided by engineer. */ +"**Warning**: the archive will be removed." = "**Uyarı**: arşiv silinecektir."; + /* No comment provided by engineer. */ "*bold*" = "\\*kalın*"; @@ -131,11 +107,14 @@ "%@ and %@ connected" = "%@ ve %@ bağlandı"; /* copied message info, at